Day 20: Pulse
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Python
import collections import math import re from .solver import Solver class Day20(Solver): modules: dict[str, tuple[str, list[str]]] conjunction_inputs: dict[str, set[str]] def __init__(self): super().__init__(20) def presolve(self, input: str): self.modules = {} self.conjunction_inputs = collections.defaultdict(set) for line in input.splitlines(): m = re.fullmatch(r'(\W?)(\w+) -> (.*)', line) assert m kind, name, destinations = m.groups() self.modules[name] = (kind, destinations.split(', ')) for source, (_, destinations) in self.modules.items(): for destination, (kind, _) in ((d, self.modules[d]) for d in destinations if d in self.modules): if kind == '&': self.conjunction_inputs[destination].add(source) def _press_button(self, flip_flops_on: set[str], conjunction_high_pulses: dict[str, set[str]]) -> tuple[list[str], list[str]]: low_pulses: list[str] = [] high_pulses: list[str] = [] pulse: list[tuple[str, str, int]] = [('', 'broadcaster', 0)] while pulse: origin, source, value = pulse.pop(0) if value == 0: low_pulses.append(source) else: high_pulses.append(source) if source not in self.modules: continue kind, destinations = self.modules[source] if source == 'broadcaster': for d in destinations: pulse.append((source, d, value)) elif kind == '%' and value == 0: if source in flip_flops_on: flip_flops_on.remove(source) for d in destinations: pulse.append((source, d, 0)) else: flip_flops_on.add(source) for d in destinations: pulse.append((source, d, 1)) elif kind == '&': if value: conjunction_high_pulses[source].add(origin) elif origin in conjunction_high_pulses[source]: conjunction_high_pulses[source].remove(origin) if conjunction_high_pulses[source] == self.conjunction_inputs[source]: for d in destinations: pulse.append((source, d, 0)) else: for d in destinations: pulse.append((source, d, 1)) return low_pulses, high_pulses def solve_first_star(self) -> int: flip_flops_on = set() conjunction_high_pulses = collections.defaultdict(set) low_pulse_count = 0 high_pulse_count = 0 for _ in range(1000): low, high = self._press_button(flip_flops_on, conjunction_high_pulses) low_pulse_count += len(low) high_pulse_count += len(high) return low_pulse_count* high_pulse_count def solve_second_star(self) -> int: flip_flops_on = set() conjunction_high_pulses = collections.defaultdict(set) button_count = 0 rx_upstream = [module for module, (_, destinations) in self.modules.items() if 'rx' in destinations] if len(rx_upstream) != 1: rx_upstream = [] else: rx_upstream = [module for module, (_, destinations) in self.modules.items() if rx_upstream[0] in destinations] rx_upstream_periods = [None] * len(rx_upstream) low_pulses = [] while 'rx' not in low_pulses and (not rx_upstream or not all(rx_upstream_periods)): button_count += 1 low_pulses, _ = self._press_button(flip_flops_on, conjunction_high_pulses) for module, periods in zip(rx_upstream, rx_upstream_periods, strict=True): if periods is not None: continue if module in low_pulses: rx_upstream_periods[rx_upstream.index(module)] = button_count if 'rx' in low_pulses: return button_count return math.lcm(*rx_upstream_periods)