#!/usr/bin/python3 """Solution for day 25 of Advent of Code 2016. This is a copy of Day 23's solution, with an 'out' instruction added. Manual testing showed the output repeats regularly, so we just run the program until there are 20 bits output. TODO: Add a common class for handling the assembly code, as it's now repeated in 3 places. """ import itertools def value(registers, x): try: return int(x) except ValueError: return registers[x] def cpy(registers, src, dst): registers[dst] = value(registers, src) def inc(registers, reg): registers[reg] += 1 def dec(registers, reg): registers[reg] -= 1 def jnz(registers, val, tar): registers['pc'] += 0 if value(registers, val) == 0 else value(registers, tar) - 1 def tgl(registers, ins): mappings = {inc: dec, dec: inc, tgl: inc, jnz: cpy, cpy: jnz} target = registers['pc'] + value(registers, ins) registers['in'][target] = (mappings[registers['in'][target][0]], registers['in'][target][1]) def out(registers, val): registers['out'].append(value(registers, val)) def try_multiply(registers): try: pc = registers['pc'] chunk = registers['in'][pc:pc+6] instr, args = map(list, zip(*chunk)) if instr == [cpy, inc, dec, jnz, dec, jnz] \ and args[0][1] == args[2][0] == args[3][0] \ and args[4][0] == args[5][0] \ and args[1][0] != args[0][1] != args[4][0] != args[0][0] \ and args[3][1] == '-2' and args[5][1] == '-5': registers[args[1][0]] += value(registers, args[0][0]) * value(registers, args[4][0]) registers[args[2][0]] = 0 registers[args[4][0]] = 0 registers['pc'] += 5 return True except: pass return False def step(registers): if not try_multiply(registers): try: registers['in'][registers['pc']][0](registers, *registers['in'][registers['pc']][1]) except: pass def run(values): registers = values registers['pc'] = 0 registers['out'] = [] registers['in'] = instr.copy() while registers['pc'] < len(instr) and len(registers['out']) < 20: step(registers) registers['pc'] += 1 return registers['out'] with open('data/25.txt', 'r') as file: instr = map(str.split, map(str.strip, file.readlines())) instr = [(globals()[i[0]], i[1:]) for i in instr] for a in itertools.count(): if run({'a': a, 'b': 0, 'c': 0, 'd': 0}) == [0, 1] * 10: print('Part one: %s' % a) break