Advent of Code 2024 Day 24
The interesting function is repair, which compares the expression for e.g. z11 with the objective adder expression for z11. If we can't find a node with the objective expression, we recurse into the sub-expressions and try repair those.
import sys
from bidict import bidict
gates = {}
nodes = set()
for line in sys.stdin.read().split('\n\n')[1].splitlines():
a, op, b, _, c = line.split()
gates[c] = (op, a, b)
nodes.update((a, b, c))
nodes = sorted(nodes)
x = [p for p in nodes if p[0] == 'x']
y = [p for p in nodes if p[0] == 'y']
z = [p for p in nodes if p[0] == 'z']
def generate_exprs(gates):
exprs = bidict()
def go(p):
if p in exprs:
return
if p in x or p in y:
exprs[p] = (p,)
else:
op, a, b = gates[p]
go(a)
go(b)
exprs[p] = (op, *sorted((exprs[a], exprs[b])))
for p in nodes:
go(p)
return exprs
def objective(n):
if n == 0:
return ('XOR', ('x00',), ('y00',))
else:
return ('XOR', carry(n - 1), ('XOR', (x[n],), (y[n],)))
def carry(n):
if n == 0:
return ('AND', ('x00',), ('y00',))
else:
return ('OR', ('AND', carry(n - 1), ('XOR', (x[n],), (y[n],))), ('AND', (x[n],), (y[n],)))
def repair(gates, exprs, p, obj):
def go(p, obj):
if exprs[p] == obj:
return
if (res := exprs.inv.get(obj)):
return (p, res)
else:
op, a, b = gates[p]
obj_op, obj_a, obj_b = obj
if op != obj_op:
raise Exception
if go(a, obj_a) is None:
return go(b, obj_b)
if go(b, obj_b) is None:
return go(a, obj_a)
if go(a, obj_b) is None:
return go(b, obj_a)
if go(b, obj_a) is None:
return go(a, obj_b)
raise Exception
return go(p, obj)
swaps = []
for i in range(45):
exprs = generate_exprs(gates)
match repair(gates, exprs, z[i], objective(i)):
case (p, q):
swaps.extend((p, q))
gates[p], gates[q] = gates[q], gates[p]
print(','.join(sorted(swaps)))