Part 1: Create a simple virtual machine with two registers (a
and b
, non-negative integers) and six instructions:
hlf (a|b)
- divide the given register by half, round downtpl (a|b)
- triple the given registerinc (a|b)
- add 1 to the given registerjmp [+-]\d+
- jump forward/backwards by the given number of instructionsjie (a|b), [+-]\d+
- if the given register is even, jumpjio (a|b), [+-]\d+
- if the given register equals one, jump
I do love writing a good virtual machine. Check out my ‘Tiny’ virtual machine in Racket.
def read_program():
return [
tuple(re.split('[, ]+', line.strip()))
for line in sys.stdin
]
def run(program, **initial_state):
pc = 0
registers = {'a': 0, 'b': 0}
registers.update(initial_state)
while True:
op = program[pc][0]
args = program[pc][1:]
if op == 'hlf':
registers[args[0]] //= 2
pc += 1
elif op == 'tpl':
registers[args[0]] *= 3
pc += 1
elif op == 'inc':
registers[args[0]] += 1
pc += 1
elif op == 'jmp':
pc += int(args[0])
elif op == 'jie':
if registers[args[0]] % 2 == 0:
pc += int(args[1])
else:
pc += 1
elif op == 'jio':
if registers[args[0]] == 1:
pc += int(args[1])
else:
pc += 1
if not (0 <= pc < len(program)):
break
return registers
if __name__ == '__main__':
program = read_program()
output = run(program)
print(output['b'])
It’s not the most abstracted thing ever, but it really doesn’t matter. It works great.
Part 2: Re-run with the initial state a=1, b=0
.
I’ll reuse the same trick I’ve used a few times before to load part 1:
part1 = imp.load_source('part1', 'part-1.py')
if __name__ == '__main__':
program = part1.read_program()
output = part1.run(program, a = 1)
print(output['b'])