This commit is contained in:
cupcakearmy 2022-12-11 15:45:20 +01:00
parent fd631127fb
commit fff2267f21
2 changed files with 133 additions and 0 deletions

28
2022/11/README.md Normal file
View File

@ -0,0 +1,28 @@
# 11
## Part one
Easy, fun, lets go.
## Part two
Performance was the issue. I started moving functions to inline code (reduce readability for less function calls). It did not make a dent.
Lesson learned: In python functions calls and destructuring are not expensive.
What helped a bit was:
- Precalculating if it's a multiplication or addition for each monkey.
- And whether to use the old value or a custom one.
However that was not nearly enough of a speed boost to get me to the performance needed.
**Enter math**: We are always diving, so what if I could make the numbers smaller somehow? But everyone uses a different divider? Thats when it struck me! Before passing the item to the next monkey, reduce it with modulo by the biggest common denominator of all monkeys (since the divisions will not influence the other decisions of the other monkeys). And it worked! Speed was no issue anymore ⚡️
<details>
<summary>Solutions</summary>
<ol>
<li>58322</li>
<li>13937702909</li>
</ol>
</details>

105
2022/11/python/main.py Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
from functools import reduce
from os.path import dirname, join
from typing import Literal
# Day 11
# Common
def read_input(filename):
data = join(dirname(__file__), '..', filename)
with open(data) as f:
return f.read().strip()
test = read_input('test.txt')
data = read_input('input.txt')
Old = Literal['old']
Operator = Literal['+', '*']
Operation = tuple[Operator, Old | int]
class Monkey:
def __init__(self, items: list[int], operation: Operation, test: tuple[int, int, int], chill: bool) -> None:
self.items = items
self.chill = chill
self.inspected: int = 0
self.divisible = test[0]
self.true = test[1]
self.false = test[2]
op, arg = operation
self._old = arg == 'old'
self._add = op == '+'
self._arg = -1 if self._old else int(arg)
def round(self) -> list[tuple[int, int]]:
l: list[tuple[int, int]] = []
for value in self.items:
arg = value if self._old else self._arg
value = value + arg if self._add else value * arg
if self.chill:
value = value // 3
next_monkey = self.true if value % self.divisible == 0 else self.false
l.append((next_monkey, value))
self.inspected += len(self.items)
self.items.clear()
return l
def __repr__(self) -> str:
return f"{self.inspected}"
@staticmethod
def parse(data: str, chill: bool):
lines = data.splitlines()
items = list(map(int, lines[1][18:].split(', ')))
# Operator
op_raw, arg_raw = lines[2][22:].split()
op: Operator = op_raw
arg: Old | int = 'old' if arg_raw == 'old' else int(arg_raw)
operation: Operation = (op, arg)
# Test
divisible = int(lines[3][21:])
true = int(lines[4][29:])
false = int(lines[5][30:])
return Monkey(items, operation, (divisible, true, false), chill)
class Game:
def __init__(self, monkeys: list[Monkey]) -> None:
self.monkeys = monkeys
dividers = [m.divisible for m in monkeys]
self.common = reduce(lambda a, b: a*b, dividers, 1)
def __str__(self) -> str:
return ", ".join(map(str, self.monkeys))
def round(self):
for monkey in self.monkeys:
thrown = monkey.round()
for monkey, item in thrown:
self.monkeys[monkey].items.append(item % self.common)
def flag(self, rounds: int = 20):
for _ in range(rounds):
self.round()
inspected = [monkey.inspected for monkey in self.monkeys]
inspected = (sorted(inspected)[-2:])
return inspected[0] * inspected[1]
@staticmethod
def parse(data: str, chill: bool = True):
return Game([Monkey.parse(monkey, chill) for monkey in data.split('\n\n')])
# Running
game = Game.parse(data, chill=True)
print(game.flag(20))
game = Game.parse(data, chill=False)
print(game.flag(10_000))