Skip to content

Commit 69a3926

Browse files
committed
Day20 done
1 parent b5dde33 commit 69a3926

File tree

3 files changed

+341
-44
lines changed

3 files changed

+341
-44
lines changed

all_tests.py

+19-19
Original file line numberDiff line numberDiff line change
@@ -700,29 +700,29 @@ def test_part1(self):
700700
def test_part2(self):
701701
self.assertEqual(day19.solvePart2(self.lines.splitlines()), 167409079868000)
702702

703-
704703
class Day20(unittest.TestCase):
705-
lines = """
706-
2413432311323
707-
3215453535623
708-
3255245654254
709-
3446585845452
710-
4546657867536
711-
1438598798454
712-
4457876987766
713-
3637877979653
714-
4654967986887
715-
4564679986453
716-
1224686865563
717-
2546548887735
718-
4322674655533
704+
lines1 = """
705+
broadcaster -> a, b, c
706+
%a -> b
707+
%b -> c
708+
%c -> inv
709+
&inv -> a
719710
"""
720-
def test_part1(self):
721-
self.assertEqual(day20.solvePart1(self.lines.splitlines()), 102)
711+
lines2 = """
712+
broadcaster -> a
713+
%a -> inv, con
714+
&inv -> b
715+
%b -> con
716+
&con -> rx
717+
"""
718+
def test_part1_1(self):
719+
self.assertEqual(day20.solvePart1(self.lines1.splitlines()), 32000000)
722720

723-
def test_part2(self):
724-
self.assertEqual(day20.solvePart2(self.lines.splitlines()), 94)
721+
def test_part1_2(self):
722+
self.assertEqual(day20.solvePart1(self.lines2.splitlines()), 11687500)
725723

724+
def test_part2(self):
725+
self.assertEqual(day20.solvePart2(self.lines2.splitlines()), 1)
726726

727727
class Day21(unittest.TestCase):
728728
lines = """

day20.py

+301-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,315 @@
11
import aoc
2+
import math
23

34
"""
5+
--- Day 20: Pulse Propagation ---
6+
With your help, the Elves manage to find the right parts and fix all of the machines.
7+
Now, they just need to send the command to boot up the machines and get the sand flowing again.
8+
9+
The machines are far apart and wired together with long cables.
10+
The cables don't connect to the machines directly, but rather to communication modules attached to the machines that perform various initialization tasks and also act as communication relays.
11+
12+
Modules communicate using pulses.
13+
Each pulse is either a high pulse or a low pulse.
14+
When a module sends a pulse, it sends that type of pulse to each module in its list of destination modules.
15+
16+
There are several different types of modules:
17+
18+
Flip-flop modules (prefix %) are either on or off; they are initially off.
19+
If a flip-flop module receives a high pulse, it is ignored and nothing happens.
20+
However, if a flip-flop module receives a low pulse, it flips between on and off.
21+
If it was off, it turns on and sends a high pulse.
22+
If it was on, it turns off and sends a low pulse.
23+
24+
Conjunction modules (prefix &) remember the type of the most recent pulse received from each of their connected input modules; they initially default to remembering a low pulse for each input.
25+
When a pulse is received, the conjunction module first updates its memory for that input.
26+
Then, if it remembers high pulses for all inputs, it sends a low pulse; otherwise, it sends a high pulse.
27+
28+
There is a single broadcast module (named broadcaster).
29+
When it receives a pulse, it sends the same pulse to all of its destination modules.
30+
31+
Here at Desert Machine Headquarters, there is a module with a single button on it called, aptly, the button module.
32+
When you push the button, a single low pulse is sent directly to the broadcaster module.
33+
34+
After pushing the button, you must wait until all pulses have been delivered and fully handled before pushing it again.
35+
Never push the button if modules are still processing pulses.
36+
37+
Pulses are always processed in the order they are sent.
38+
So, if a pulse is sent to modules a, b, and c, and then module a processes its pulse and sends more pulses, the pulses sent to modules b and c would have to be handled first.
39+
40+
The module configuration (your puzzle input) lists each module.
41+
The name of the module is preceded by a symbol identifying its type, if any.
42+
The name is then followed by an arrow and a list of its destination modules.
43+
For example:
44+
45+
broadcaster -> a, b, c
46+
%a -> b
47+
%b -> c
48+
%c -> inv
49+
&inv -> a
50+
51+
In this module configuration, the broadcaster has three destination modules named a, b, and c.
52+
Each of these modules is a flip-flop module (as indicated by the % prefix).
53+
a outputs to b which outputs to c which outputs to another module named inv.
54+
inv is a conjunction module (as indicated by the & prefix) which, because it has only one input, acts like an inverter (it sends the opposite of the pulse type it receives); it outputs to a.
55+
56+
By pushing the button once, the following pulses are sent:
57+
58+
button -low-> broadcaster
59+
broadcaster -low-> a
60+
broadcaster -low-> b
61+
broadcaster -low-> c
62+
a -high-> b
63+
b -high-> c
64+
c -high-> inv
65+
inv -low-> a
66+
a -low-> b
67+
b -low-> c
68+
c -low-> inv
69+
inv -high-> a
70+
71+
After this sequence, the flip-flop modules all end up off, so pushing the button again repeats the same sequence.
72+
73+
Here's a more interesting example:
74+
75+
broadcaster -> a
76+
%a -> inv, con
77+
&inv -> b
78+
%b -> con
79+
&con -> output
80+
81+
This module configuration includes the broadcaster, two flip-flops (named a and b), a single-input conjunction module (inv), a multi-input conjunction module (con), and an untyped module named output (for testing purposes).
82+
The multi-input conjunction module con watches the two flip-flop modules and, if they're both on, sends a low pulse to the output module.
83+
84+
Here's what happens if you push the button once:
85+
86+
button -low-> broadcaster
87+
broadcaster -low-> a
88+
a -high-> inv
89+
a -high-> con
90+
inv -low-> b
91+
con -high-> output
92+
b -high-> con
93+
con -low-> output
94+
95+
Both flip-flops turn on and a low pulse is sent to output! However, now that both flip-flops are on and con remembers a high pulse from each of its two inputs, pushing the button a second time does something different:
96+
97+
button -low-> broadcaster
98+
broadcaster -low-> a
99+
a -low-> inv
100+
a -low-> con
101+
inv -high-> b
102+
con -high-> output
103+
104+
Flip-flop a turns off! Now, con remembers a low pulse from module a, and so it sends only a high pulse to output.
105+
106+
Push the button a third time:
107+
108+
button -low-> broadcaster
109+
broadcaster -low-> a
110+
a -high-> inv
111+
a -high-> con
112+
inv -low-> b
113+
con -low-> output
114+
b -low-> con
115+
con -high-> output
116+
117+
This time, flip-flop a turns on, then flip-flop b turns off.
118+
However, before b can turn off, the pulse sent to con is handled first, so it briefly remembers all high pulses for its inputs and sends a low pulse to output.
119+
After that, flip-flop b turns off, which causes con to update its state and send a high pulse to output.
120+
121+
Finally, with a on and b off, push the button a fourth time:
122+
123+
button -low-> broadcaster
124+
broadcaster -low-> a
125+
a -low-> inv
126+
a -low-> con
127+
inv -high-> b
128+
con -high-> output
129+
130+
This completes the cycle: a turns off, causing con to remember only low pulses and restoring all modules to their original states.
131+
132+
To get the cables warmed up, the Elves have pushed the button 1000 times.
133+
How many pulses got sent as a result (including the pulses sent by the button itself)?
134+
135+
In the first example, the same thing happens every time the button is pushed: 8 low pulses and 4 high pulses are sent.
136+
So, after pushing the button 1000 times, 8000 low pulses and 4000 high pulses are sent.
137+
Multiplying these together gives 32000000.
138+
139+
In the second example, after pushing the button 1000 times, 4250 low pulses and 2750 high pulses are sent.
140+
Multiplying these together gives 11687500.
141+
142+
Consult your module configuration; determine the number of low pulses and high pulses that would be sent after pushing the button 1000 times, waiting for all pulses to be fully handled after each push of the button.
143+
What do you get if you multiply the total number of low pulses sent by the total number of high pulses sent?
144+
145+
--- Part Two ---
146+
147+
The final machine responsible for moving the sand down to Island Island has a module attached named rx.
148+
The machine turns on when a single low pulse is sent to rx.
149+
150+
Reset all modules to their default states.
151+
Waiting for all pulses to be fully handled after each button press,
152+
what is the fewest number of button presses required to deliver a single low pulse to the module named rx?
153+
4154
"""
5155

6-
def solvePart1(lines):
7-
result = 0
156+
BROADCASTER = 0
157+
FLIP_FLOP = 1
158+
CONJUNCTION = 2
159+
OUTPUT = 3
160+
161+
def parse(lines):
162+
modules = {}
8163
for l in lines:
9-
result += len(l)
164+
l = l.strip()
165+
if len(l) == 0:
166+
continue
167+
# broadcaster -> a, b, c
168+
# %a -> b
169+
# %b -> c
170+
# %c -> inv
171+
# &inv -> a
172+
toks = l.split('->')
173+
name = toks[0].strip()
174+
c = name[0]
175+
t = OUTPUT
176+
if c == '%':
177+
t = FLIP_FLOP
178+
name = name[1:]
179+
elif c == '&':
180+
t = CONJUNCTION
181+
name = name[1:]
182+
elif name == 'broadcaster':
183+
t = BROADCASTER
184+
185+
outputs = toks[1].strip().split(',')
186+
for i in range(len(outputs)):
187+
outputs[i] = outputs[i].strip()
188+
modules[name] = (name, t, outputs)
189+
for o in outputs:
190+
if o not in modules:
191+
modules[o] = (o, OUTPUT, [])
192+
193+
# find the inputs to the conjunction modules
194+
inputs = {}
195+
for m in modules:
196+
src, t, outputs = modules[m]
197+
for o in outputs:
198+
_, outType, _ = modules[o]
199+
if outType == CONJUNCTION:
200+
if o not in inputs:
201+
inputs[o] = {}
202+
inputs[o][src] = 0
203+
204+
return modules, inputs
205+
206+
def press_button(state, modules, inputs, interested):
207+
signals = []
208+
countSigs = [0,0]
209+
interestedSignals = {}
210+
# button low -> broadcaster
211+
signals.append(('button', 'broadcaster', 0))
212+
213+
# run until the signals stop flowing the system i.e. go through all nodes
214+
while len(signals) > 0:
215+
src, dst, sigVal = signals.pop()
216+
if interested != None and dst == interested and sigVal == 1:
217+
interestedSignals[src] = 1
218+
219+
countSigs[sigVal] += 1
220+
name, type, outputs = modules[dst]
221+
# broadcaster : sends its signal to all its output, in sequence
222+
if type == BROADCASTER:
223+
for o in outputs:
224+
signals.insert(0, [name, o, sigVal])
225+
# flip-flop : if signal low then state ^=1 : sends state to its outputs
226+
elif type == FLIP_FLOP:
227+
if sigVal == 0:
228+
state[dst] ^= 1
229+
for o in outputs:
230+
signals.insert(0, [name, o, state[dst]])
231+
# conjunction : stores signal from each of its inputs (default low)
232+
# conjunction : updates the signal for the input it received
233+
# conjunction : if all high signals output low else output high
234+
elif type == CONJUNCTION:
235+
ins = inputs[dst]
236+
ins[src] = sigVal
237+
out = 0
238+
for i in ins:
239+
if ins[i] == 0:
240+
out = 1
241+
break
242+
state[dst] = out
243+
for o in outputs:
244+
signals.insert(0,[name, o, state[dst]])
245+
return countSigs, interestedSignals
246+
247+
def solvePart1(lines):
248+
modules, inputs = parse(lines)
249+
state = {}
250+
for m in modules:
251+
state[m] = 0
252+
253+
countLow = 0
254+
countHigh = 0
255+
for i in range(1000):
256+
countSigs, _ = press_button(state, modules, inputs, None)
257+
countLow += countSigs[0]
258+
countHigh += countSigs[1]
259+
result = countLow * countHigh
10260
return result
11261

12262
def solvePart2(lines):
13-
result = 0
14-
for l in lines:
15-
result += len(l)
16-
return result
263+
modules, inputs = parse(lines)
264+
265+
# Find the module that outputs to rx
266+
rxInput = None
267+
for m in modules:
268+
name, type, outputs = modules[m]
269+
if 'rx' in outputs:
270+
rxInput = m
271+
break
272+
273+
if rxInput == None:
274+
return None
275+
276+
# Check it is a CONJUNCTION module
277+
if modules[rxInput][1] != CONJUNCTION:
278+
return None
279+
280+
# Find the inputs to that conjunction module
281+
conjIns = inputs[rxInput]
282+
283+
cycleCounts = {}
284+
for i in conjIns:
285+
cycleCounts[i] = 0
286+
287+
# Find the button press cycle count which send low signsl to each input
288+
# Then return lowest common demoninator of those cycles
289+
buttonCount = 0
290+
state = {}
291+
for m in modules:
292+
state[m] = 0
293+
294+
found = 0
295+
while True:
296+
buttonCount += 1
297+
_, interestedSignals = press_button(state, modules, inputs, rxInput)
298+
for i in conjIns:
299+
if cycleCounts[i] > 0:
300+
continue
301+
if i in interestedSignals:
302+
cycleCounts[i] = buttonCount
303+
found += 1
304+
if found == len(cycleCounts):
305+
break
306+
if buttonCount > 10000:
307+
break
308+
309+
return math.lcm(*cycleCounts.values())
17310

18311
def main():
19-
aoc.run_day(20, solvePart1, 123, solvePart2, 456)
312+
aoc.run_day(20, solvePart1, 867118762, solvePart2, 217317393039529)
20313

21314
if __name__ == '__main__':
22315
main()

0 commit comments

Comments
 (0)