diff --git a/2019/day5/intcode.rb b/2019/day5/intcode.rb index 9ee5aa8..881d772 100644 --- a/2019/day5/intcode.rb +++ b/2019/day5/intcode.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'forwardable' + module Intcode def self.read(filename) parse File.read(filename) @@ -14,7 +16,24 @@ module Intcode Program.new parsed end + class Program + def initialize(mem) + @input = nil + @initial_state = mem + end + + def run(&user_input) + mem = Memory.new(@initial_state.clone) + Executor.new(mem).run(&user_input) + end + end + class Memory + extend Forwardable + + def_delegator :@cells, :[] + def_delegator :@cells, :[]= + def initialize(cells) @cells = cells end @@ -28,107 +47,103 @@ module Intcode end output end - - def [](addr) - @cells[addr] - end - - def []=(addr, val) - @cells[addr] = val - end end - class Program - attr_accessor :mem + class DoneExecuting < StandardError; end + + class Executor def initialize(mem) - @input = nil - @initial_state = mem + @mem = mem + @ip = 0 end def run(&user_input) - @mem = Memory.new(@initial_state.clone) - ip = 0 loop do - opcode = operation(mem[ip]) - mode = modes(mem[ip]) - case opcode - when 1 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - r3 = mem[ip + 3] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + tick(&user_input) + rescue DoneExecuting + # TODO: Stop using exceptions for control flow + return + end + end - mem[r3] = a + b - ip += 4 - when 2 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - r3 = mem[ip + 3] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + def tick(&user_input) + opcode = operation(@mem[@ip]) + mode = modes(@mem[@ip]) + case opcode + when 1 + r1, r2, r3 = @mem[@ip + 1, 3] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 - mem[r3] = a * b - ip += 4 - when 3 - r1 = mem[ip + 1] - raise StandardError, "unexpected mode: #{mode[0]}" if mode[0] != 0 + @mem[r3] = a + b + @ip += 4 + when 2 + r1 = @mem[@ip + 1] + r2 = @mem[@ip + 2] + r3 = @mem[@ip + 3] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 - mem[r1] = user_input.nil? ? input : user_input.call - ip += 2 - when 4 - r1 = mem[ip + 1] - output = param(r1, mode[0]) - puts "output: #{output}" + @mem[r3] = a * b + @ip += 4 + when 3 + r1 = @mem[@ip + 1] + raise StandardError, "unexpected mode: #{mode[0]}" if mode[0] != 0 - ip += 2 - when 5 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - if !a.zero? - ip = b - else - ip += 3 - end - when 6 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - if a.zero? - ip = b - else - ip += 3 - end - when 7 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - r3 = mem[ip + 3] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + @mem[r1] = user_input.nil? ? input : user_input.call + @ip += 2 + when 4 + r1 = @mem[@ip + 1] + output = param(r1, mode[0]) + puts "output: #{output}" - mem[r3] = a < b ? 1 : 0 - ip += 4 - when 8 - r1 = mem[ip + 1] - r2 = mem[ip + 2] - r3 = mem[ip + 3] - a = param(r1, mode[0]) - b = param(r2, mode[1]) - raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 - - mem[r3] = a == b ? 1 : 0 - ip += 4 - when 99 - return mem[0] + @ip += 2 + when 5 + r1 = @mem[@ip + 1] + r2 = @mem[@ip + 2] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + if !a.zero? + @ip = b else - raise StandardError, "unexpected opcode: #{opcode}" + @ip += 3 end + when 6 + r1 = @mem[@ip + 1] + r2 = @mem[@ip + 2] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + if a.zero? + @ip = b + else + @ip += 3 + end + when 7 + r1 = @mem[@ip + 1] + r2 = @mem[@ip + 2] + r3 = @mem[@ip + 3] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r3] = a < b ? 1 : 0 + @ip += 4 + when 8 + r1 = @mem[@ip + 1] + r2 = @mem[@ip + 2] + r3 = @mem[@ip + 3] + a = param(r1, mode[0]) + b = param(r2, mode[1]) + raise StandardError, "unexpected mode: #{mode[2]}" if mode[2] != 0 + + @mem[r3] = a == b ? 1 : 0 + @ip += 4 + when 99 + raise DoneExecuting + else + raise StandardError, "unexpected opcode: #{opcode}" end end @@ -162,12 +177,16 @@ module Intcode def param(val, mode) case mode when 0 - mem[val] + @mem[val] when 1 val else raise StandardError, "unexpected mode: #{mode}" end end + + # Operators + + def add; end end end