You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
3.2 KiB
119 lines
3.2 KiB
6 years ago
|
require "minitest"
|
||
|
require "minitest/pride"
|
||
|
|
||
|
OPCODES = {
|
||
|
addr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] + registers[b] } },
|
||
|
addi: ->(a, b, c) { ->(registers) { registers[c] = registers[a] + b } },
|
||
|
mulr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] * registers[b] } },
|
||
|
muli: ->(a, b, c) { ->(registers) { registers[c] = registers[a] * b } },
|
||
|
banr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] & registers[b] } },
|
||
|
bani: ->(a, b, c) { ->(registers) { registers[c] = registers[a] & b } },
|
||
|
borr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] | registers[b] } },
|
||
|
bori: ->(a, b, c) { ->(registers) { registers[c] = registers[a] | b } },
|
||
|
setr: ->(a, b, c) { ->(registers) { registers[c] = registers[a] } },
|
||
|
seti: ->(a, b, c) { ->(registers) { registers[c] = a } },
|
||
|
gtir: ->(a, b, c) { ->(registers) { registers[c] = (a > registers[b]) ? 1 : 0 } },
|
||
|
gtri: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] > b) ? 1 : 0 } },
|
||
|
gtrr: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] > registers[b]) ? 1 : 0 } },
|
||
|
eqir: ->(a, b, c) { ->(registers) { registers[c] = (a == registers[b]) ? 1 : 0 } },
|
||
|
eqri: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] == b) ? 1 : 0 } },
|
||
|
eqrr: ->(a, b, c) { ->(registers) { registers[c] = (registers[a] == registers[b]) ? 1 : 0 } },
|
||
|
}
|
||
|
|
||
|
class TestOpcodes < Minitest::Test
|
||
|
def test_mulr
|
||
|
registers = [3, 2, 1, 1]
|
||
|
|
||
|
OPCODES[:mulr].call(2, 1, 2).call(registers)
|
||
|
|
||
|
assert_equal [3, 2, 2, 1], registers
|
||
|
end
|
||
|
|
||
|
def test_addi
|
||
|
registers = [3, 2, 1, 1]
|
||
|
|
||
|
OPCODES[:addi].call(2, 1, 2).call(registers)
|
||
|
|
||
|
assert_equal [3, 2, 2, 1], registers
|
||
|
end
|
||
|
|
||
|
def test_seti
|
||
|
registers = [3, 2, 1, 1]
|
||
|
|
||
|
OPCODES[:seti].call(2, 1, 2).call(registers)
|
||
|
|
||
|
assert_equal [3, 2, 2, 1], registers
|
||
|
end
|
||
|
end
|
||
|
|
||
|
Instruction = Struct.new(:opcode, :args)
|
||
|
|
||
|
class Program
|
||
|
def self.parse(input)
|
||
|
lines = input.lines
|
||
|
ip = lines.shift[/^#ip (\d)+$/, 1].to_i
|
||
|
instructions = lines.map {|line|
|
||
|
opcode, *args = line.split
|
||
|
opcode = opcode.to_sym
|
||
|
args = args.map(&:to_i)
|
||
|
Instruction.new(opcode, args)
|
||
|
}
|
||
|
new(ip, instructions)
|
||
|
end
|
||
|
|
||
|
attr_reader :registers
|
||
|
|
||
|
def initialize(ip, instructions)
|
||
|
@ip, @instructions = ip, instructions
|
||
|
@registers = Array.new(6, 0)
|
||
|
end
|
||
|
|
||
|
def run
|
||
|
return enum_for(__method__) unless block_given?
|
||
|
|
||
|
while (0...@instructions.length).cover?(@registers[@ip])
|
||
|
instruction = @instructions.fetch(@registers[@ip])
|
||
|
OPCODES.fetch(instruction.opcode)[*instruction.args][@registers]
|
||
|
yield self
|
||
|
@registers[@ip] += 1
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class TestProgram < Minitest::Test
|
||
|
def test_program
|
||
|
program = Program.parse(<<~PROGRAM)
|
||
|
#ip 0
|
||
|
seti 5 0 1
|
||
|
seti 6 0 2
|
||
|
addi 0 1 0
|
||
|
addr 1 2 3
|
||
|
setr 1 0 0
|
||
|
seti 8 0 4
|
||
|
seti 9 0 5
|
||
|
PROGRAM
|
||
|
|
||
|
run = program.run
|
||
|
loop do
|
||
|
run.next
|
||
|
end
|
||
|
|
||
|
assert_equal [7, 5, 6, 0, 0, 9], program.registers
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def solve(input)
|
||
|
program = Program.parse(input)
|
||
|
run = program.run
|
||
|
loop do
|
||
|
run.next
|
||
|
end
|
||
|
program.registers[0]
|
||
|
end
|
||
|
|
||
|
if __FILE__ == $0
|
||
|
require "minitest/autorun" and exit if ENV["TEST"]
|
||
|
|
||
|
puts solve(ARGF.read)
|
||
|
end
|