day11 finished

This commit is contained in:
Ruediger Ludwig 2022-12-11 14:51:33 +01:00
parent dd3bba8a23
commit dab59aafa1
9 changed files with 411 additions and 20 deletions

View file

View file

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 97, 81, 57, 57, 91, 61
Operation: new = old * 7
Test: divisible by 11
If true: throw to monkey 5
If false: throw to monkey 6
Monkey 1:
Starting items: 88, 62, 68, 90
Operation: new = old * 17
Test: divisible by 19
If true: throw to monkey 4
If false: throw to monkey 2
Monkey 2:
Starting items: 74, 87
Operation: new = old + 2
Test: divisible by 5
If true: throw to monkey 7
If false: throw to monkey 4
Monkey 3:
Starting items: 53, 81, 60, 87, 90, 99, 75
Operation: new = old + 1
Test: divisible by 2
If true: throw to monkey 2
If false: throw to monkey 1
Monkey 4:
Starting items: 57
Operation: new = old + 6
Test: divisible by 13
If true: throw to monkey 7
If false: throw to monkey 0
Monkey 5:
Starting items: 54, 84, 91, 55, 59, 72, 75, 70
Operation: new = old * old
Test: divisible by 7
If true: throw to monkey 6
If false: throw to monkey 3
Monkey 6:
Starting items: 95, 79, 79, 68, 78
Operation: new = old + 3
Test: divisible by 3
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 7:
Starting items: 61, 97, 67
Operation: new = old + 4
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 5

View file

@ -0,0 +1,27 @@
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1

View file

@ -0,0 +1,134 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from math import prod
from typing import Callable, Iterator, Self
from advent.parser.parser import P
day_num = 11
def part1(lines: Iterator[str]) -> int:
horde = Troop_While_Worried.parse(lines)
horde.rounds(20)
return horde.inspected_result()
def part2(lines: Iterator[str]) -> int:
horde = Troop_While_Kinda_Relieved.parse(lines)
horde.rounds(10_000)
return horde.inspected_result()
def worry_increaser(op: str, value: int | str) -> WorryIncreaser:
match (op, value):
case '*', 'old': return lambda old: old * old
case '*', int(v): return lambda old: old * v
case '+', int(v): return lambda old: old + v
case _: raise Exception(f"Illegal line: {op} {value}")
class Parser:
""" All the parsers needed for this solution """
worry_inc: P[WorryIncreaser] = P.second(
P.tstring("Operation: new = old"),
P.map2(P.one_of('+*'), P.either(P.tstring('old'), P.tsigned()),
worry_increaser)).tline()
monkey_number: P[int] = P.unsigned().between(P.tstring('Monkey'), P.tchar(':')).tline()
items: P[list[int]] = P.second(
P.tstring('Starting items:'), P.unsigned().sep_by(sep=P.tchar(','))).tline()
modulo: P[int] = P.second(
P.tstring("Test: divisible by"), P.unsigned()).tline()
throw_parser: P[int] = P.second(
P.seq(
P.tstring("If"),
P.either(P.tstring("true"), P.tstring("false")),
P.tstring(": throw to monkey")),
P.unsigned()).tline()
test: P[tuple[int, int, int]] = P.seq(
modulo, throw_parser, throw_parser)
monkey: P[Monkey] = P.map4(monkey_number, items,
worry_inc, test,
lambda number, items, worry_inc, test:
Monkey(number, items, worry_inc, *test))
monkey_list: P[list[Monkey]] = P.first(monkey, P.eol().optional()).many()
WorryIncreaser = Callable[[int], int]
@dataclass(slots=True)
class Monkey:
number: int
items: list[int]
worry_increaser: WorryIncreaser
modulator: int
target_if_divides: int
catcher_if_not_divides: int
inspected: int = field(default=0, compare=False)
def inspect_items(self, worry_decrease: int | None) -> Iterator[tuple[int, int]]:
for item in self.items:
self.inspected += 1
next_level = self.worry_increaser(item)
if worry_decrease is not None:
next_level //= worry_decrease
if next_level % self.modulator == 0:
target_monkey = self.target_if_divides
else:
target_monkey = self.catcher_if_not_divides
yield target_monkey, next_level
self.items.clear()
def catch_item(self, item: int):
self.items.append(item)
@dataclass(slots=True)
class Troop(ABC):
monkeys: list[Monkey]
@abstractmethod
def single_round(self):
...
def rounds(self, count: int):
for _ in range(count):
self.single_round()
def inspected_result(self):
most = sorted((monkey.inspected for monkey in self.monkeys), reverse=True)
return most[0] * most[1]
@dataclass(slots=True)
class Troop_While_Worried(Troop):
""" The """
@classmethod
def parse(cls, lines: Iterator[str]) -> Self:
monkeys = Parser.monkey_list.parse_iterator(lines).get()
return Troop_While_Worried(monkeys)
def single_round(self):
for currentMonkey in self.monkeys:
for target_monkey, item in currentMonkey.inspect_items(3):
self.monkeys[target_monkey].catch_item(item)
@dataclass(slots=True)
class Troop_While_Kinda_Relieved(Troop):
modulator: int
@classmethod
def parse(cls, lines: Iterator[str]) -> Self:
monkeys = Parser.monkey_list.parse_iterator(lines).get()
return Troop_While_Kinda_Relieved(monkeys, prod(monkey.modulator for monkey in monkeys))
def single_round(self):
for current_monkey in self.monkeys:
for target_monkey, item in current_monkey.inspect_items(None):
self.monkeys[target_monkey].catch_item(item % self.modulator)

View file

@ -0,0 +1,56 @@
from advent.common import input
from .solution import Troop_While_Kinda_Relieved, Troop_While_Worried, day_num, part1, part2
def test_part1():
lines = input.read_lines(day_num, 'test01.txt')
expected = 10605
result = part1(lines)
assert result == expected
def test_part2():
lines = input.read_lines(day_num, 'test01.txt')
expected = 2713310158
result = part2(lines)
assert result == expected
def test_parse_all():
lines = input.read_lines(day_num, 'test01.txt')
expected = 4
result = Troop_While_Worried.parse(lines)
assert len(result.monkeys) == expected
def test_one_round():
lines = input.read_lines(day_num, 'test01.txt')
expected = [2080, 25, 167, 207, 401, 1046]
result = Troop_While_Worried.parse(lines)
result.single_round()
assert list(result.monkeys[1].items) == expected
def test_rounds():
lines = input.read_lines(day_num, 'test01.txt')
expected = 101
result = Troop_While_Worried.parse(lines)
result.rounds(20)
assert result.monkeys[0].inspected == expected
def test_inspected():
lines = input.read_lines(day_num, 'test01.txt')
expected = 10605
result = Troop_While_Worried.parse(lines)
result.rounds(20)
assert result.inspected_result() == expected
def test_inspected2():
lines = input.read_lines(day_num, 'test01.txt')
expected = 2713310158
result = Troop_While_Kinda_Relieved.parse(lines)
result.rounds(10_000)
assert result.inspected_result() == expected