diff --git a/2019/ruby/computer.rb b/2019/ruby/computer.rb index 6c5b953..c691582 100644 --- a/2019/ruby/computer.rb +++ b/2019/ruby/computer.rb @@ -11,20 +11,22 @@ class Mode < T::Enum enums do Position = new Immediate = new + Relative = new end end OPCODES = T.let({ - 1 => ->(m, _, _, a, b, c) { m[c] = m[a] + m[b] ; nil }, # add - 2 => ->(m, _, _, a, b, c) { m[c] = m[a] * m[b] ; nil }, # multiply - 3 => ->(m, i, _, a) { m[a] = i.gets.to_i ; nil }, # input - 4 => ->(m, _, o, a) { o.puts(m[a]) ; nil }, # output - 5 => ->(m, _, _, a, b) { m[a].nonzero? ? m[b] : nil }, # jump-if-true - 6 => ->(m, _, _, a, b) { m[a].zero? ? m[b] : nil }, # jump-if-false - 7 => ->(m, _, _, a, b, c) { m[c] = (m[a] < m[b]) ? 1 : 0; nil }, # less than - 8 => ->(m, _, _, a, b, c) { m[c] = (m[a] == m[b]) ? 1 : 0; nil }, # equals - 99 => ->(*) { throw :halt }, # halt -}, T::Hash[T.untyped, T.untyped]) + 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 +}, T::Hash[Integer, T.untyped]) class Parameter extend T::Sig @@ -71,6 +73,7 @@ class Computer @input = T.let(input, AnyIO) @output = T.let(output, AnyIO) @pc = T.let(0, Integer) + @relative_base = T.let(0, Integer) end sig {params(input: AnyIO, output: AnyIO).returns(Memory)} @@ -101,13 +104,16 @@ class Computer mode = case mode when ?0 then Mode::Position when ?1 then Mode::Immediate + when ?2 then Mode::Relative else fail "unexpected mode: #{mode}" end Parameter.new(mode, value) } @pc += n - @pc = opcode.call(self, input, output, *args) || @pc + pc, rb = opcode.call(self, input, output, *args) + @pc = pc if pc + @relative_base += rb if rb yield @memory end @@ -123,8 +129,9 @@ class Computer parameter = Parameter.from(parameter) mode = parameter.mode case mode - when Mode::Position then @memory.fetch(parameter.value) || 0 + when Mode::Position then @memory[parameter.value] || 0 when Mode::Immediate then parameter.value + when Mode::Relative then @memory[@relative_base + parameter.value] || 0 else T.absurd(mode) end end @@ -132,8 +139,13 @@ class Computer sig {params(parameter: Parameter, value: Integer).void} def []=(parameter, value) - raise "writes should never be in immediate mode" if parameter.mode == Mode::Immediate - @memory[parameter.value] = value + mode = parameter.mode + 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 + else T.absurd(mode) + end end end diff --git a/2019/ruby/day_09.rb b/2019/ruby/day_09.rb new file mode 100644 index 0000000..64508ea --- /dev/null +++ b/2019/ruby/day_09.rb @@ -0,0 +1,6 @@ +require_relative "computer" + +program = ARGF.read +c = Computer.from(program) + +c.run(StringIO.new("2"))