# Advent of Code: Day 23

Source

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 down
• tpl (a|b) - triple the given register
• inc (a|b) - add 1 to the given register
• jmp [+-]\d+ - jump forward/backwards by the given number of instructions
• jie (a|b), [+-]\d+ - if the given register is even, jump
• jio (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]
args = program[pc][1:]

if op == 'hlf':
registers[args] //= 2
pc += 1
elif op == 'tpl':
registers[args] *= 3
pc += 1
elif op == 'inc':
registers[args] += 1
pc += 1
elif op == 'jmp':
pc += int(args)
elif op == 'jie':
if registers[args] % 2 == 0:
pc += int(args)
else:
pc += 1
elif op == 'jio':
if registers[args] == 1:
pc += int(args)
else:
pc += 1

if not (0 <= pc < len(program)):
break

return registers

if __name__ == '__main__':
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__':
print(output['b'])