2022-21
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
5f4ec35ccf
commit
4d3b3fd231
7 changed files with 194 additions and 12 deletions
|
@ -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)
|
||||
return (inp.index([[2]]) + 1) * (inp.index([[6]]) + 1)
|
||||
|
|
1
challenges/2022/21-monkeyMath/README.md
Normal file
1
challenges/2022/21-monkeyMath/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# [Day 21: Monkey Math](https://adventofcode.com/2022/day/21)
|
15
challenges/2022/21-monkeyMath/benchmark.json
Normal file
15
challenges/2022/21-monkeyMath/benchmark.json
Normal file
|
@ -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
|
||||
}
|
17
challenges/2022/21-monkeyMath/info.json
Normal file
17
challenges/2022/21-monkeyMath/info.json
Normal file
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
146
challenges/2022/21-monkeyMath/py/__init__.py
Normal file
146
challenges/2022/21-monkeyMath/py/__init__.py
Normal file
|
@ -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)
|
|
@ -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. |
|
||||
| 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 |
|
Binary file not shown.
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 71 KiB |
Loading…
Add table
Add a link
Reference in a new issue