day21 finished
This commit is contained in:
parent
122f3a3730
commit
e7bf3149c8
6 changed files with 2933 additions and 0 deletions
|
|
@ -8,6 +8,8 @@ I use python 3.11 without any libraries beyond the standard.
|
||||||
|
|
||||||
| Day | Time | Rank | Score | Time | Rank | Score |
|
| Day | Time | Rank | Score | Time | Rank | Score |
|
||||||
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
| --- | --------- | ----- | ----- | -------- | ----- | ----- |
|
||||||
|
| 21 | 00:20:57 | 2178 | 0 | 01:10:59 | 2175 | 0 |
|
||||||
|
| 20 | 11:19:34 | 9421 | 0 | 11:24:45 | 8501 | 0 |
|
||||||
| 18 | 00:31:49 | 3269 | 0 | 01:51:15 | 3121 | 0 |
|
| 18 | 00:31:49 | 3269 | 0 | 01:51:15 | 3121 | 0 |
|
||||||
| 17 | 01:21:11 | 2058 | 0 | 02:42:45 | 1665 | 0 |
|
| 17 | 01:21:11 | 2058 | 0 | 02:42:45 | 1665 | 0 |
|
||||||
| 16 | 02:44:20 | 2611 | 0 | >24h | 10509 | 0 |
|
| 16 | 02:44:20 | 2611 | 0 | >24h | 10509 | 0 |
|
||||||
|
|
|
||||||
0
advent/days/day21/__init__.py
Normal file
0
advent/days/day21/__init__.py
Normal file
15
advent/days/day21/data/example01.txt
Normal file
15
advent/days/day21/data/example01.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
root: pppw + sjmn
|
||||||
|
dbpl: 5
|
||||||
|
cczh: sllz + lgvd
|
||||||
|
zczc: 2
|
||||||
|
ptdq: humn - dvpt
|
||||||
|
dvpt: 3
|
||||||
|
lfqf: 4
|
||||||
|
humn: 5
|
||||||
|
ljgn: 2
|
||||||
|
sjmn: drzm * dbpl
|
||||||
|
sllz: 4
|
||||||
|
pppw: cczh / lfqf
|
||||||
|
lgvd: ljgn * ptdq
|
||||||
|
drzm: hmdt - zczc
|
||||||
|
hmdt: 32
|
||||||
2755
advent/days/day21/data/input.txt
Normal file
2755
advent/days/day21/data/input.txt
Normal file
File diff suppressed because it is too large
Load diff
144
advent/days/day21/solution.py
Normal file
144
advent/days/day21/solution.py
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
day_num = 21
|
||||||
|
|
||||||
|
|
||||||
|
def part1(lines: Iterator[str]) -> int:
|
||||||
|
troop = Monkey.parse_troop(lines)
|
||||||
|
return troop["root"].get_value(troop)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(lines: Iterator[str]) -> int:
|
||||||
|
troop = Monkey.parse_troop(lines)
|
||||||
|
|
||||||
|
root = troop["root"]
|
||||||
|
new_root = Monkey("root", None, Operation.Sub, root.monkeys)
|
||||||
|
|
||||||
|
result = new_root.reach_value(0, troop, 'humn')
|
||||||
|
if result is None:
|
||||||
|
raise Exception("Can't find solution")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class Operation(Enum):
|
||||||
|
Add = 0
|
||||||
|
Sub = 1
|
||||||
|
Mul = 2
|
||||||
|
Div = 3
|
||||||
|
|
||||||
|
def calc_value(self, value1: int, value2: int) -> int:
|
||||||
|
match self:
|
||||||
|
case Operation.Add: return value1 + value2
|
||||||
|
case Operation.Sub: return value1 - value2
|
||||||
|
case Operation.Mul: return value1 * value2
|
||||||
|
case Operation.Div: return value1 // value2
|
||||||
|
|
||||||
|
def calc_value1(self, value: int, value2: int) -> int:
|
||||||
|
match self:
|
||||||
|
case Operation.Add: return value - value2
|
||||||
|
case Operation.Sub: return value + value2
|
||||||
|
case Operation.Mul: return value // value2
|
||||||
|
case Operation.Div: return value * value2
|
||||||
|
|
||||||
|
def calc_value2(self, value: int, value1: int) -> int:
|
||||||
|
match self:
|
||||||
|
case Operation.Add: return value - value1
|
||||||
|
case Operation.Sub: return value1 - value
|
||||||
|
case Operation.Mul: return value // value1
|
||||||
|
case Operation.Div: return value1 // value
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(slots=True, frozen=True)
|
||||||
|
class Monkey:
|
||||||
|
name: str
|
||||||
|
value: int | None
|
||||||
|
|
||||||
|
operation: Operation | None = field(default=None)
|
||||||
|
monkeys: tuple[str, str] | None = field(default=None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_troop(cls, lines: Iterator[str]) -> dict[str, Monkey]:
|
||||||
|
troop = {monkey.name: monkey for monkey in (Monkey.parse(line) for line in lines)}
|
||||||
|
return troop
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls, line: str) -> Monkey:
|
||||||
|
match line.split(': '):
|
||||||
|
case [name, formula]:
|
||||||
|
match formula.split():
|
||||||
|
case [value]:
|
||||||
|
return Monkey(name, int(value))
|
||||||
|
|
||||||
|
case [monkey1, '+', monkey2]:
|
||||||
|
return Monkey(name, None, Operation.Add, (monkey1, monkey2))
|
||||||
|
case [monkey1, '-', monkey2]:
|
||||||
|
return Monkey(name, None, Operation.Sub, (monkey1, monkey2))
|
||||||
|
case [monkey1, '*', monkey2]:
|
||||||
|
return Monkey(name, None, Operation.Mul, (monkey1, monkey2))
|
||||||
|
case [monkey1, '/', monkey2]:
|
||||||
|
return Monkey(name, None, Operation.Div, (monkey1, monkey2))
|
||||||
|
|
||||||
|
case _:
|
||||||
|
raise Exception(f"Unknown formula: {formula}")
|
||||||
|
case _:
|
||||||
|
raise Exception(f"Unknown line: {line}")
|
||||||
|
|
||||||
|
def get_value(self, troop: dict[str, Monkey]) -> int:
|
||||||
|
if self.value is not None:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
if self.monkeys is None or self.operation is None:
|
||||||
|
raise Exception("Illegal state")
|
||||||
|
|
||||||
|
value1 = troop[self.monkeys[0]].get_value(troop)
|
||||||
|
value2 = troop[self.monkeys[1]].get_value(troop)
|
||||||
|
|
||||||
|
return self.operation.calc_value(value1, value2)
|
||||||
|
|
||||||
|
def get_for_human(self, troop: dict[str, Monkey], human_name: str) -> int | None:
|
||||||
|
if self.name == human_name:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.value is not None:
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
if self.monkeys is None or self.operation is None:
|
||||||
|
raise Exception("Illegal state")
|
||||||
|
|
||||||
|
value1 = troop[self.monkeys[0]] .get_for_human(troop, human_name)
|
||||||
|
value2 = troop[self.monkeys[1]] .get_for_human(troop, human_name)
|
||||||
|
|
||||||
|
if value1 is None or value2 is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.operation.calc_value(value1, value2)
|
||||||
|
|
||||||
|
def reach_value(self, value: int, troop: dict[str, Monkey], human_name: str) -> int:
|
||||||
|
if self.name == human_name:
|
||||||
|
return value
|
||||||
|
|
||||||
|
if self.monkeys is None or self.operation is None:
|
||||||
|
raise Exception("Illegal state")
|
||||||
|
|
||||||
|
monkey1 = troop[self.monkeys[0]]
|
||||||
|
monkey2 = troop[self.monkeys[1]]
|
||||||
|
|
||||||
|
value1 = monkey1.get_for_human(troop, human_name)
|
||||||
|
value2 = monkey2.get_for_human(troop, human_name)
|
||||||
|
|
||||||
|
assert value1 is None or value2 is None
|
||||||
|
|
||||||
|
if value1 is not None:
|
||||||
|
value2 = self.operation.calc_value2(value, value1)
|
||||||
|
return monkey2.reach_value(value2, troop, human_name)
|
||||||
|
|
||||||
|
elif value2 is not None:
|
||||||
|
value1 = self.operation.calc_value1(value, value2)
|
||||||
|
return monkey1.reach_value(value1, troop, human_name)
|
||||||
|
|
||||||
|
assert False, "Unreachable"
|
||||||
17
advent/days/day21/test_solution.py
Normal file
17
advent/days/day21/test_solution.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from advent.common import input
|
||||||
|
|
||||||
|
from .solution import day_num, part1, part2
|
||||||
|
|
||||||
|
|
||||||
|
def test_part1():
|
||||||
|
lines = input.read_lines(day_num, 'example01.txt')
|
||||||
|
expected = 152
|
||||||
|
result = part1(lines)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_part2():
|
||||||
|
lines = input.read_lines(day_num, 'example01.txt')
|
||||||
|
expected = 301
|
||||||
|
result = part2(lines)
|
||||||
|
assert result == expected
|
||||||
Loading…
Add table
Add a link
Reference in a new issue