From d61d9d455992e35d4165a7f8914aee9359ccb07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 24 Dec 2024 12:33:53 +0100 Subject: [PATCH] 2024 day 24. --- src/holt59/aoc/2024/day24.py | 167 +++++++++- src/holt59/aoc/__main__.py | 29 +- src/holt59/aoc/inputs/holt59/2024/day24.txt | 313 ++++++++++++++++++ .../tests/2024/{day15.txt => day15_v1.txt} | 0 src/holt59/aoc/inputs/tests/2024/day15_v2.txt | 10 + src/holt59/aoc/inputs/tests/2024/day15_v3.txt | 9 + .../tests/2024/{day16.txt => day16_v1.txt} | 0 .../2024/{day16_p1v2.txt => day16_v2.txt} | 0 .../tests/2024/{day17.txt => day17_v1.txt} | 0 .../tests/2024/{day17_p2.txt => day17_v2.txt} | 0 .../tests/2024/{day22.txt => day22_v1.txt} | 0 .../tests/2024/{day22_p2.txt => day22_v2.txt} | 0 src/holt59/aoc/inputs/tests/2024/day24.txt | 0 src/holt59/aoc/inputs/tests/2024/day24_v1.txt | 10 + src/holt59/aoc/inputs/tests/2024/day24_v2.txt | 47 +++ src/holt59/aoc/inputs/tests/2024/day24_v3.txt | 19 ++ 16 files changed, 599 insertions(+), 5 deletions(-) rename src/holt59/aoc/inputs/tests/2024/{day15.txt => day15_v1.txt} (100%) create mode 100644 src/holt59/aoc/inputs/tests/2024/day15_v2.txt create mode 100644 src/holt59/aoc/inputs/tests/2024/day15_v3.txt rename src/holt59/aoc/inputs/tests/2024/{day16.txt => day16_v1.txt} (100%) rename src/holt59/aoc/inputs/tests/2024/{day16_p1v2.txt => day16_v2.txt} (100%) rename src/holt59/aoc/inputs/tests/2024/{day17.txt => day17_v1.txt} (100%) rename src/holt59/aoc/inputs/tests/2024/{day17_p2.txt => day17_v2.txt} (100%) rename src/holt59/aoc/inputs/tests/2024/{day22.txt => day22_v1.txt} (100%) rename src/holt59/aoc/inputs/tests/2024/{day22_p2.txt => day22_v2.txt} (100%) delete mode 100644 src/holt59/aoc/inputs/tests/2024/day24.txt create mode 100644 src/holt59/aoc/inputs/tests/2024/day24_v1.txt create mode 100644 src/holt59/aoc/inputs/tests/2024/day24_v2.txt create mode 100644 src/holt59/aoc/inputs/tests/2024/day24_v3.txt diff --git a/src/holt59/aoc/2024/day24.py b/src/holt59/aoc/2024/day24.py index 07e201e..cc4b6d3 100644 --- a/src/holt59/aoc/2024/day24.py +++ b/src/holt59/aoc/2024/day24.py @@ -1,7 +1,170 @@ -from typing import Any, Iterator +from dataclasses import dataclass +from typing import Any, Iterator, Literal, TypeAlias, cast from ..base import BaseSolver +GateType: TypeAlias = Literal["and", "or", "xor"] + + +@dataclass(frozen=True, eq=True) +class Gate: + type: GateType + lhs: str + rhs: str + + def __call__(self, lhs: int, rhs: int) -> Any: + match self.type: + case "or": + return int(lhs or rhs) + case "and": + return int(lhs and rhs) + case "xor": + return int(lhs != rhs) + class Solver(BaseSolver): - def solve(self, input: str) -> Iterator[Any]: ... + def solve(self, input: str) -> Iterator[Any]: + inputs_s, gates_s = input.split("\n\n") + + wires: dict[str, int | None] = {} + for row in inputs_s.splitlines(): + parts = row.split(": ") + wires[parts[0]] = int(parts[1]) + + gates: dict[str, Gate] = {} + for row in gates_s.splitlines(): + parts = row.split() + assert parts[4] not in gates + assert parts[4] not in wires + + lhs, rhs = sorted([parts[0], parts[2]]) + + gates[parts[4]] = Gate(cast(GateType, parts[1].lower()), lhs, rhs) + wires[parts[4]] = None + + if self.files: + content = "digraph G {\n" + for wire in wires: + content += f'{wire} [label="{wire}"]\n' + for wire, gate in gates.items(): + gate_n = f"G_{wire}" + content += f'{gate_n} [label="{gate.type.upper()}"]\n' + content += f"{gate.lhs} -> {gate_n}\n" + content += f"{gate.rhs} -> {gate_n}\n" + content += f"{gate_n} -> {wire}\n" + content += "}\n" + self.files.create("gates.dot", content.encode(), text=False) + + wires_to_find = set(gates) + + while wires_to_find: + wires_found: list[str] = [] + for wire in wires_to_find: + gate = gates[wire] + + lhs, rhs = wires[gate.lhs], wires[gate.rhs] + + if lhs is None or rhs is None: + continue + + assert wires[wire] is None + wires[wire] = gate(lhs, rhs) + + wires_found.append(wire) + + wires_to_find.difference_update(wires_found) + + z_wires = sorted((wire for wire in wires if wire.startswith("z"))) + self.logger.info( + "binary value is '{}'".format( + "".join(str(wires[w]) for w in reversed(z_wires)) + ) + ) + yield int("".join(str(wires[w]) for w in reversed(z_wires)), base=2) + + # e00 = x00 ^ y00 + # z00 = e00 + # r00 = x00 & y00 + + # e01 = x01 ^ y01 + # z01 = r00 ^ e01 + # a01 = x01 & y01 + # b01 = r00 & e01 + # r01 = a01 | (r00 & e01) + + assert gates["z00"] == Gate("xor", "x00", "y00") + + # normalized names -> gate name + m_gates: dict[str, str] = {} + + def find_gate(type: GateType, lhs: str, rhs: str): + try: + return next( + wire + for wire, gate in gates.items() + if gate.type == type + and {gate.lhs, gate.rhs} + == {m_gates.get(lhs, lhs), m_gates.get(rhs, rhs)} + ) + except StopIteration as ex: + self.logger.info( + f"gate {lhs} [{m_gates.get(lhs, lhs)}] {type} {rhs} [{m_gates.get(rhs, rhs)}] not found" + ) + raise ex + + # find the r00 gate (= x00 & y00) + m_gates["r00"] = find_gate("and", "x00", "y00") + + swapped: list[str] = [] + + for i_wire, z_wire in enumerate(z_wires[1:-1], start=1): + i2d = f"{i_wire:02d}" + r0n = f"r{i_wire - 1:02d}" + m_gates[f"e{i2d}"] = find_gate("xor", f"x{i2d}", f"y{i2d}") + + try: + z_gate = find_gate("xor", r0n, f"e{i2d}") + except StopIteration: + # gate xor not found -> one of the input gate has been swapped + # + # assume there is a XOR gate with the remainder, so it is the other + # input that has been swapped + assert gates[z_wire].type == "xor" + assert m_gates.get(r0n, r0n) in (gates[z_wire].lhs, gates[z_wire].rhs) + + wrong_wire_1 = ( + gates[z_wire].lhs + if gates[z_wire].rhs == m_gates.get(r0n, r0n) + else gates[z_wire].rhs + ) + wrong_wire_2 = m_gates[f"e{i2d}"] + + # we are going to fix all the gates (there is probably only 2 but + # eh...) whose wires needs to be swapped + + self.logger.info(f"swapping {wrong_wire_1} <> {wrong_wire_2}") + switch = {wrong_wire_1: wrong_wire_2, wrong_wire_2: wrong_wire_1} + + for wire, gate in list(gates.items()): + lhs, rhs = ( + switch.get(gate.lhs, gate.lhs), + switch.get(gate.rhs, gate.rhs), + ) + if lhs != gate.lhs or rhs != gate.rhs: + gates[wire] = Gate(gate.type, lhs, rhs) + + swapped.extend((wrong_wire_1, wrong_wire_2)) + + z_gate = find_gate("xor", r0n, f"e{i2d}") + + if z_gate != z_wire: + self.logger.info(f"swapping {z_gate} <> {z_wire}") + gates[z_gate], gates[z_wire] = gates[z_wire], gates[z_gate] + swapped.extend((z_gate, z_wire)) + + m_gates[f"a{i2d}"] = find_gate("and", f"x{i2d}", f"y{i2d}") + m_gates[f"b{i2d}"] = find_gate("and", r0n, f"e{i2d}") + m_gates[f"r{i2d}"] = find_gate("or", f"a{i2d}", f"b{i2d}") + + assert len(swapped) == 8 + yield ",".join(sorted(swapped)) diff --git a/src/holt59/aoc/__main__.py b/src/holt59/aoc/__main__.py index 2586fbd..dd347e3 100644 --- a/src/holt59/aoc/__main__.py +++ b/src/holt59/aoc/__main__.py @@ -12,10 +12,29 @@ from .utils.files import SimpleFileHandler from .utils.progress import ProgressNone, ProgressTQDM +def find_input_file(folder: Path, day: int, target: Path | None): + if (path := folder.joinpath(f"day{day}.txt")).exists(): + return path + + if ( + target is not None + and (path := folder.joinpath(f"day{day}_v{target}.txt")).exists() + ): + return path + + try: + return next(path for path in sorted(folder.glob(f"day{day}*.txt"))) + except StopIteration: + ... + + return folder.joinpath(f"day{day}.txt") + + def main(): parser = argparse.ArgumentParser("Holt59 Advent-Of-Code Runner") parser.add_argument("-v", "--verbose", action="store_true", help="verbose mode") parser.add_argument("-t", "--test", action="store_true", help="test mode") + parser.add_argument("-a", "--api", action="store_true", help="API mode") parser.add_argument( "-o", @@ -61,9 +80,13 @@ def main(): handlers=[LoggerAPIHandler()] if api else None, ) - if input_path is None: - input_path = Path(__file__).parent.joinpath( - "inputs", "tests" if test else user, str(year), f"day{day}.txt" + if input_path is None or not input_path.exists(): + input_path = find_input_file( + Path(__file__).parent.joinpath( + "inputs", "tests" if test else user, str(year) + ), + day, + input_path, ) assert input_path.exists(), f"{input_path} missing" diff --git a/src/holt59/aoc/inputs/holt59/2024/day24.txt b/src/holt59/aoc/inputs/holt59/2024/day24.txt index e69de29..d45e7eb 100644 --- a/src/holt59/aoc/inputs/holt59/2024/day24.txt +++ b/src/holt59/aoc/inputs/holt59/2024/day24.txt @@ -0,0 +1,313 @@ +x00: 1 +x01: 0 +x02: 1 +x03: 1 +x04: 0 +x05: 0 +x06: 1 +x07: 1 +x08: 0 +x09: 1 +x10: 1 +x11: 1 +x12: 1 +x13: 0 +x14: 1 +x15: 1 +x16: 1 +x17: 1 +x18: 1 +x19: 1 +x20: 0 +x21: 1 +x22: 0 +x23: 1 +x24: 0 +x25: 1 +x26: 1 +x27: 1 +x28: 1 +x29: 0 +x30: 0 +x31: 1 +x32: 0 +x33: 1 +x34: 1 +x35: 0 +x36: 0 +x37: 1 +x38: 0 +x39: 1 +x40: 1 +x41: 1 +x42: 1 +x43: 0 +x44: 1 +y00: 1 +y01: 0 +y02: 0 +y03: 1 +y04: 1 +y05: 0 +y06: 0 +y07: 0 +y08: 0 +y09: 0 +y10: 0 +y11: 1 +y12: 0 +y13: 1 +y14: 0 +y15: 1 +y16: 1 +y17: 1 +y18: 1 +y19: 0 +y20: 0 +y21: 1 +y22: 1 +y23: 1 +y24: 1 +y25: 0 +y26: 0 +y27: 1 +y28: 1 +y29: 1 +y30: 1 +y31: 0 +y32: 0 +y33: 0 +y34: 1 +y35: 1 +y36: 1 +y37: 0 +y38: 1 +y39: 1 +y40: 1 +y41: 1 +y42: 0 +y43: 1 +y44: 1 + +x03 AND y03 -> htr +gwb AND kvf -> pkd +x04 AND y04 -> jjm +qcm XOR twv -> z21 +rrq XOR bmp -> z44 +x43 AND y43 -> pnn +x06 XOR y06 -> qmt +x26 AND y26 -> z26 +y00 AND x00 -> whb +jfq XOR fbb -> z36 +y33 AND x33 -> mmb +x38 AND y38 -> vqt +bbh OR qtd -> jfq +cbs AND ttb -> qtd +wqs OR cmf -> tpf +x10 AND y10 -> bfm +djp OR pfb -> qvr +x20 XOR y20 -> vhb +kkd XOR cjg -> z32 +qpp XOR stg -> z41 +kkd AND cjg -> mdv +tpp OR pfj -> twv +www AND qdf -> vjf +y15 XOR x15 -> hmr +mtg XOR sqm -> z09 +x33 XOR y33 -> chc +x41 AND y41 -> pkj +x31 AND y31 -> cvn +x09 AND y09 -> nvw +mtg AND sqm -> chg +pkr AND kcv -> thc +x07 XOR y07 -> cds +x15 AND y15 -> fpr +mwv AND jsg -> wdw +mwv XOR jsg -> z38 +y16 XOR x16 -> svs +y14 XOR x14 -> fnq +wth OR vjf -> btv +bvp AND gdb -> stc +cjb XOR rjc -> z04 +x13 AND y13 -> pfb +x30 AND y30 -> qgf +htq AND rtk -> dsm +x18 XOR y18 -> kvf +y12 AND x12 -> mqn +bcj XOR bkh -> z03 +x07 AND y07 -> sdj +bdf OR wbw -> qkf +y30 XOR x30 -> kbn +tpf AND vhb -> tpp +hqd OR fpr -> hgh +vfm XOR hbw -> z23 +x01 AND y01 -> bdf +nvw OR chg -> vgp +x21 XOR y21 -> qcm +bwg AND mfn -> djp +dnf OR pkj -> ksp +y44 AND x44 -> gqr +y11 AND x11 -> smr +smr OR dsm -> ksn +jkm OR pkd -> rjf +thc OR sqt -> rbd +qvr XOR fnq -> z14 +cjb AND rjc -> fsb +svg XOR fmt -> z31 +x06 AND y06 -> ssv +dtj OR vvq -> jvp +chv XOR fqf -> z34 +cvr AND hck -> pjd +dqp AND nbm -> hvv +x29 AND y29 -> vvq +y13 XOR x13 -> mfn +ksn AND nft -> z12 +jjd XOR whb -> z01 +chc AND rnq -> vjh +y36 AND x36 -> kfn +cwh OR vvw -> ttb +qkf AND wsv -> pqc +rdj OR kfv -> gdb +x08 AND y08 -> jrr +x02 AND y02 -> vdf +x12 XOR y12 -> nft +ptf OR jrr -> sqm +tdv OR wjp -> cjw +qvr AND fnq -> mch +x28 XOR y28 -> cfj +gtn XOR qmt -> z06 +mqn OR jpj -> bwg +x36 XOR y36 -> fbb +qht OR bfm -> htq +y42 AND x42 -> mkg +ksn XOR nft -> jpj +x20 AND y20 -> pfj +cmt AND nbq -> gmc +rbd XOR knm -> z25 +pvj XOR ksp -> z42 +kgj OR stc -> www +tpf XOR vhb -> z20 +pjd OR dsg -> mwv +cbs XOR ttb -> z35 +bfk OR jvm -> gwb +ffj XOR rpg -> z17 +vjr OR kwg -> pkr +pvj AND ksp -> dkc +y37 XOR x37 -> cvr +btv XOR cfj -> z28 +gtq OR qgf -> fmt +nbq XOR cmt -> z39 +wgq AND dqj -> tws +x24 AND y24 -> sqt +whj OR pnn -> bmp +x02 XOR y02 -> wsv +stg AND qpp -> dnf +kbn XOR jvp -> z30 +y39 AND x39 -> gwq +cds AND rkv -> nph +kvf XOR gwb -> z18 +mkg OR dkc -> sch +bqh XOR rjf -> z19 +hck XOR cvr -> z37 +jmk OR ssv -> rkv +x21 AND y21 -> cgd +pqc OR vdf -> bkh +rff OR mts -> rpg +bkh AND bcj -> rhq +bnv OR bst -> stg +bwg XOR mfn -> z13 +sgt AND scc -> bnv +btv AND cfj -> tdv +svs AND hgh -> rff +hbw AND vfm -> kwg +x40 XOR y40 -> scc +y17 AND x17 -> jvm +y34 AND x34 -> chv +y35 AND x35 -> bbh +mdv OR rft -> rnq +fqf AND chv -> cwh +y28 AND x28 -> wjp +sch AND srj -> whj +htr OR rhq -> rjc +x05 XOR y05 -> dqp +cvn OR qnk -> cjg +y14 AND x14 -> tfr +y11 XOR x11 -> rtk +jfq AND fbb -> trr +ppb AND hmr -> hqd +gtb OR hvv -> gtn +y44 XOR x44 -> rrq +rtk XOR htq -> z11 +x01 XOR y01 -> jjd +hmv XOR rts -> z08 +y10 XOR x10 -> vpc +jvp AND kbn -> gtq +cjw AND ntj -> dtj +x22 AND y22 -> prp +ppb XOR hmr -> z15 +y18 AND x18 -> jkm +x39 XOR y39 -> nbq +jjd AND whb -> wbw +x34 XOR y34 -> vvw +x19 AND y19 -> wqs +gwq OR gmc -> sgt +rbd AND knm -> rdj +srj XOR sch -> z43 +y05 AND x05 -> gtb +x08 XOR y08 -> hmv +y25 AND x25 -> kfv +cgd OR jth -> dqj +vpc XOR vgp -> z10 +tws OR prp -> hbw +jjm OR fsb -> nbm +wdw OR vqt -> cmt +rrq AND bmp -> cbv +rts AND hmv -> ptf +svs XOR hgh -> z16 +y41 XOR x41 -> qpp +ntj XOR cjw -> z29 +ffj AND rpg -> bfk +gqr OR cbv -> z45 +x25 XOR y25 -> knm +chc XOR rnq -> z33 +y43 XOR x43 -> srj +vgp AND vpc -> qht +x00 XOR y00 -> z00 +cds XOR rkv -> rts +x24 XOR y24 -> kcv +x32 AND y32 -> rft +nbm XOR dqp -> z05 +x35 XOR y35 -> cbs +mch OR tfr -> ppb +x16 AND y16 -> mts +www XOR qdf -> z27 +x23 AND y23 -> vjr +x26 XOR y26 -> bvp +gtn AND qmt -> jmk +x29 XOR y29 -> ntj +y19 XOR x19 -> bqh +rjf AND bqh -> cmf +y38 XOR x38 -> jsg +x32 XOR y32 -> kkd +y03 XOR x03 -> bcj +y31 XOR x31 -> svg +y22 XOR x22 -> wgq +qkf XOR wsv -> z02 +bvp XOR gdb -> kgj +x04 XOR y04 -> cjb +x17 XOR y17 -> ffj +y37 AND x37 -> dsg +y27 AND x27 -> wth +y23 XOR x23 -> vfm +sgt XOR scc -> z40 +mmb OR vjh -> fqf +qcm AND twv -> jth +y09 XOR x09 -> mtg +sdj OR nph -> z07 +wgq XOR dqj -> z22 +trr OR kfn -> hck +y27 XOR x27 -> qdf +kcv XOR pkr -> z24 +x42 XOR y42 -> pvj +x40 AND y40 -> bst +svg AND fmt -> qnk diff --git a/src/holt59/aoc/inputs/tests/2024/day15.txt b/src/holt59/aoc/inputs/tests/2024/day15_v1.txt similarity index 100% rename from src/holt59/aoc/inputs/tests/2024/day15.txt rename to src/holt59/aoc/inputs/tests/2024/day15_v1.txt diff --git a/src/holt59/aoc/inputs/tests/2024/day15_v2.txt b/src/holt59/aoc/inputs/tests/2024/day15_v2.txt new file mode 100644 index 0000000..8163605 --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2024/day15_v2.txt @@ -0,0 +1,10 @@ +######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<< diff --git a/src/holt59/aoc/inputs/tests/2024/day15_v3.txt b/src/holt59/aoc/inputs/tests/2024/day15_v3.txt new file mode 100644 index 0000000..6ee6098 --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2024/day15_v3.txt @@ -0,0 +1,9 @@ +####### +#...#.# +#.....# +#..OO@# +#..O..# +#.....# +####### + + z00 +x01 XOR y01 -> z01 +x02 OR y02 -> z02 diff --git a/src/holt59/aoc/inputs/tests/2024/day24_v2.txt b/src/holt59/aoc/inputs/tests/2024/day24_v2.txt new file mode 100644 index 0000000..94b6eed --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2024/day24_v2.txt @@ -0,0 +1,47 @@ +x00: 1 +x01: 0 +x02: 1 +x03: 1 +x04: 0 +y00: 1 +y01: 1 +y02: 1 +y03: 1 +y04: 1 + +ntg XOR fgs -> mjb +y02 OR x01 -> tnw +kwq OR kpj -> z05 +x00 OR x03 -> fst +tgd XOR rvg -> z01 +vdt OR tnw -> bfw +bfw AND frj -> z10 +ffh OR nrd -> bqk +y00 AND y03 -> djm +y03 OR y00 -> psh +bqk OR frj -> z08 +tnw OR fst -> frj +gnj AND tgd -> z11 +bfw XOR mjb -> z00 +x03 OR x00 -> vdt +gnj AND wpb -> z02 +x04 AND y00 -> kjc +djm OR pbm -> qhw +nrd AND vdt -> hwm +kjc AND fst -> rvg +y04 OR y02 -> fgs +y01 AND x02 -> pbm +ntg OR kjc -> kwq +psh XOR fgs -> tgd +qhw XOR tgd -> z09 +pbm OR djm -> kpj +x03 XOR y03 -> ffh +x00 XOR y04 -> ntg +bfw OR bqk -> z06 +nrd XOR fgs -> wpb +frj XOR qhw -> z04 +bqk OR frj -> z07 +y03 OR x01 -> nrd +hwm AND bqk -> z03 +tgd XOR rvg -> z12 +tnw OR pbm -> gnj diff --git a/src/holt59/aoc/inputs/tests/2024/day24_v3.txt b/src/holt59/aoc/inputs/tests/2024/day24_v3.txt new file mode 100644 index 0000000..7709fe2 --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2024/day24_v3.txt @@ -0,0 +1,19 @@ +x00: 0 +x01: 1 +x02: 0 +x03: 1 +x04: 0 +x05: 1 +y00: 0 +y01: 0 +y02: 1 +y03: 1 +y04: 0 +y05: 1 + +x00 AND y00 -> z05 +x01 AND y01 -> z02 +x02 AND y02 -> z01 +x03 AND y03 -> z03 +x04 AND y04 -> z04 +x05 AND y05 -> z00