Mercurial > repos > other > adventofcode2023
diff day10b.rb @ 17:92144824cbb7
Add "inside the loop" counting for day 10 part 2
Use the "how many lines do we cross?" approach. We also need
extra state for "am I actually inside a line, or did it just
revert back on itself".
Slightly slow, but not terrible.
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sun, 10 Dec 2023 20:26:12 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/day10b.rb Sun Dec 10 20:26:12 2023 +0000 @@ -0,0 +1,119 @@ +#! /usr/bin/env ruby + +if ARGV.length != 1 + abort("Incorrect arguments - needs input file") +elsif not File.exist? (ARGV[0]) + abort("File #{ARGV[0]} did not exist") +end + +file = ARGV[0] + +charmap = File.open(file, "r").each_line(chomp: true).map {|line| line.each_char.to_a}.to_a +$map_height = charmap.length +$map_width = charmap[0].length + +# Ugly way to find the start, because we know there's one (for now) +start = charmap.each_with_index.flat_map { |columns, i| j = columns.find_index("S"); if j then [j, i] else nil end }.compact +start_x, start_y = start + +$direction_inversion = {north: :south, south: :north, east: :west, west: :east} + +$pipemap = charmap.map do |row| + row.map do |cell| + case cell + when "|" + [:north, :south] + when "-" + [:east, :west] + when "L" + [:north, :east] + when "J" + [:north, :west] + when "7" + [:south, :west] + when "F" + [:south, :east] + when "S" + # Assume all directions are valid + [:north, :south, :east, :west] + else + [] + end + end +end + +def valid_position? (x, y) + x >= 0 and x < $map_width and y >= 0 and y < $map_height +end + +def get_target_for_step(step) + case step.direction + when :north then [step.x, step.y-1] + when :south then [step.x, step.y+1] + when :east then [step.x+1, step.y] + when :west then [step.x-1, step.y] + end +end + +def get_next_step(step) + new_x, new_y = get_target_for_step(step) + target_inputs = valid_position?(new_x, new_y) ? $pipemap[new_y][new_x] : [] + required_direction = $direction_inversion[step.direction] + target_inputs.include?(required_direction) ? Step.new(new_x, new_y, (target_inputs - [required_direction])[0]) : nil +end + +Step = Struct.new(:x, :y, :direction) + +first_steps = [:north, :south, :east, :west].map {|dir| Step.new(start_x, start_y, dir)} +routes = first_steps.map{|v|[v]} + +while ! routes.any? {|route| last_step = route[-1]; route.length > 1 and last_step.x == start_x and last_step.y == start_y} + routes = routes.map do |route| + next_step = get_next_step(route[-1]) + if next_step + route << next_step + else + nil + end + end.compact +end + +# We get two routes - the two opposite directions around the same path +# so take the first one +route = routes[0].map {|v| [v.x, v.y]} + +in_out_transition = [:north, :south] + +State = Struct.new(:crossed_lines, :inside_count, :last_switch) + +# TODO: Replace Source with actual piece based on route +actual_start_directions = first_steps.map do |v| + step = get_next_step(v) + !step.nil? and [route[1], route[-2]].include?([step.x, step.y]) ? v.direction : nil +end.filter {|v| v} +$pipemap[start_y][start_x] = actual_start_directions + +cells_inside = $pipemap.each_with_index.map do |row, y| + row_state = State.new(0, 0, nil) + row.each_with_index do |cell, x| + if route.include?([x,y]) + if cell == in_out_transition + row_state.crossed_lines += 1 + elsif cell != [:east, :west] + this_switch = (cell & in_out_transition) + if row_state.last_switch.nil? + row_state.last_switch = this_switch + else + if this_switch != row_state.last_switch + row_state.crossed_lines += 1 + end + row_state.last_switch = nil + end + end + elsif row_state.crossed_lines % 2.0 != 0 + row_state.inside_count += 1 + end + end + row_state +end.reduce(0) {|accum, row_state| accum + row_state.inside_count} +puts cells_inside \ No newline at end of file