Watch
1
0
Fork
You've already forked advent-of-code
0
advent-of-code/2019/day11/part1.rb
2021-12-04 21:20:57 -08:00

127 lines
2.3 KiB
Ruby

# frozen_string_literal: true
require 'set'
require_relative 'intcode'
Point = Struct.new(:x, :y) do
def to_s
"(#{x}, #{y})"
end
end
class Hull
def initialize
@panels = Hash.new { |hash, key| hash[key] = :black }
@painted = Set.new
end
def color_at(point)
@panels[point]
end
def paint(point, color)
@painted << point
@panels[point] = color
end
def painted
@painted.count
end
end
class DoneExecuting < StandardError; end
class Robot
def initialize(program, hull)
@computer = Intcode::Computer.new(program.as_memory) { on_input }
@hull = hull
@facing = :up
@position = Point.new(0, 0)
end
def step
color_output = nil
direction_output = nil
color_output = tick until color_output
direction_output = tick until direction_output
@hull.paint(@position, color(color_output))
@facing = turn(@facing, direction_output)
@position = move(@position, @facing)
end
def tick
ret = @computer.tick
raise DoneExecuting if ret == :done_executing
ret
end
def on_input
color = @hull.color_at(@position)
case color
when :black
0
when :white
1
else
raise ArgumentError, "unexpected color: #{color}"
end
end
def color(code)
case code
when 0
:black
when 1
:white
else
raise ArgumentError, "unexpected color code: #{code}"
end
end
DIRECTIONS = %i[up right down left].freeze
def turn(facing, direction_code)
idx = DIRECTIONS.index(facing)
case direction_code
when 0
# left 90 degrees (ccw)
next_idx = (idx - 1) % DIRECTIONS.length
when 1
# right 90 degrees (cw)
next_idx = (idx + 1) % DIRECTIONS.length
else
raise ArgumentError, "unexpected direction code: #{direction_code}"
end
DIRECTIONS.fetch(next_idx)
end
def move(pos, direction)
case direction
when :up
Point.new(pos.x, pos.y - 1)
when :down
Point.new(pos.x, pos.y + 1)
when :left
Point.new(pos.x - 1, pos.y)
when :right
Point.new(pos.x + 1, pos.y)
else
raise ArgumentError, "unexpected direction: #{direction}"
end
end
end
program = Intcode.read(File.join(__dir__, 'input'))
hull = Hull.new
robot = Robot.new(program, hull)
loop do
robot.step
rescue DoneExecuting
break
end
puts hull.painted