|
|
@ -16,15 +16,15 @@ 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])
|
|
|
|
|
|
|
|
|
|
|
@ -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
|
|
|
|