summaryrefslogtreecommitdiffstats
path: root/7/solution.py
diff options
context:
space:
mode:
Diffstat (limited to '7/solution.py')
-rw-r--r--7/solution.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/7/solution.py b/7/solution.py
new file mode 100644
index 0000000..77a1391
--- /dev/null
+++ b/7/solution.py
@@ -0,0 +1,115 @@
+from abc import abstractmethod
+
+class U16:
+ def __init__(self, value: int):
+ self.value = value & 0xffff
+ def __invert__(self):
+ return U16(~self.value)
+ def __and__(self, other):
+ return U16(self.value & other.value)
+ def __or__(self, other):
+ return U16(self.value | other.value)
+ def __rshift__(self, other):
+ return U16(self.value >> other.value)
+ def __lshift__(self, other):
+ return U16(self.value << other.value)
+ def __str__(self):
+ return str(self.value)
+
+class Node:
+ _value: U16 = None
+ @property
+ def value(self) -> U16:
+ if not self._value:
+ self._value = self.eval()
+ return self._value
+ def eval(self, net: dict[Node]) -> U16:
+ return self.eval()
+ @absrtactmethod
+ def eval(self) -> U16:
+ pass
+
+class Reference(Node):
+ def __init__(self, name: str):
+ self.name = name
+ def eval(self, net: dict[Node]) -> U16:
+ return net[self.name].value
+
+class Constant(Node):
+ def __init__(self, value):
+ self._value = value
+
+class BinaryOp(Node):
+ def __init__(self, left, right):
+ self.left = left
+ self.right = right
+
+class And(BinaryOp):
+ def eval(self) -> U16:
+ return self.left.value & self.right.value
+
+class Or(BinaryOp):
+ def eval(self) -> U16:
+ return self.left.value | self.right.value
+
+class LShift(BinaryOp):
+ def eval(self) -> U16:
+ return self.left.value << self.right.value
+
+class RShift(BinaryOp):
+ def eval(self) -> U16:
+ return self.left.value >> self.right.value
+
+class Not(Node):
+ def __init__(self, node):
+ self.node = node
+ def eval(self):
+ return ~self.node.value
+
+def parse_expr(net: dict[Node], expr: str) -> Node:
+ parts = expr.split(' ')
+ if len(parts) == 3:
+ lhs = parse_expr(net, parts[0])
+ rhs = parse_expr(net, parts[2])
+ if parts[1] == 'AND':
+ return And(lhs, rhs)
+ elif parts[1] == 'OR':
+ return Or(lhs, rhs)
+ elif parts[1] == 'LSHIFT':
+ return LShift(lhs, rhs)
+ elif parts[1] == 'RSHIFT':
+ return RShift(lhs, rhs)
+ elif len(parts) == 2:
+ if parts[0] == 'NOT':
+ return Not(parse_expr(net, parts[1]))
+ elif len(parts) == 1:
+ if expr.isnumeric():
+ return Constant(U16(int(expr)))
+ if expr.isalpha():
+ return Reference(net, expr)
+ raise ValueError(expr)
+
+def parse_conn(net: dict[Node], conn: str) -> tuple[str, Node]:
+ expr, target = conn.split(" -> ")
+ return target, parse_expr(net, expr)
+
+def part1(inp: list[str]) -> int:
+ net = dict()
+ for conn in inp:
+ target, node = parse_conn(net, conn)
+ net[target] = node
+ return net['a'].value
+
+def part2(inp: list[str]) -> int:
+ net = dict()
+ for conn in inp:
+ target, node = parse_conn(net, conn)
+ net[target] = node
+ net['b'] = Constant(part1(inp))
+ return net['a'].value
+
+if __name__ == '__main__':
+ with open('input') as f:
+ inp = [line.strip() for line in f]
+ print(part1(inp))
+ print(part2(inp)) \ No newline at end of file