diff --git a/2019/ruby/computer.rb b/2019/ruby/computer.rb index c691582..4c3ab85 100644 --- a/2019/ruby/computer.rb +++ b/2019/ruby/computer.rb @@ -16,16 +16,16 @@ class Mode < T::Enum end OPCODES = T.let({ - 1 => ->(m, _, _, a, b, c) { m[c] = m[a] + m[b] ; [nil, nil] }, # add - 2 => ->(m, _, _, a, b, c) { m[c] = m[a] * m[b] ; [nil, nil] }, # multiply - 3 => ->(m, i, _, a) { m[a] = i.gets.to_i ; [nil, nil] }, # input - 4 => ->(m, _, o, a) { o.puts(m[a]) ; [nil, nil] }, # output - 5 => ->(m, _, _, a, b) { [m[a].nonzero? ? m[b] : nil, nil] }, # jump-if-true - 6 => ->(m, _, _, a, b) { [m[a].zero? ? m[b] : nil, nil] }, # jump-if-false - 7 => ->(m, _, _, a, b, c) { m[c] = (m[a] < m[b]) ? 1 : 0; [nil, nil] }, # less than - 8 => ->(m, _, _, a, b, c) { m[c] = (m[a] == m[b]) ? 1 : 0; [nil, nil] }, # equals - 9 => ->(m, _, _, a) { [nil, m[a]] }, # adjust relative base - 99 => ->(*) { throw :halt }, # halt + 1 => ->(x, _, _, a, b, c) { x[c] = x[a] + x[b] }, # add + 2 => ->(x, _, _, a, b, c) { x[c] = x[a] * x[b] }, # multiply + 3 => ->(x, i, _, a) { x[a] = i.gets.to_i }, # input + 4 => ->(x, _, o, a) { o.puts(x[a]) }, # output + 5 => ->(x, _, _, a, b) { x.pc = x[b] if x[a].nonzero? }, # jump-if-true + 6 => ->(x, _, _, a, b) { x.pc = x[b] if x[a].zero? }, # jump-if-false + 7 => ->(x, _, _, a, b, c) { x[c] = (x[a] < x[b]) ? 1 : 0 }, # less than + 8 => ->(x, _, _, a, b, c) { x[c] = (x[a] == x[b]) ? 1 : 0 }, # equals + 9 => ->(x, _, _, a) { x.rb += x[a] }, # adjust relative base + 99 => ->(*) { throw :halt }, # halt }, T::Hash[Integer, T.untyped]) class Parameter @@ -64,16 +64,14 @@ class Computer new(input.split(?,).map(&:to_i)) end - sig {returns(AnyIO)} - attr_reader :input, :output + sig {returns(Integer)} + attr_accessor :pc, :rb - sig {params(program: Memory, input: AnyIO, output: AnyIO).void} - def initialize(program, input=STDIN, output=STDOUT) + sig {params(program: Memory).void} + def initialize(program) @memory = T.let(program.dup, Memory) - @input = T.let(input, AnyIO) - @output = T.let(output, AnyIO) @pc = T.let(0, Integer) - @relative_base = T.let(0, Integer) + @rb = T.let(0, Integer) end sig {params(input: AnyIO, output: AnyIO).returns(Memory)} @@ -93,14 +91,14 @@ class Computer catch(:halt) do loop do - instruction = @memory[@pc].to_s.rjust(5, ?0) + instruction = @memory[pc].to_s.rjust(5, ?0) opcode = OPCODES.fetch(instruction[-2..-1].to_i) - @pc += 1 + self.pc += 1 - n = opcode.arity - 3 # subtract the memory, input, and output params + n = opcode.arity - 3 # subtract the computer, input, and output params args = (0...n).map {|i| mode = instruction[2-i] { ?0 } - value = @memory.fetch(@pc + i) || 0 + value = @memory.fetch(pc + i) || 0 mode = case mode when ?0 then Mode::Position when ?1 then Mode::Immediate @@ -109,11 +107,9 @@ class Computer end Parameter.new(mode, value) } - @pc += n + self.pc += n - pc, rb = opcode.call(self, input, output, *args) - @pc = pc if pc - @relative_base += rb if rb + opcode.call(self, input, output, *args) yield @memory end @@ -131,7 +127,7 @@ class Computer case mode when Mode::Position then @memory[parameter.value] || 0 when Mode::Immediate then parameter.value - when Mode::Relative then @memory[@relative_base + parameter.value] || 0 + when Mode::Relative then @memory[rb + parameter.value] || 0 else T.absurd(mode) end end @@ -144,7 +140,7 @@ class Computer case mode when Mode::Position then @memory[parameter.value] = value when Mode::Immediate then raise "writes should never be in immediate mode" - when Mode::Relative then @memory[@relative_base + parameter.value] = value + when Mode::Relative then @memory[rb + parameter.value] = value else T.absurd(mode) end end diff --git a/2019/ruby/test_computer.rb b/2019/ruby/test_computer.rb index 40f0a95..27f48dc 100644 --- a/2019/ruby/test_computer.rb +++ b/2019/ruby/test_computer.rb @@ -25,7 +25,7 @@ class TestComputer < Minitest::Test end def test_input_output - assert_equal 1, run_program("3,50,99", input: "1\n")[50] + assert_equal 1, run_program("3,50,99", input: "1")[50] output = StringIO.new run_program("4,3,99,50", output: output) @@ -84,6 +84,21 @@ class TestComputer < Minitest::Test end end + def test_relative_base + program = "109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99" + output = StringIO.new + run_program(program, output: output) + assert_equal program.split(?,).join("\n"), output.string.strip + + output = StringIO.new + run_program("1102,34915192,34915192,7,4,7,99,0", output: output) + assert_equal 16, output.string.strip.size + + output = StringIO.new + run_program("104,1125899906842624,99", output: output) + assert_equal 1125899906842624, output.string.to_i + end + private def run_program(p, input: "", output: StringIO.new)