-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathp24.py
162 lines (135 loc) · 5.18 KB
/
p24.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
from collections import namedtuple
Calc = namedtuple("Calc", ["wires", "op"])
def read_input():
circuit = {}
with open("p24.input.txt", "r") as f:
doing_wires = True
for line in f.readlines():
line = line.strip()
if not line:
doing_wires = False
continue
if doing_wires:
wire, val = line.split(": ")
val = int(val)
circuit[wire] = val
continue
wire1, op, wire2, _, outwire = line.split(" ")
circuit[outwire] = Calc([wire1, wire2], op)
return circuit
def find_value(circuit, key):
val = circuit[key]
if isinstance(val, int):
return val
lval = find_value(circuit, val.wires[0])
rval = find_value(circuit, val.wires[1])
if val.op == "AND":
return lval & rval
elif val.op == "XOR":
return lval ^ rval
else:
return lval | rval
def get_wire_nums(circuit, prefix: str) -> int:
total = 0
keys = list(sorted(filter(lambda key: key[0] == prefix, circuit.keys())))
for i, k in enumerate(keys):
total += find_value(circuit, k) * 2**i
return total
HALF_ADD_PRE = "HA_"
HALF_ADD_CARRY_PRE = "HAC_"
FULL_ADD_CARRY_PRE = "FAC_"
ADD_WITH_CARRY_PRE = "z"
CARRY_PRE = "C_"
def find_exact_inputs(circuit, wires) -> [(str, Calc)]:
results = []
for outwire, val in circuit.items():
if not isinstance(val, Calc):
continue
if set(wires) == set(val.wires):
results.append((outwire, val))
return results
def traverse_gates(circuit):
name_map = {}
swaps = []
def replace_wire_name(old_name: str, new_name: str):
new_circuit = {}
items = list(circuit.items())
for outwire, val in items:
if outwire == old_name:
new_circuit[new_name] = val
continue
if not isinstance(val, Calc):
new_circuit[outwire] = val
continue
wires = list(val.wires)
if old_name not in wires:
new_circuit[outwire] = val
continue
wires.remove(old_name)
wires.append(new_name)
new_circuit[outwire] = Calc(wires, val.op)
name_map[new_name] = old_name
assert len(new_circuit) == len(circuit)
return new_circuit
def swap_wires(wires: [str]) -> None:
for outwire, val in list(circuit.items()):
if not isinstance(val, Calc):
continue
if wires[0] == val.wires[0]:
circuit[outwire] = Calc([wires[1], val.wires[1]], val.op)
elif wires[0] == val.wires[1]:
circuit[outwire] = Calc([wires[1], val.wires[0]], val.op)
elif wires[1] == val.wires[0]:
circuit[outwire] = Calc([wires[0], val.wires[1]], val.op)
elif wires[1] == val.wires[1]:
circuit[outwire] = Calc([wires[0], val.wires[0]], val.op)
swaps.extend(wires)
def find_exact(wires, op) -> str:
results = find_exact_inputs(circuit, wires)
for outwire, calc in results:
if calc.op == op:
return outwire
swap_possibles = []
for _, val in circuit.items():
if not isinstance(val, Calc):
continue
if (wires[0] in val.wires or wires[1] in val.wires) and val.op == op:
swap_possibles.append(val)
assert len(swap_possibles) == 1
wires_to_swap = list(
filter(lambda x: x not in wires, swap_possibles[0].wires)
) + list(filter(lambda x: x not in swap_possibles[0].wires, wires))
swap_wires(wires_to_swap)
return find_exact(wires, op)
# start with x00, y00 and make the carry
# no carry to take into account for least sig
outwire = find_exact(["x00", "y00"], "AND")
circuit = replace_wire_name(outwire, f"{CARRY_PRE}00")
# then make half adds and half add carries for individual bits
# if exact match isn't found, find whichever one is there and then swap the other
# with the wire shared with it
ixbit = 1
while f"x{str(ixbit).zfill(2)}" in circuit:
prev_suffix = str(ixbit - 1).zfill(2)
suffix = str(ixbit).zfill(2)
outwire = find_exact([f"{c}{suffix}" for c in "xy"], "XOR")
circuit = replace_wire_name(outwire, f"{HALF_ADD_PRE}{suffix}")
outwire = find_exact([f"{c}{suffix}" for c in "xy"], "AND")
circuit = replace_wire_name(outwire, f"{HALF_ADD_CARRY_PRE}{suffix}")
outwire = find_exact(
[f"{CARRY_PRE}{prev_suffix}", f"{HALF_ADD_PRE}{suffix}"], "AND"
)
circuit = replace_wire_name(outwire, f"{FULL_ADD_CARRY_PRE}{suffix}")
outwire = find_exact(
[f"{HALF_ADD_CARRY_PRE}{suffix}", f"{FULL_ADD_CARRY_PRE}{suffix}"], "OR"
)
circuit = replace_wire_name(outwire, f"{CARRY_PRE}{suffix}")
ixbit += 1
return ",".join(
sorted([wire if len(wire) == 3 else name_map[wire] for wire in swaps])
)
if __name__ == "__main__":
circuit = read_input()
print(get_wire_nums(circuit, "z"))
swaps = traverse_gates(circuit)
print(swaps)