diff --git a/challenges/2022/13-distressSignal/py/__init__.py b/challenges/2022/13-distressSignal/py/__init__.py index c92c103..d722f99 100644 --- a/challenges/2022/13-distressSignal/py/__init__.py +++ b/challenges/2022/13-distressSignal/py/__init__.py @@ -4,18 +4,21 @@ import json from functools import cmp_to_key -_Pair = Union[int, List['_Pair']] +_Pair = Union[int, List["_Pair"]] Pair = Tuple[_Pair, _Pair] + def parse(instr: str) -> List[Pair]: res = [] for raw_pair in instr.strip().split("\n\n"): a, b = raw_pair.splitlines() - res.append(( - json.loads(a), - json.loads(b), - )) + res.append( + ( + json.loads(a), + json.loads(b), + ) + ) return res @@ -26,7 +29,7 @@ def is_pair_well_ordered(pair: Pair) -> Optional[bool]: if type_a == int and type_b == list: return is_pair_well_ordered(([a], b)) - + elif type_a == list and type_b == int: return is_pair_well_ordered((a, [b])) @@ -34,9 +37,9 @@ def is_pair_well_ordered(pair: Pair) -> Optional[bool]: if a == b: return None return a < b - + elif type_a == list and type_b == list: - + for x in zip(a, b): y = is_pair_well_ordered(x) if y is not None: @@ -47,7 +50,7 @@ def is_pair_well_ordered(pair: Pair) -> Optional[bool]: if la == lb: return None return la < lb - + raise ValueError(f"impossible combiation of types ({type_a} and {type_b})") @@ -60,7 +63,6 @@ def is_well_ordered(a, b: Any) -> int: class Challenge(BaseChallenge): - @staticmethod def one(instr: str) -> int: inp = parse(instr) @@ -79,4 +81,4 @@ class Challenge(BaseChallenge): inp.sort(key=is_well_ordered, reverse=True) - return (inp.index([[2]]) + 1) * (inp.index([[6]]) + 1) \ No newline at end of file + return (inp.index([[2]]) + 1) * (inp.index([[6]]) + 1) diff --git a/challenges/2022/21-monkeyMath/README.md b/challenges/2022/21-monkeyMath/README.md new file mode 100644 index 0000000..0bec300 --- /dev/null +++ b/challenges/2022/21-monkeyMath/README.md @@ -0,0 +1 @@ +# [Day 21: Monkey Math](https://adventofcode.com/2022/day/21) diff --git a/challenges/2022/21-monkeyMath/benchmark.json b/challenges/2022/21-monkeyMath/benchmark.json new file mode 100644 index 0000000..c26e5af --- /dev/null +++ b/challenges/2022/21-monkeyMath/benchmark.json @@ -0,0 +1,15 @@ +{ + "day": 21, + "dir": "challenges/2022/21-monkeyMath", + "implementations": { + "Python": { + "part.1.avg": 0.004400461912155151, + "part.1.max": 0.018651485443115234, + "part.1.min": 0.003258943557739258, + "part.2.avg": 0.007683101415634155, + "part.2.max": 0.03519558906555176, + "part.2.min": 0.005395650863647461 + } + }, + "numRuns": 1000 +} \ No newline at end of file diff --git a/challenges/2022/21-monkeyMath/info.json b/challenges/2022/21-monkeyMath/info.json new file mode 100644 index 0000000..1cf5a79 --- /dev/null +++ b/challenges/2022/21-monkeyMath/info.json @@ -0,0 +1,17 @@ +{ + "inputFile": "input.txt", + "testCases": { + "one": [ + { + "input": "root: pppw + sjmn\ndbpl: 5\ncczh: sllz + lgvd\nzczc: 2\nptdq: humn - dvpt\ndvpt: 3\nlfqf: 4\nhumn: 5\nljgn: 2\nsjmn: drzm * dbpl\nsllz: 4\npppw: cczh / lfqf\nlgvd: ljgn * ptdq\ndrzm: hmdt - zczc\nhmdt: 32\n", + "expected": "152" + } + ], + "two": [ + { + "input": "root: pppw + sjmn\ndbpl: 5\ncczh: sllz + lgvd\nzczc: 2\nptdq: humn - dvpt\ndvpt: 3\nlfqf: 4\nhumn: 5\nljgn: 2\nsjmn: drzm * dbpl\nsllz: 4\npppw: cczh / lfqf\nlgvd: ljgn * ptdq\ndrzm: hmdt - zczc\nhmdt: 32\n", + "expected": "301" + } + ] + } +} \ No newline at end of file diff --git a/challenges/2022/21-monkeyMath/py/__init__.py b/challenges/2022/21-monkeyMath/py/__init__.py new file mode 100644 index 0000000..bf72fde --- /dev/null +++ b/challenges/2022/21-monkeyMath/py/__init__.py @@ -0,0 +1,146 @@ +from __future__ import annotations +from typing import * +from aocpy import BaseChallenge +from enum import Enum +import re + + +class Operator(Enum): + ADD = "+" + SUB = "-" + DIV = "/" + MULT = "*" + + def inverse(self) -> Operator: + if self.value == self.ADD.value: + return self.SUB + elif self.value == self.SUB.value: + return self.ADD + elif self.value == self.MULT.value: + return self.DIV + else: + return self.MULT + + def calc(self, a_val, b_val) -> float: + res = 0 + if self.value == self.ADD.value: + res = a_val + b_val + elif self.value == self.SUB.value: + res = a_val - b_val + elif self.value == self.MULT.value: + res = a_val * b_val + elif self.value == self.DIV.value: + res = a_val / b_val + + return res + + +Monkey = Union[int, Tuple[str, Operator, str]] +Monkies = Dict[str, Monkey] + + +number_re = re.compile(r"([a-z]{4}): (\d+)") +operation_re = re.compile(r"([a-z]{4}): ([a-z]{4}) ([+\-/*]) ([a-z]{4})") + + +def parse(instr: str) -> Monkies: + res: Dict[str, Monkey] = {} + for line in instr.strip().splitlines(): + if x := number_re.match(line): + name, num_str = x.groups() + num = int(num_str) + res[name] = num + elif x := operation_re.match(line): + name, a, op_str, b = x.groups() + res[name] = (a, Operator(op_str), b) + return res + + +def evaluate_monkey(monkey: Monkey, monkies: Monkies) -> float: + if type(monkey) == int: + return monkey + + elif type(monkey) == tuple: + (a_name, op, b_name) = monkey + a_val = evaluate_monkey(monkies[a_name], monkies) + b_val = evaluate_monkey(monkies[b_name], monkies) + return op.calc(a_val, b_val) + + raise ValueError(f"impossible type {type(monkey)}") + + +def does_branch_contain(target: str, monkey: Monkey, monkies: Monkies) -> bool: + if type(monkey) == int: + return False + + elif type(monkey) == tuple: + (a_name, _, b_name) = monkey + if a_name == target or b_name == target: + return True + + if does_branch_contain(target, monkies[a_name], monkies): + return True + return does_branch_contain(target, monkies[b_name], monkies) + + raise ValueError(f"impossible type {type(monkey)}") + + +class Challenge(BaseChallenge): + @staticmethod + def one(instr: str) -> int: + inp = parse(instr) + return int(evaluate_monkey(inp["root"], inp)) + + @staticmethod + def two(instr: str) -> int: + inp = parse(instr) + + root = inp["root"] + assert type(root) == tuple + + (a_name, _, b_name) = root + + if does_branch_contain("humn", inp[a_name], inp): + branch_with_human = inp[a_name] + target_value = evaluate_monkey(inp[b_name], inp) + elif does_branch_contain("humn", inp[b_name], inp): + branch_with_human = inp[b_name] + target_value = evaluate_monkey(inp[a_name], inp) + else: + raise ValueError("neither root branch contains humn") + + acc = target_value + cursor = branch_with_human + while True: + (a_name, op, b_name) = cursor + + if a_name == "humn" or does_branch_contain("humn", inp[a_name], inp): + x = evaluate_monkey(inp[b_name], inp) + is_first_arg = False + cursor = inp[a_name] + elif b_name == "humn" or does_branch_contain("humn", inp[b_name], inp): + x = evaluate_monkey(inp[a_name], inp) + is_first_arg = True + cursor = inp[b_name] + else: + raise ValueError("neither branch contains humn") + + if op == Operator.ADD or op == Operator.MULT: + acc = op.inverse().calc(acc, x) + elif op == Operator.SUB: + if is_first_arg: + acc = op.calc(-acc, -x) + else: + acc = op.inverse().calc(acc, x) + elif op == Operator.DIV: + if is_first_arg: + acc = op.calc(1 / acc, 1 / x) + else: + acc = op.inverse().calc(acc, x) + else: + raise ValueError(f"invalid operator {op}") + + if a_name == "humn" or b_name == "humn": + break + + return int(acc) diff --git a/challenges/2022/README.md b/challenges/2022/README.md index 051a500..8bcdc2e 100644 --- a/challenges/2022/README.md +++ b/challenges/2022/README.md @@ -35,4 +35,5 @@ The red dotted line denotes 15 seconds. | 17 - Pyroclastic Flow | ★ ★ | [Python](17-pyroclasticFlow/py/__init__.py) | Detecting cycles in a large amount of knock-off Tetris. | | 18 - Boiling Boulders | ★ ★ | [Python](18-boilingBoulders/py/__init__.py) | Finding the surface area of a shape specified by a list of unit cubes. | | 19 - Not Enough Minerals | ★ ★ | [Python](19-notEnoughMinerals/py/__init__.py) | Finding the most effective sequence of operations to complete a specific task. | -| 20 - Grove Positioning System | ★ ★ | [Python](20-grovePositioningSystem/py/__init__.py) | My hell is lined with circular sequences. | \ No newline at end of file +| 20 - Grove Positioning System | ★ ★ | [Python](20-grovePositioningSystem/py/__init__.py) | My hell is lined with circular sequences. | +| 21 - Monkey Math | ★ ★ | [Python](21-monkeyMath/py/__init__.py) | Trees of math with a fairly satisfying solution :D | \ No newline at end of file diff --git a/challenges/2022/running-times.png b/challenges/2022/running-times.png index ac2cbc7..eaf455d 100644 Binary files a/challenges/2022/running-times.png and b/challenges/2022/running-times.png differ