diff --git a/2024/d24/ex1/ex1.py b/2024/d24/ex1/ex1.py new file mode 100755 index 0000000..c756b20 --- /dev/null +++ b/2024/d24/ex1/ex1.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +import copy +import enum +import sys +from typing import NamedTuple + + +class Op(enum.StrEnum): + AND = "AND" + OR = "OR" + XOR = "XOR" + + def apply(self, lhs: bool, rhs: bool) -> bool: + match self: + case Op.AND: + return lhs & rhs + case Op.OR: + return lhs | rhs + case Op.XOR: + return lhs ^ rhs + + +class Gate(NamedTuple): + lhs: str + op: Op + rhs: str + + +def solve(input: str) -> int: + def parse_values(input: list[str]) -> dict[str, bool]: + return { + name: bool(int(val)) for name, val in map(lambda s: s.split(": "), input) + } + + def parse_operation(input: str) -> tuple[str, Gate]: + lhs, op, rhs, _, name = input.split() + return name, Gate(lhs, Op(op), rhs) + + def parse_circuit(input: list[str]) -> dict[str, Gate]: + return {name: gate for name, gate in map(parse_operation, input)} + + def parse(input: str) -> tuple[dict[str, bool], dict[str, Gate]]: + values, circuit = input.split("\n\n") + return parse_values(values.splitlines()), parse_circuit(circuit.splitlines()) + + def run_circuit( + values: dict[str, bool], circuit: dict[str, Gate] + ) -> dict[str, bool]: + def helper(values: dict[str, bool], wire: str) -> bool: + if (res := values.get(wire)) is not None: + return res + gate = circuit[wire] + val = gate.op.apply(helper(values, gate.lhs), helper(values, gate.rhs)) + values[wire] = val + return val + + res = copy.copy(values) + for key in circuit: + helper(res, key) + return res + + def read_zs(values: dict[str, bool]) -> int: + zs = sorted( + (wire for wire in values.keys() if wire.startswith("z")), reverse=True + ) + res = 0 + for z in zs: + res = res << 1 | values[z] + return res + + values, circuit = parse(input) + values = run_circuit(values, circuit) + return read_zs(values) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main() diff --git a/2024/d24/ex1/input b/2024/d24/ex1/input new file mode 100644 index 0000000..b45d7bc --- /dev/null +++ b/2024/d24/ex1/input @@ -0,0 +1,313 @@ +x00: 1 +x01: 1 +x02: 1 +x03: 1 +x04: 0 +x05: 1 +x06: 0 +x07: 1 +x08: 0 +x09: 1 +x10: 1 +x11: 1 +x12: 1 +x13: 0 +x14: 1 +x15: 0 +x16: 0 +x17: 1 +x18: 0 +x19: 1 +x20: 0 +x21: 0 +x22: 0 +x23: 1 +x24: 0 +x25: 0 +x26: 1 +x27: 1 +x28: 0 +x29: 0 +x30: 1 +x31: 1 +x32: 1 +x33: 1 +x34: 1 +x35: 0 +x36: 1 +x37: 0 +x38: 1 +x39: 1 +x40: 0 +x41: 0 +x42: 0 +x43: 0 +x44: 1 +y00: 1 +y01: 1 +y02: 1 +y03: 1 +y04: 1 +y05: 0 +y06: 0 +y07: 0 +y08: 0 +y09: 0 +y10: 1 +y11: 0 +y12: 0 +y13: 0 +y14: 1 +y15: 1 +y16: 0 +y17: 1 +y18: 0 +y19: 1 +y20: 0 +y21: 0 +y22: 0 +y23: 0 +y24: 1 +y25: 1 +y26: 0 +y27: 1 +y28: 0 +y29: 1 +y30: 1 +y31: 1 +y32: 1 +y33: 0 +y34: 1 +y35: 1 +y36: 1 +y37: 1 +y38: 0 +y39: 1 +y40: 0 +y41: 0 +y42: 1 +y43: 0 +y44: 1 + +sjd XOR mcr -> mvb +phj OR mhq -> kdf +bbb OR rrh -> qhk +x30 AND y30 -> gjm +pbd XOR vvt -> z36 +pqv XOR nws -> z19 +bdd OR jjf -> fmk +x18 AND y18 -> z18 +y13 XOR x13 -> nfq +rtb AND tnr -> qtg +scd XOR mgv -> z28 +bwk OR tdq -> wfr +y33 XOR x33 -> hhg +y04 XOR x04 -> tcf +y22 AND x22 -> hmb +rqd XOR dpg -> z30 +x41 XOR y41 -> qhh +x37 XOR y37 -> hrn +mfk XOR fmm -> wss +x26 XOR y26 -> pjf +rkf AND mgk -> kmj +pvk XOR kdf -> z10 +scs AND rds -> dcv +x17 XOR y17 -> vhf +pbd AND vvt -> jrj +hmt OR pdq -> scs +x08 XOR y08 -> mcr +y13 AND x13 -> pdq +bvv XOR tkf -> z31 +wwr OR jvw -> fmj +jvg AND fgc -> kpt +gtd XOR qpn -> z20 +tsg XOR cnb -> z15 +cnb AND tsg -> rrh +y16 XOR x16 -> cvn +x23 AND y23 -> fwj +hqb OR nwd -> sgh +x40 AND y40 -> fpf +cvn XOR qhk -> z16 +bvv AND tkf -> dkm +gcv OR bvj -> mvj +x35 XOR y35 -> fpq +pjf AND vhm -> npv +x35 AND y35 -> prf +y26 AND x26 -> cdf +nfq AND fmk -> hmt +y20 XOR x20 -> gtd +wwn OR pwv -> wch +y19 XOR x19 -> pqv +x38 AND y38 -> wwn +mvk XOR shr -> z32 +mgv AND scd -> tdq +fwj OR vsq -> z23 +y16 AND x16 -> fgn +y12 AND x12 -> bdd +y27 AND x27 -> grd +sfr AND scr -> cws +jrj OR qtk -> jrs +shw OR wss -> nws +y18 XOR x18 -> fmm +y24 AND x24 -> fmp +rbp AND snr -> jjf +y12 XOR x12 -> rbp +bmn AND wqk -> jwd +qgd AND hjm -> tgn +x03 AND y03 -> nmn +hjm XOR qgd -> z06 +y21 AND x21 -> rjm +y19 AND x19 -> rmp +y11 AND x11 -> rpw +y33 AND x33 -> dpb +mvf AND jbg -> snp +y27 XOR x27 -> mvf +mvj XOR tsw -> z43 +x34 XOR y34 -> ggn +smq XOR ggn -> z34 +x14 AND y14 -> rds +cws OR fpf -> rmb +y36 AND x36 -> qtk +y42 XOR x42 -> ssh +cdv OR prf -> vvt +x24 XOR y24 -> wqk +ggn AND smq -> wng +msq AND fmj -> vvf +fmp OR jwd -> jvg +scs XOR rds -> z14 +y17 AND x17 -> vmq +nfw OR kmj -> rwp +dkm OR hgm -> shr +x31 AND y31 -> hgm +tht OR mtg -> rqd +x02 AND y02 -> wwr +qfj AND rcg -> bck +vrk OR nhn -> z45 +x28 AND y28 -> bwk +pvc XOR sgh -> z44 +x29 XOR y29 -> gqk +qmd XOR bpr -> bmn +x36 XOR y36 -> pbd +y08 AND x08 -> wdc +y41 AND x41 -> mgc +prv XOR fpq -> z35 +cjw OR mcv -> qfj +msq XOR fmj -> z03 +mqw OR rpw -> snr +kqp OR hrw -> mqb +y43 XOR x43 -> tsw +wch XOR brj -> z39 +y40 XOR x40 -> scr +y02 XOR x02 -> kfr +y31 XOR x31 -> tkf +x14 XOR y14 -> jss +jrp OR wrk -> rcq +rmp OR npb -> qpn +x30 XOR y30 -> dpg +y15 XOR x15 -> cnb +snp OR grd -> mgv +x07 AND y07 -> qtw +fmk XOR nfq -> z13 +x22 XOR y22 -> qns +ngc AND rcq -> mqw +bmn XOR wqk -> z24 +x28 XOR y28 -> scd +y00 AND x00 -> mgk +fgn OR cpm -> qfp +qtg OR qtw -> sjd +bpr AND qmd -> vsq +x06 AND y06 -> shf +pqv AND nws -> npb +pjf XOR vhm -> z26 +y09 XOR x09 -> rpg +wfr AND gqk -> mtg +rpg XOR ggm -> z09 +x25 XOR y25 -> fgc +qns XOR qtq -> z22 +x42 AND y42 -> gcv +shr AND mvk -> hrw +jrs XOR hrn -> z37 +ssh AND vcr -> bvj +mvb OR wdc -> ggm +qtq AND qns -> ksj +mgc OR fgh -> vcr +qfj XOR rcg -> z05 +mcr AND sjd -> z08 +y32 AND x32 -> kqp +mqb AND hhg -> bwc +tsw AND mvj -> nwd +x01 AND y01 -> nfw +gkw XOR mhv -> z21 +vvf OR nmn -> ncp +y05 XOR x05 -> rcg +mqb XOR hhg -> z33 +qhk AND cvn -> cpm +kdv OR kdk -> chf +scr XOR sfr -> z40 +djs XOR chf -> z38 +ncp AND tcf -> cjw +pvk AND kdf -> jrp +y10 XOR x10 -> pvk +cdf OR npv -> jbg +rkf XOR mgk -> z01 +y44 XOR x44 -> pvc +rtb XOR tnr -> z07 +ksj OR hmb -> bpr +hrn AND jrs -> kdv +x07 XOR y07 -> rtb +qhh AND rmb -> fgh +y38 XOR x38 -> djs +y34 AND x34 -> qmr +x01 XOR y01 -> rkf +djs AND chf -> pwv +jvg XOR fgc -> z25 +kfr AND rwp -> jvw +rwp XOR kfr -> z02 +qpn AND gtd -> qgt +sgh AND pvc -> vrk +y11 XOR x11 -> ngc +kpt OR tqk -> vhm +wng OR qmr -> prv +shf OR tgn -> tnr +x23 XOR y23 -> qmd +y03 XOR x03 -> msq +bck OR qtn -> hjm +gkw AND mhv -> dvk +y37 AND x37 -> kdk +y43 AND x43 -> hqb +y09 AND x09 -> phj +rmb XOR qhh -> z41 +x44 AND y44 -> nhn +phf OR qgt -> gkw +vmq OR jvq -> mfk +ggm AND rpg -> mhq +y05 AND x05 -> qtn +bwc OR dpb -> smq +y20 AND x20 -> phf +rqd AND dpg -> pps +wch AND brj -> mwm +x04 AND y04 -> mcv +mfk AND fmm -> shw +ssh XOR vcr -> z42 +dcv OR jss -> tsg +pps OR gjm -> bvv +gqk XOR wfr -> z29 +snr XOR rbp -> z12 +x29 AND y29 -> tht +y10 AND x10 -> wrk +x00 XOR y00 -> z00 +qfp XOR vhf -> z17 +tcf XOR ncp -> z04 +vhf AND qfp -> jvq +dvk OR rjm -> qtq +mvf XOR jbg -> z27 +ngc XOR rcq -> z11 +x06 XOR y06 -> qgd +fpq AND prv -> cdv +x15 AND y15 -> bbb +x25 AND y25 -> tqk +x32 XOR y32 -> mvk +y39 AND x39 -> dpd +y39 XOR x39 -> brj +x21 XOR y21 -> mhv +dpd OR mwm -> sfr diff --git a/2024/d24/ex2/ex2.py b/2024/d24/ex2/ex2.py new file mode 100755 index 0000000..0d54e57 --- /dev/null +++ b/2024/d24/ex2/ex2.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python + +import enum +import sys +from collections import defaultdict +from typing import NamedTuple + + +class Op(enum.StrEnum): + AND = "AND" + OR = "OR" + XOR = "XOR" + + def apply(self, lhs: bool, rhs: bool) -> bool: + match self: + case Op.AND: + return lhs & rhs + case Op.OR: + return lhs | rhs + case Op.XOR: + return lhs ^ rhs + + +class Gate(NamedTuple): + lhs: str + op: Op + rhs: str + + +def solve(input: str) -> str: + def parse_values(input: list[str]) -> dict[str, bool]: + return { + name: bool(int(val)) for name, val in map(lambda s: s.split(": "), input) + } + + def parse_operation(input: str) -> tuple[str, Gate]: + lhs, op, rhs, _, name = input.split() + return name, Gate(lhs, Op(op), rhs) + + def parse_circuit(input: list[str]) -> dict[str, Gate]: + return {name: gate for name, gate in map(parse_operation, input)} + + def parse(input: str) -> tuple[dict[str, bool], dict[str, Gate]]: + values, circuit = input.split("\n\n") + return parse_values(values.splitlines()), parse_circuit(circuit.splitlines()) + + def downstream_ops(circuit: dict[str, Gate]) -> dict[str, set[Op]]: + res: dict[str, set[Op]] = defaultdict(set) + for gate in circuit.values(): + res[gate.lhs].add(gate.op) + res[gate.rhs].add(gate.op) + return res + + def match_adders(circuit: dict[str, Gate]) -> set[str]: + def validate_and(wire: str, wire_ops: dict[str, set[Op]]) -> bool: + gate = circuit[wire] + assert gate.op == Op.AND # Sanity check + + # AND must lead into an OR carry, unless it reads the first bit + return wire_ops[wire] == {Op.OR} or {gate.lhs, gate.rhs} == {"x00", "y00"} + + def validate_or(wire: str, wire_ops: dict[str, set[Op]]) -> bool: + gate = circuit[wire] + assert gate.op == Op.OR # Sanity check + + # OR outputs the last bit as a direct carry, or into an AND and XOR + return wire == "z45" or wire_ops[wire] == {Op.AND, Op.XOR} + + def validate_xor(wire: str, wire_ops: dict[str, set[Op]]) -> bool: + gate = circuit[wire] + assert gate.op == Op.XOR # Sanity check + + inputs = {gate.lhs, gate.rhs} + has_input = all(any(i.startswith(w) for i in inputs) for w in ("x", "y")) + + # If lowest bit, XOR has no carry and outputs directly + if inputs == {"x00", "y00"} and wire == "z00": + return True + # Otherwise, if it read input bits, it outputs to a carry XOR + if has_input and Op.XOR in wire_ops[wire]: + return True + # If it doesn't read input bits, it must output to Z + if not has_input and wire.startswith("z"): + return True + return False + + def validate(wire: str, wire_ops: dict[str, set[Op]]) -> bool: + return { + Op.AND: validate_and, + Op.OR: validate_or, + Op.XOR: validate_xor, + }[circuit[wire].op](wire, wire_ops) + + wire_ops = downstream_ops(circuit) + return {wire for wire in circuit if not validate(wire, wire_ops)} + + _, circuit = parse(input) + return ",".join(sorted(match_adders(circuit))) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main() diff --git a/2024/d24/ex2/input b/2024/d24/ex2/input new file mode 100644 index 0000000..b45d7bc --- /dev/null +++ b/2024/d24/ex2/input @@ -0,0 +1,313 @@ +x00: 1 +x01: 1 +x02: 1 +x03: 1 +x04: 0 +x05: 1 +x06: 0 +x07: 1 +x08: 0 +x09: 1 +x10: 1 +x11: 1 +x12: 1 +x13: 0 +x14: 1 +x15: 0 +x16: 0 +x17: 1 +x18: 0 +x19: 1 +x20: 0 +x21: 0 +x22: 0 +x23: 1 +x24: 0 +x25: 0 +x26: 1 +x27: 1 +x28: 0 +x29: 0 +x30: 1 +x31: 1 +x32: 1 +x33: 1 +x34: 1 +x35: 0 +x36: 1 +x37: 0 +x38: 1 +x39: 1 +x40: 0 +x41: 0 +x42: 0 +x43: 0 +x44: 1 +y00: 1 +y01: 1 +y02: 1 +y03: 1 +y04: 1 +y05: 0 +y06: 0 +y07: 0 +y08: 0 +y09: 0 +y10: 1 +y11: 0 +y12: 0 +y13: 0 +y14: 1 +y15: 1 +y16: 0 +y17: 1 +y18: 0 +y19: 1 +y20: 0 +y21: 0 +y22: 0 +y23: 0 +y24: 1 +y25: 1 +y26: 0 +y27: 1 +y28: 0 +y29: 1 +y30: 1 +y31: 1 +y32: 1 +y33: 0 +y34: 1 +y35: 1 +y36: 1 +y37: 1 +y38: 0 +y39: 1 +y40: 0 +y41: 0 +y42: 1 +y43: 0 +y44: 1 + +sjd XOR mcr -> mvb +phj OR mhq -> kdf +bbb OR rrh -> qhk +x30 AND y30 -> gjm +pbd XOR vvt -> z36 +pqv XOR nws -> z19 +bdd OR jjf -> fmk +x18 AND y18 -> z18 +y13 XOR x13 -> nfq +rtb AND tnr -> qtg +scd XOR mgv -> z28 +bwk OR tdq -> wfr +y33 XOR x33 -> hhg +y04 XOR x04 -> tcf +y22 AND x22 -> hmb +rqd XOR dpg -> z30 +x41 XOR y41 -> qhh +x37 XOR y37 -> hrn +mfk XOR fmm -> wss +x26 XOR y26 -> pjf +rkf AND mgk -> kmj +pvk XOR kdf -> z10 +scs AND rds -> dcv +x17 XOR y17 -> vhf +pbd AND vvt -> jrj +hmt OR pdq -> scs +x08 XOR y08 -> mcr +y13 AND x13 -> pdq +bvv XOR tkf -> z31 +wwr OR jvw -> fmj +jvg AND fgc -> kpt +gtd XOR qpn -> z20 +tsg XOR cnb -> z15 +cnb AND tsg -> rrh +y16 XOR x16 -> cvn +x23 AND y23 -> fwj +hqb OR nwd -> sgh +x40 AND y40 -> fpf +cvn XOR qhk -> z16 +bvv AND tkf -> dkm +gcv OR bvj -> mvj +x35 XOR y35 -> fpq +pjf AND vhm -> npv +x35 AND y35 -> prf +y26 AND x26 -> cdf +nfq AND fmk -> hmt +y20 XOR x20 -> gtd +wwn OR pwv -> wch +y19 XOR x19 -> pqv +x38 AND y38 -> wwn +mvk XOR shr -> z32 +mgv AND scd -> tdq +fwj OR vsq -> z23 +y16 AND x16 -> fgn +y12 AND x12 -> bdd +y27 AND x27 -> grd +sfr AND scr -> cws +jrj OR qtk -> jrs +shw OR wss -> nws +y18 XOR x18 -> fmm +y24 AND x24 -> fmp +rbp AND snr -> jjf +y12 XOR x12 -> rbp +bmn AND wqk -> jwd +qgd AND hjm -> tgn +x03 AND y03 -> nmn +hjm XOR qgd -> z06 +y21 AND x21 -> rjm +y19 AND x19 -> rmp +y11 AND x11 -> rpw +y33 AND x33 -> dpb +mvf AND jbg -> snp +y27 XOR x27 -> mvf +mvj XOR tsw -> z43 +x34 XOR y34 -> ggn +smq XOR ggn -> z34 +x14 AND y14 -> rds +cws OR fpf -> rmb +y36 AND x36 -> qtk +y42 XOR x42 -> ssh +cdv OR prf -> vvt +x24 XOR y24 -> wqk +ggn AND smq -> wng +msq AND fmj -> vvf +fmp OR jwd -> jvg +scs XOR rds -> z14 +y17 AND x17 -> vmq +nfw OR kmj -> rwp +dkm OR hgm -> shr +x31 AND y31 -> hgm +tht OR mtg -> rqd +x02 AND y02 -> wwr +qfj AND rcg -> bck +vrk OR nhn -> z45 +x28 AND y28 -> bwk +pvc XOR sgh -> z44 +x29 XOR y29 -> gqk +qmd XOR bpr -> bmn +x36 XOR y36 -> pbd +y08 AND x08 -> wdc +y41 AND x41 -> mgc +prv XOR fpq -> z35 +cjw OR mcv -> qfj +msq XOR fmj -> z03 +mqw OR rpw -> snr +kqp OR hrw -> mqb +y43 XOR x43 -> tsw +wch XOR brj -> z39 +y40 XOR x40 -> scr +y02 XOR x02 -> kfr +y31 XOR x31 -> tkf +x14 XOR y14 -> jss +jrp OR wrk -> rcq +rmp OR npb -> qpn +x30 XOR y30 -> dpg +y15 XOR x15 -> cnb +snp OR grd -> mgv +x07 AND y07 -> qtw +fmk XOR nfq -> z13 +x22 XOR y22 -> qns +ngc AND rcq -> mqw +bmn XOR wqk -> z24 +x28 XOR y28 -> scd +y00 AND x00 -> mgk +fgn OR cpm -> qfp +qtg OR qtw -> sjd +bpr AND qmd -> vsq +x06 AND y06 -> shf +pqv AND nws -> npb +pjf XOR vhm -> z26 +y09 XOR x09 -> rpg +wfr AND gqk -> mtg +rpg XOR ggm -> z09 +x25 XOR y25 -> fgc +qns XOR qtq -> z22 +x42 AND y42 -> gcv +shr AND mvk -> hrw +jrs XOR hrn -> z37 +ssh AND vcr -> bvj +mvb OR wdc -> ggm +qtq AND qns -> ksj +mgc OR fgh -> vcr +qfj XOR rcg -> z05 +mcr AND sjd -> z08 +y32 AND x32 -> kqp +mqb AND hhg -> bwc +tsw AND mvj -> nwd +x01 AND y01 -> nfw +gkw XOR mhv -> z21 +vvf OR nmn -> ncp +y05 XOR x05 -> rcg +mqb XOR hhg -> z33 +qhk AND cvn -> cpm +kdv OR kdk -> chf +scr XOR sfr -> z40 +djs XOR chf -> z38 +ncp AND tcf -> cjw +pvk AND kdf -> jrp +y10 XOR x10 -> pvk +cdf OR npv -> jbg +rkf XOR mgk -> z01 +y44 XOR x44 -> pvc +rtb XOR tnr -> z07 +ksj OR hmb -> bpr +hrn AND jrs -> kdv +x07 XOR y07 -> rtb +qhh AND rmb -> fgh +y38 XOR x38 -> djs +y34 AND x34 -> qmr +x01 XOR y01 -> rkf +djs AND chf -> pwv +jvg XOR fgc -> z25 +kfr AND rwp -> jvw +rwp XOR kfr -> z02 +qpn AND gtd -> qgt +sgh AND pvc -> vrk +y11 XOR x11 -> ngc +kpt OR tqk -> vhm +wng OR qmr -> prv +shf OR tgn -> tnr +x23 XOR y23 -> qmd +y03 XOR x03 -> msq +bck OR qtn -> hjm +gkw AND mhv -> dvk +y37 AND x37 -> kdk +y43 AND x43 -> hqb +y09 AND x09 -> phj +rmb XOR qhh -> z41 +x44 AND y44 -> nhn +phf OR qgt -> gkw +vmq OR jvq -> mfk +ggm AND rpg -> mhq +y05 AND x05 -> qtn +bwc OR dpb -> smq +y20 AND x20 -> phf +rqd AND dpg -> pps +wch AND brj -> mwm +x04 AND y04 -> mcv +mfk AND fmm -> shw +ssh XOR vcr -> z42 +dcv OR jss -> tsg +pps OR gjm -> bvv +gqk XOR wfr -> z29 +snr XOR rbp -> z12 +x29 AND y29 -> tht +y10 AND x10 -> wrk +x00 XOR y00 -> z00 +qfp XOR vhf -> z17 +tcf XOR ncp -> z04 +vhf AND qfp -> jvq +dvk OR rjm -> qtq +mvf XOR jbg -> z27 +ngc XOR rcq -> z11 +x06 XOR y06 -> qgd +fpq AND prv -> cdv +x15 AND y15 -> bbb +x25 AND y25 -> tqk +x32 XOR y32 -> mvk +y39 AND x39 -> dpd +y39 XOR x39 -> brj +x21 XOR y21 -> mhv +dpd OR mwm -> sfr