view day10b.rb @ 19:1e16a25a9553

Strip down the text to just the puzzle, not the fluff
author IBBoard <dev@ibboard.co.uk>
date Mon, 11 Dec 2023 20:38:55 +0000
parents 92144824cbb7
children
line wrap: on
line source

#! /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