advent-of-code/2022/11/python/main.py

106 lines
3.0 KiB
Python
Raw Permalink Normal View History

2022-12-11 14:45:20 +00:00
#!/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))