diff --git a/day20.py b/day20.py new file mode 100644 index 0000000..a5c9122 --- /dev/null +++ b/day20.py @@ -0,0 +1,151 @@ +from __future__ import annotations +import math +from collections import defaultdict, deque +from tools.aoc import AOCDay +from typing import Any + + +class Module: + def __init__(self, name: str, machine: Machine): + self.name = name + self.machine = machine + self.state = False + self.last_input = defaultdict(bool) + self.inputs = [] + self.outputs = [] + + def add_output(self, output: str): + self.outputs.append(output) + + def add_input(self, input: str): + self.inputs.append(input) + + def send(self, signal: bool): + self.state = signal + for output in self.outputs: + self.machine.send(self.name, output, signal) + + def receive(self, sender: str, signal: bool): + raise NotImplementedError() + + +class Broadcaster(Module): + def receive(self, sender: str, signal: bool): + self.send(signal) + + +class FlipFlop(Module): + def receive(self, sender: str, signal: bool): + if not signal: + self.send(not self.state) + + +class Conjunction(Module): + def receive(self, sender: str, signal: bool): + self.last_input[sender] = signal + if sum(self.last_input.values()) == len(self.inputs): + self.send(False) + else: + self.send(True) + + +class Machine: + def __init__(self): + self.queue = deque() + self.send_signals = { + True: 0, False: 0 + } + self.modules = {} + + def add_module(self, module: Module): + self.modules[module.name] = module + + def update_connections(self): + modules = list(self.modules.values()) + for module in modules: + module.inputs = [] + + for module in modules: + for output in module.outputs: + if output not in self.modules: + self.modules[output] = FlipFlop(output, self) + self.modules[output].add_input(module.name) + + def send(self, sender: str, destination: str, signal: bool): + self.queue.append((sender, destination, signal)) + + def push_button(self, monitor: str = None): + self.send('button', 'broadcaster', False) + monitor_highs = [] + while self.queue: + sender, destination, signal = self.queue.popleft() + if destination == monitor and signal: + monitor_highs.append(sender) + self.send_signals[signal] += 1 + self.modules[destination].receive(sender, signal) + + return monitor_highs + + def get_signal_value(self) -> int: + return self.send_signals[True] * self.send_signals[False] + + +class Day(AOCDay): + inputs = [ + [ + (32000000, "input20_test"), + (11687500, "input20_test2"), + (1020211150, "input20"), + ], + [ + (238815727638557, "input20"), + ] + ] + + def parse_input(self) -> Machine: + machine = Machine() + for line in self.getInput(): + module_name, targets = line.split(" -> ") + if module_name.startswith("broad"): + module = Broadcaster(module_name, machine) + elif module_name.startswith("%"): + module = FlipFlop(module_name[1:], machine) + elif module_name.startswith("&"): + module = Conjunction(module_name[1:], machine) + else: + assert False, "unknown module type: %s" % module_name + + for target in targets.split(", "): + module.add_output(target) + + machine.add_module(module) + + machine.update_connections() + return machine + + def part1(self) -> Any: + machine = self.parse_input() + for _ in range(1000): + machine.push_button() + return machine.get_signal_value() + + def part2(self) -> Any: + machine = self.parse_input() + count = 0 + input_highs = {} + to_monitor = machine.modules["rx"].inputs[0] + wait_for = len(machine.modules[to_monitor].inputs) + while True: + count += 1 + found_highs = machine.push_button(monitor=to_monitor) + + for high in found_highs: + input_highs[high] = count + + if len(input_highs) == wait_for: + return math.lcm(*list(input_highs.values())) + + +if __name__ == '__main__': + day = Day(2023, 20) + day.run(verbose=True) diff --git a/inputs/input20 b/inputs/input20 new file mode 100644 index 0000000..f0970b5 --- /dev/null +++ b/inputs/input20 @@ -0,0 +1,58 @@ +%ls -> gl +%rz -> vm, gl +broadcaster -> rz, fp, kv, fd +%ql -> bn +%bm -> hr, fj +%fp -> cc, gk +&lk -> nc +%xg -> gl, mz +%dg -> gk, mp +%zg -> ls, gl +%lg -> hr +%pt -> lg, hr +%sp -> mj +%ms -> gl, hx +%kj -> fl, gk +%bn -> rj, gk +%xc -> vq +%fl -> gk +%dh -> hr, nm +%jk -> gk, dg +%tf -> cb +%kd -> cm, nr +&hr -> hh, kv, xl, qq +%kv -> xr, hr +%hq -> ql +&fn -> nc +%vm -> gl, xn +%jh -> nr, kd +%mz -> dd +%tp -> hq +%cf -> nr +%gr -> jh +%jd -> hr, bm +%xr -> qq, hr +%cm -> nr, cf +&fh -> nc +%rb -> xl, hr +&nc -> rx +%mp -> gk, kj +&nr -> fd, gr, fn, cb, tf, xc, vq +&gl -> fh, xn, sp, mz, rz, mj, dd +%rj -> jk +&hh -> nc +%fd -> nr, df +&gk -> lk, tp, fp, ql, hq, rj +%fj -> pt, hr +%qq -> dh +%df -> nr, nv +%mj -> ms +%xn -> xg +%cc -> gk, tp +%nm -> rb, hr +%dd -> sp +%vq -> gr +%cb -> xc +%nv -> tf, nr +%xl -> jd +%hx -> gl, zg diff --git a/inputs/input20_test b/inputs/input20_test new file mode 100644 index 0000000..9ed10dd --- /dev/null +++ b/inputs/input20_test @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a \ No newline at end of file diff --git a/inputs/input20_test2 b/inputs/input20_test2 new file mode 100644 index 0000000..4da4379 --- /dev/null +++ b/inputs/input20_test2 @@ -0,0 +1,5 @@ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output \ No newline at end of file