-
Notifications
You must be signed in to change notification settings - Fork 0
/
20_PulsePropagation.py
114 lines (93 loc) · 2.82 KB
/
20_PulsePropagation.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
import collections, math
from lib import *
inp = get_input()
M = {} # Modules (in -> out map)
FS = {} # Flip-Flop state
CS = {} # Conjunction state
for l in inp:
n, d = l.split(" -> ")
d = d.split(", ")
if n == "broadcaster":
M[n] = ("", d)
else:
M[n[1:]] = (n[0], d)
OUT_IN_MAP = collections.defaultdict(list) # Modules (out -> in map)
for k,v in M.items():
for vv in v[1]:
OUT_IN_MAP[vv].append(k)
# # - output mermaid diagram code
# out = ["flowchart TD"]
# for n,(t,d) in M.items():
# for dd in d:
# out.append(f" {n}[{t}{n}] --> {dd}")
# print("\n".join(out))
# initiate state
for n, (t, d) in M.items():
if t == "%":
FS[n] = False
if t == "&":
CS[n] = {k: False for k in OUT_IN_MAP[n]}
# Find cycle roots and put in CYCLES MAP
def find_roots(mod):
roots = []
has_found = False
for d in OUT_IN_MAP[mod]:
if d not in M or M[d][0] != "&":
continue
roots.extend(find_roots(d))
has_found = True
if not has_found:
roots.append(mod)
return roots
CYCLES = {d: [] for d in find_roots("rx")}
lp = 0
hp = 0
i=0 # cnt button presses
while True:
Q = collections.deque([("broadcaster", False, "button")])
i+=1
while Q:
m_name, p, last = Q.popleft()
# cnt for p1
if p: hp+=1
else: lp+=1
if m_name not in M:
continue
t,d = M[m_name]
out_pulse = None
if m_name == "broadcaster":
out_pulse = p
elif t == "%": # Flip flop
if not p:
FS[m_name] = not FS[m_name]
out_pulse = FS[m_name]
elif t == "&": # Conjunction module
CS[m_name][last] = p
out_pulse = not all(CS[m_name].values())
# Store cycles when the output goes low
if out_pulse is False and m_name in CYCLES:
CYCLES[m_name].append(i)
if out_pulse is not None:
Q.extend((sm, out_pulse, m_name) for sm in d)
# p1
if i == 1000:
print(lp*hp)
if not "rx" in OUT_IN_MAP: # skip p2, because there is no module named rx in the outputs of any module
print("-")
exit(0)
# we now have all data collected to solve p2
if i >= 1000 and all(len(a) > 0 for a in CYCLES.values()):
break
# walk down starting from rx and calculate the lcm of all inputs to get the number where rx gets a low input
def walk_down(mod, p):
"""mod -> start_module, p -> what should be the input for that module."""
global CYCLES
if mod in CYCLES and p:
return CYCLES[mod][0]
c = []
for d in OUT_IN_MAP[mod]:
if d not in M or M[d][0] != "&":
continue
c.append(walk_down(d, not p))
return math.lcm(*c)
print(walk_down("rx", False))