2024: d24: ex2: add solution
This commit is contained in:
parent
2930ef2b0f
commit
1278ebe7f0
107
2024/d24/ex2/ex2.py
Executable file
107
2024/d24/ex2/ex2.py
Executable file
|
@ -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()
|
Loading…
Reference in a new issue