84 lines
2.1 KiB
Python
84 lines
2.1 KiB
Python
|
#!/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()
|