diff --git a/2019/day11/intcode.rb b/2019/day11/intcode.rb index e29ac62..cee7224 100644 --- a/2019/day11/intcode.rb +++ b/2019/day11/intcode.rb @@ -137,6 +137,7 @@ module Intcode nil when 9 move_relative_base! + nil when 99 :done_executing else diff --git a/2019/day11/part2.rb b/2019/day11/part2.rb new file mode 100644 index 0000000..725feab --- /dev/null +++ b/2019/day11/part2.rb @@ -0,0 +1,172 @@ +# 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 + + def corners + min_x = +Float::INFINITY + min_y = +Float::INFINITY + max_x = -Float::INFINITY + max_y = -Float::INFINITY + + @panels.keys.each do |p| + min_x = [min_x, p.x].min + min_y = [min_y, p.y].min + max_x = [max_x, p.x].max + max_y = [max_y, p.y].max + end + + upper_left = Point.new(min_x, min_y) + lower_right = Point.new(max_x, max_y) + [upper_left, lower_right] + end + + def to_s + tl, br = corners + + s = String.new + (tl.y..br.y).each do |y| + (tl.x..br.x).each do |x| + s << char(color_at(Point.new(x, y))) + end + s << "\n" + end + s + end + + def char(code) + case code + when :black + ' ' + when :white + # Unicode full block + "\u2588" + else + raise ArgumentError, "unexpected color code: #{code}" + end + 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 + +hull = Hull.new +hull.paint(Point.new(0, 0), :white) + +program = Intcode.read(File.join(__dir__, 'input')) +robot = Robot.new(program, hull) + +loop do + robot.step +rescue DoneExecuting + break +end + +puts hull