# -*- coding: utf-8 -*- from abc import ABC from typing import Dict, Union, Iterator, Type, Callable, List from operator import add, sub, mul, truediv, eq, floordiv from aoc import BaseAssignment from aoc.decorators import list_input from aoc.mixins import AStarMixin class MonkeyActivity: def __init__(self, name: str, monkeys: Dict[str, "MonkeyActivity"]): self.name = name self.monkeys = monkeys self.monkeys[name] = self def __repr__(self): return f"{self.name}" @property def value(self) -> int: raise NotImplementedError() class Yell(MonkeyActivity): def __init__(self, value: str, *args, **kwargs): super().__init__(*args, **kwargs) self._value = int(value) @property def value(self) -> int: return self._value class Operate(MonkeyActivity): def __init__(self, a: str, b: str, operator: str, *args, **kwargs): super().__init__(*args, **kwargs) self.operator = operator self._a = a self._b = b self._operator = { "+": add, "-": sub, "*": mul, "/": truediv, }[operator] @property def a(self) -> MonkeyActivity: return self.monkeys[self._a] @property def b(self) -> MonkeyActivity: return self.monkeys[self._b] @property def value(self) -> int: return int(self._operator(self.a.value, self.b.value)) class Assignment(BaseAssignment[int, MonkeyActivity], ABC): monkeys = {} def parse_item(self, item: str) -> MonkeyActivity: name, activity = item.split(": ") activity = activity.split(" ") if len(activity) == 1: return Yell(*activity, name, self.monkeys) a, operator, b = activity return Operate(a, b, operator, name, self.monkeys) class AssignmentOne(Assignment): example_result = 152 @list_input def run(self, _: List[MonkeyActivity]) -> int: return self.monkeys["root"].value class AssignmentTwo(Assignment, AStarMixin[MonkeyActivity]): example_result = 301 @staticmethod def neighbours(node: MonkeyActivity): if isinstance(node, Operate): yield node.a yield node.b @list_input def run(self, _: List[MonkeyActivity]) -> int: root = self.monkeys["root"] humn = self.monkeys["humn"] humn._value = 0 path = self.a_star( root, lambda n, _: n is humn, self.neighbours, ) for item in path: if isinstance(item, Yell): break item: Operate = item reverse_operator = { "+": sub, "-": add, "*": floordiv, "/": mul, }[item.operator] humn._value = reverse_operator(item.b.value, item.a.value) pass return humn._value