-
Notifications
You must be signed in to change notification settings - Fork 0
/
202320.py
137 lines (112 loc) · 3.83 KB
/
202320.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
import math
from abc import abstractmethod
from collections import defaultdict, deque
from dataclasses import dataclass
@dataclass
class Signal:
src: str
dest: str
value: bool
class Box:
def __init__(self, label, outputs):
self.label = label
self.outputs = outputs
@abstractmethod
def handle_input(self, input):
pass
def set_inputs(self, inputs):
pass
class Broadcast(Box):
def handle_input(self, input):
# print(f"Broadcast {self.label} got {input.value} from {input.src}")
for output in self.outputs:
yield Signal(self.label, output, input.value)
class FlipFlop(Box):
def __init__(self, label, outputs):
super().__init__(label, outputs)
self.state = False
def handle_input(self, input):
# print(f"Flipflop {self.label} got {input.value} from {input.src}")
if not input.value: # low signal
s = False
if self.state: # it was already on
self.state = False
else: # it was off
self.state = True
s = True
for output in self.outputs:
yield Signal(self.label, output, s)
class Conj(Box):
def __init__(self, label, outputs):
super().__init__(label, outputs)
self.state = None
def handle_input(self, input):
# print(f"Conj {self.label} got {input.value} from {input.src}")
self.state[input.src] = input.value
s = not all(self.state.values())
for output in self.outputs:
yield Signal(self.label, output, s)
def set_inputs(self, inputs):
self.state = {input: False for input in inputs}
def get_network():
network = {}
input_map = defaultdict(list)
with open("202320.txt", "r") as f:
for line in f:
[label_raw, outputs_raw] = line.strip().split(" -> ")
outputs = outputs_raw.split(", ")
(type, label) = (
None if label_raw == "broadcaster" else label_raw[0],
"broadcaster" if label_raw == "broadcaster" else label_raw[1:],
)
match (type, label):
case (_, "broadcaster"):
network[label] = Broadcast(label, outputs)
case ("%", label):
network[label] = FlipFlop(label, outputs)
case ("&", label):
network[label] = Conj(label, outputs)
case _:
raise ValueError(f"Unknown label type: {label_raw}")
for output in outputs:
input_map[output].append(label)
for dest, srcs in input_map.items():
if dest in network:
network[dest].set_inputs(srcs)
return network
def push_button(n, *, target=None):
low_count, high_count, target_sent_high = 0, 0, False
q = deque([Signal(None, "broadcaster", False)])
while q:
signal = q.popleft()
if signal.value:
high_count += 1
else:
low_count += 1
if target is not None and signal.src == target and signal.value:
assert not target_sent_high
target_sent_high = True
if signal.dest in n:
q.extend(n[signal.dest].handle_input(signal))
return (low_count, high_count, target_sent_high)
p1low, p1high = 0, 0
network = get_network()
for _ in range(1000):
(low, high, _) = push_button(network)
p1low += low
p1high += high
print(f"Part one: {p1low*p1high}")
def first_high(target):
i = 1
network = get_network()
while True:
(_, _, high) = push_button(network, target=target)
if high:
break
i += 1
return i
part_two = []
for t in ["qz", "cq", "jx", "tt"]: # maybe input specific?
res = first_high(t)
part_two.append(res)
print(f"Part two: {math.lcm(*part_two)}")