import logging import operator import os import sys from typing import Callable VERBOSE = os.getenv("AOC_VERBOSE") == "True" logging.basicConfig(level=logging.INFO if VERBOSE else logging.WARNING) OPERATORS = { "AND": operator.and_, "OR": operator.or_, "LSHIFT": operator.lshift, "RSHIFT": operator.rshift, } ValueGetter = Callable[[dict[str, int]], int] Signals = dict[ str, tuple[ tuple[str, str], tuple[ValueGetter, ValueGetter], Callable[[int, int], int], ], ] def zero_op(_a: int, _b: int) -> int: return 0 def value_of(key: str) -> tuple[str, Callable[[dict[str, int]], int]]: try: return "", lambda _p, _v=int(key): _v except ValueError: return key, lambda values: values[key] lines = sys.stdin.read().splitlines() signals: Signals = {} values: dict[str, int] = {"": 0} for line in lines: command, signal = line.split(" -> ") if command.startswith("NOT"): name = command.split(" ")[1] signals[signal] = ( (name, ""), (lambda values, _n=name: values[_n], lambda _v: 0), lambda a, _b: ~a, ) elif not any(command.find(name) >= 0 for name in OPERATORS): try: values[signal] = int(command) except ValueError: signals[signal] = ( (command, ""), (lambda values, _c=command: values[_c], lambda _v: 0), lambda a, _b: a, ) else: op: Callable[[int, int], int] = zero_op lhs_s, rhs_s = "", "" for name in OPERATORS: if command.find(name) >= 0: op = OPERATORS[name] lhs_s, rhs_s = command.split(f" {name} ") break lhs_s, lhs_fn = value_of(lhs_s) rhs_s, rhs_fn = value_of(rhs_s) signals[signal] = ((lhs_s, rhs_s), (lhs_fn, rhs_fn), op) def process( signals: Signals, values: dict[str, int], ) -> dict[str, int]: while signals: signal = next(s for s in signals if all(p in values for p in signals[s][0])) _, deps, command = signals[signal] values[signal] = command(deps[0](values), deps[1](values)) % 65536 del signals[signal] return values values_1 = process(signals.copy(), values.copy()) logging.info("\n" + "\n".join(f"{k}: {values_1[k]}" for k in sorted(values_1))) answer_1 = values_1["a"] print(f"answer 1 is {answer_1}") values_2 = process(signals.copy(), values | {"b": values_1["a"]}) answer_2 = values_2["a"] print(f"answer 2 is {answer_2}")