diff --git a/2023/d20/ex1/ex1.py b/2023/d20/ex1/ex1.py deleted file mode 100755 index 7c29abb..0000000 --- a/2023/d20/ex1/ex1.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python - -import enum -import math -import sys -from collections import defaultdict, deque -from typing import NamedTuple - - -class ModuleType(enum.StrEnum): - FLIP_FLOP = "%" - CONJUNCTION = "&" - BROADCAST = "broadcaster" - - -class Pulse(enum.IntEnum): - LOW = 0 - HIGH = 1 - - -class Rule(NamedTuple): - module_type: ModuleType - destinations: list[str] - - -Modules = dict[str, Rule] - - -def solve(input: list[str]) -> int: - def parse_rule(line: str) -> tuple[str, Rule]: - module, outputs = line.split(" -> ") - - name: str - module_type: ModuleType - - if module != "broadcaster": - name = module[1:] - module_type = ModuleType(module[0]) - else: - name = module - module_type = ModuleType(module) - - return name, Rule(module_type, outputs.split(", ")) - - def parse(input: list[str]) -> Modules: - return dict(map(parse_rule, input)) - - def compute_inputs(modules: Modules) -> dict[str, list[str]]: - inputs: dict[str, list[str]] = defaultdict(list) - - for src, rule in modules.items(): - for dst in rule.destinations: - inputs[dst].append(src) - - return inputs - - def count_pulses(modules: Modules, button_pushes: int) -> tuple[int, int]: - inputs = compute_inputs(modules) - total_pulses = {pulse: 0 for pulse in Pulse} - last_pulse: dict[str, Pulse] = defaultdict(lambda: Pulse.LOW) - - for _ in range(button_pushes): - queue: deque[tuple[Pulse, str]] = deque([(Pulse.LOW, "broadcaster")]) - - while queue: - pulse, name = queue.popleft() - total_pulses[pulse] += 1 - mod = modules.get(name) - - # This is for unknown outputs - if mod is None: - continue - - new_pulse: Pulse - match mod.module_type: - case ModuleType.FLIP_FLOP: - if pulse == Pulse.HIGH: - continue - new_pulse = Pulse(1 - last_pulse[name]) - case ModuleType.CONJUNCTION: - high_inputs = all( - last_pulse[input] == Pulse.HIGH for input in inputs[name] - ) - new_pulse = Pulse.LOW if high_inputs else Pulse.HIGH - case ModuleType.BROADCAST: - new_pulse = pulse - - last_pulse[name] = new_pulse - for dst in mod.destinations: - queue.append((new_pulse, dst)) - - return total_pulses[Pulse.LOW], total_pulses[Pulse.HIGH] - - modules = parse(input) - return math.prod(count_pulses(modules, 1000)) - - -def main() -> None: - input = sys.stdin.read().splitlines() - print(solve(input)) - - -if __name__ == "__main__": - main() diff --git a/2023/d20/ex1/input b/2023/d20/ex1/input deleted file mode 100644 index 5447f5f..0000000 --- a/2023/d20/ex1/input +++ /dev/null @@ -1,58 +0,0 @@ -%fl -> tf, gz -%xb -> hl, tl -%mq -> tf, fl -%px -> hl, tm -%dp -> xv -broadcaster -> js, ng, lb, gr -&ql -> rx -%gk -> hm -%vp -> vf, sn -%fp -> xb -&lr -> ss, rm, dc, js, gk, dp, bq -%xl -> gx, lr -%xx -> hb -%cb -> jg -&hl -> nj, lb, tl, xx, hb, fp, mf -%vr -> tf, hq -%bq -> gk -%jg -> qn -%hb -> qk -%qk -> hs, hl -%gz -> tf -%rm -> hj -&tf -> cb, jg, fz, gr, zj, qn, kb -%qn -> td -%js -> lr, dc -%qb -> nc -%zj -> vr -%td -> tf, zj -%tl -> kg -%gx -> lr -%hm -> lr, rd -&fh -> ql -%nj -> xx -%hq -> kb, tf -%kg -> px, hl -%dc -> dp -%vf -> th, sn -&mf -> ql -%tm -> hl -&fz -> ql -%xd -> tn, sn -%ng -> vp, sn -%th -> qb -%rd -> xl, lr -%bt -> xd, sn -%tv -> sn -%nl -> bt -%hs -> fp, hl -%xv -> rm, lr -%tn -> sn, tv -%hj -> lr, bq -&ss -> ql -%sd -> nl -&sn -> sd, fh, th, qb, nl, ng, nc -%kb -> mq -%lb -> nj, hl -%gr -> tf, cb -%nc -> sd diff --git a/2023/d20/ex2/ex2.py b/2023/d20/ex2/ex2.py deleted file mode 100755 index 16d81b5..0000000 --- a/2023/d20/ex2/ex2.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python - -import enum -import itertools -import math -import sys -from collections import defaultdict, deque -from typing import NamedTuple - - -class ModuleType(enum.StrEnum): - FLIP_FLOP = "%" - CONJUNCTION = "&" - BROADCAST = "broadcaster" - - -class Pulse(enum.IntEnum): - LOW = 0 - HIGH = 1 - - -class Rule(NamedTuple): - module_type: ModuleType - destinations: list[str] - - -Modules = dict[str, Rule] - - -def solve(input: list[str]) -> int: - def parse_rule(line: str) -> tuple[str, Rule]: - module, outputs = line.split(" -> ") - - name: str - module_type: ModuleType - - if module != "broadcaster": - name = module[1:] - module_type = ModuleType(module[0]) - else: - name = module - module_type = ModuleType(module) - - return name, Rule(module_type, outputs.split(", ")) - - def parse(input: list[str]) -> Modules: - return dict(map(parse_rule, input)) - - def compute_inputs(modules: Modules) -> dict[str, list[str]]: - inputs: dict[str, list[str]] = defaultdict(list) - - for src, rule in modules.items(): - for dst in rule.destinations: - inputs[dst].append(src) - - return inputs - - def count_buttons(modules: Modules) -> int: - def count_buttons_for( - wanted_src: str, - wanted_dst: str, - pulse_wanted: Pulse, - ) -> int: - inputs = compute_inputs(modules) - last_pulse: dict[str, Pulse] = defaultdict(lambda: Pulse.LOW) - - for i in itertools.count(start=1): - queue: deque[tuple[Pulse, str]] = deque([(Pulse.LOW, "broadcaster")]) - - while queue: - pulse, name = queue.popleft() - - mod = modules.get(name) - - # This is for unknown outputs - if mod is None: - continue - - new_pulse: Pulse - match mod.module_type: - case ModuleType.FLIP_FLOP: - if pulse == Pulse.HIGH: - continue - new_pulse = Pulse(1 - last_pulse[name]) - case ModuleType.CONJUNCTION: - high_inputs = all( - last_pulse[input] == Pulse.HIGH - for input in inputs[name] - ) - new_pulse = Pulse.LOW if high_inputs else Pulse.HIGH - case ModuleType.BROADCAST: - new_pulse = pulse - - last_pulse[name] = new_pulse - for dst in mod.destinations: - queue.append((new_pulse, dst)) - # We found the pulse we wanted, report the number of button presses - if ( - new_pulse == pulse_wanted - and name == wanted_src - and dst == wanted_dst - ): - return i - - assert False # Sanity check - - inputs = compute_inputs(modules) - # The input has a single conjunction leading to "rx" - # So we want to compute when all of *its* inputs are high at the same time - assert len(inputs["rx"]) == 1 # Sanity check - rx_input = inputs["rx"][0] - assert modules[rx_input].module_type == ModuleType.CONJUNCTION # Sanity check - # Shortcut: assume that the high pulse output is cyclic - return math.lcm( - *(count_buttons_for(mod, rx_input, Pulse.HIGH) for mod in inputs[rx_input]) - ) - - modules = parse(input) - return count_buttons(modules) - - -def main() -> None: - input = sys.stdin.read().splitlines() - print(solve(input)) - - -if __name__ == "__main__": - main() diff --git a/2023/d20/ex2/input b/2023/d20/ex2/input deleted file mode 100644 index 5447f5f..0000000 --- a/2023/d20/ex2/input +++ /dev/null @@ -1,58 +0,0 @@ -%fl -> tf, gz -%xb -> hl, tl -%mq -> tf, fl -%px -> hl, tm -%dp -> xv -broadcaster -> js, ng, lb, gr -&ql -> rx -%gk -> hm -%vp -> vf, sn -%fp -> xb -&lr -> ss, rm, dc, js, gk, dp, bq -%xl -> gx, lr -%xx -> hb -%cb -> jg -&hl -> nj, lb, tl, xx, hb, fp, mf -%vr -> tf, hq -%bq -> gk -%jg -> qn -%hb -> qk -%qk -> hs, hl -%gz -> tf -%rm -> hj -&tf -> cb, jg, fz, gr, zj, qn, kb -%qn -> td -%js -> lr, dc -%qb -> nc -%zj -> vr -%td -> tf, zj -%tl -> kg -%gx -> lr -%hm -> lr, rd -&fh -> ql -%nj -> xx -%hq -> kb, tf -%kg -> px, hl -%dc -> dp -%vf -> th, sn -&mf -> ql -%tm -> hl -&fz -> ql -%xd -> tn, sn -%ng -> vp, sn -%th -> qb -%rd -> xl, lr -%bt -> xd, sn -%tv -> sn -%nl -> bt -%hs -> fp, hl -%xv -> rm, lr -%tn -> sn, tv -%hj -> lr, bq -&ss -> ql -%sd -> nl -&sn -> sd, fh, th, qb, nl, ng, nc -%kb -> mq -%lb -> nj, hl -%gr -> tf, cb -%nc -> sd