[2019][ruby] refactor computer

master
Alpha Chen 5 years ago
parent 43356b2e0c
commit dc76d7df9d

@ -16,16 +16,16 @@ class Mode < T::Enum
end end
OPCODES = T.let({ OPCODES = T.let({
1 => ->(m, _, _, a, b, c) { m[c] = m[a] + m[b] ; [nil, nil] }, # add 1 => ->(x, _, _, a, b, c) { x[c] = x[a] + x[b] }, # add
2 => ->(m, _, _, a, b, c) { m[c] = m[a] * m[b] ; [nil, nil] }, # multiply 2 => ->(x, _, _, a, b, c) { x[c] = x[a] * x[b] }, # multiply
3 => ->(m, i, _, a) { m[a] = i.gets.to_i ; [nil, nil] }, # input 3 => ->(x, i, _, a) { x[a] = i.gets.to_i }, # input
4 => ->(m, _, o, a) { o.puts(m[a]) ; [nil, nil] }, # output 4 => ->(x, _, o, a) { o.puts(x[a]) }, # output
5 => ->(m, _, _, a, b) { [m[a].nonzero? ? m[b] : nil, nil] }, # jump-if-true 5 => ->(x, _, _, a, b) { x.pc = x[b] if x[a].nonzero? }, # jump-if-true
6 => ->(m, _, _, a, b) { [m[a].zero? ? m[b] : nil, nil] }, # jump-if-false 6 => ->(x, _, _, a, b) { x.pc = x[b] if x[a].zero? }, # jump-if-false
7 => ->(m, _, _, a, b, c) { m[c] = (m[a] < m[b]) ? 1 : 0; [nil, nil] }, # less than 7 => ->(x, _, _, a, b, c) { x[c] = (x[a] < x[b]) ? 1 : 0 }, # less than
8 => ->(m, _, _, a, b, c) { m[c] = (m[a] == m[b]) ? 1 : 0; [nil, nil] }, # equals 8 => ->(x, _, _, a, b, c) { x[c] = (x[a] == x[b]) ? 1 : 0 }, # equals
9 => ->(m, _, _, a) { [nil, m[a]] }, # adjust relative base 9 => ->(x, _, _, a) { x.rb += x[a] }, # adjust relative base
99 => ->(*) { throw :halt }, # halt 99 => ->(*) { throw :halt }, # halt
}, T::Hash[Integer, T.untyped]) }, T::Hash[Integer, T.untyped])
class Parameter class Parameter
@ -64,16 +64,14 @@ class Computer
new(input.split(?,).map(&:to_i)) new(input.split(?,).map(&:to_i))
end end
sig {returns(AnyIO)} sig {returns(Integer)}
attr_reader :input, :output attr_accessor :pc, :rb
sig {params(program: Memory, input: AnyIO, output: AnyIO).void} sig {params(program: Memory).void}
def initialize(program, input=STDIN, output=STDOUT) def initialize(program)
@memory = T.let(program.dup, Memory) @memory = T.let(program.dup, Memory)
@input = T.let(input, AnyIO)
@output = T.let(output, AnyIO)
@pc = T.let(0, Integer) @pc = T.let(0, Integer)
@relative_base = T.let(0, Integer) @rb = T.let(0, Integer)
end end
sig {params(input: AnyIO, output: AnyIO).returns(Memory)} sig {params(input: AnyIO, output: AnyIO).returns(Memory)}
@ -93,14 +91,14 @@ class Computer
catch(:halt) do catch(:halt) do
loop 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) 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| args = (0...n).map {|i|
mode = instruction[2-i] { ?0 } mode = instruction[2-i] { ?0 }
value = @memory.fetch(@pc + i) || 0 value = @memory.fetch(pc + i) || 0
mode = case mode mode = case mode
when ?0 then Mode::Position when ?0 then Mode::Position
when ?1 then Mode::Immediate when ?1 then Mode::Immediate
@ -109,11 +107,9 @@ class Computer
end end
Parameter.new(mode, value) Parameter.new(mode, value)
} }
@pc += n self.pc += n
pc, rb = opcode.call(self, input, output, *args) opcode.call(self, input, output, *args)
@pc = pc if pc
@relative_base += rb if rb
yield @memory yield @memory
end end
@ -131,7 +127,7 @@ class Computer
case mode case mode
when Mode::Position then @memory[parameter.value] || 0 when Mode::Position then @memory[parameter.value] || 0
when Mode::Immediate then parameter.value 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) else T.absurd(mode)
end end
end end
@ -144,7 +140,7 @@ class Computer
case mode case mode
when Mode::Position then @memory[parameter.value] = value when Mode::Position then @memory[parameter.value] = value
when Mode::Immediate then raise "writes should never be in immediate mode" 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) else T.absurd(mode)
end end
end end

@ -25,7 +25,7 @@ class TestComputer < Minitest::Test
end end
def test_input_output 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 output = StringIO.new
run_program("4,3,99,50", output: output) run_program("4,3,99,50", output: output)
@ -84,6 +84,21 @@ class TestComputer < Minitest::Test
end end
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 private
def run_program(p, input: "", output: StringIO.new) def run_program(p, input: "", output: StringIO.new)

Loading…
Cancel
Save