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