Compare commits

...

4 commits

4 changed files with 301 additions and 0 deletions

93
2022/d11/ex1/ex1.py Executable file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python
import dataclasses
import sys
from collections import Counter, deque
from typing import Literal, Optional
@dataclasses.dataclass
class Operation:
lhs: Optional[int]
operator: Literal["+", "*"]
rhs: Optional[int]
def __call__(self, old: int) -> int:
lhs = old if self.lhs is None else self.lhs
rhs = old if self.rhs is None else self.rhs
if self.operator == "*":
return lhs * rhs
if self.operator == "+":
return lhs + rhs
assert False
@classmethod
def from_input(cls, input: str) -> "Operation":
assert input.startswith(" Operation: new = ")
lhs, op, rhs = input.split()[-3:]
assert op in ("+", "*") # Sanity check
return cls(
None if lhs == "old" else int(lhs),
op, # type: ignore
None if rhs == "old" else int(rhs),
)
@dataclasses.dataclass
class Monkey:
items: deque[int]
operation: Operation
test_divisor: int
transfer: dict[bool, int]
@classmethod
def from_input(cls, input: list[str]) -> "Monkey":
# Sanity checks
assert input[0].startswith("Monkey ")
assert "divisible by" in input[3]
assert "true" in input[4]
assert "false" in input[5]
items = deque(
int(n) for n in input[1].removeprefix(" Starting items: ").split(",")
)
operation = Operation.from_input(input[2])
divisor = int(input[3].split()[-1])
transfer = {
True: int(input[4].split()[-1]),
False: int(input[5].split()[-1]),
}
return Monkey(items, operation, divisor, transfer)
def solve(input: list[str]) -> int:
def do_round(monkeys: list[Monkey], counts: dict[int, int]) -> None:
for i, monkey in enumerate(monkeys):
counts[i] += len(monkey.items)
while monkey.items:
item = monkey.items.popleft()
item = monkey.operation(item)
item //= 3
target = monkey.transfer[(item % monkey.test_divisor) == 0]
monkeys[target].items.append(item)
monkeys = [Monkey.from_input(monkey_spec.splitlines()) for monkey_spec in input]
counts: Counter[int] = Counter()
for _ in range(20):
do_round(monkeys, counts)
((_, a), (_, b)) = counts.most_common(2)
return a * b
def main() -> None:
input = sys.stdin.read().split("\n\n")
print(solve(input))
if __name__ == "__main__":
main()

55
2022/d11/ex1/input Normal file
View file

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 50, 70, 54, 83, 52, 78
Operation: new = old * 3
Test: divisible by 11
If true: throw to monkey 2
If false: throw to monkey 7
Monkey 1:
Starting items: 71, 52, 58, 60, 71
Operation: new = old * old
Test: divisible by 7
If true: throw to monkey 0
If false: throw to monkey 2
Monkey 2:
Starting items: 66, 56, 56, 94, 60, 86, 73
Operation: new = old + 1
Test: divisible by 3
If true: throw to monkey 7
If false: throw to monkey 5
Monkey 3:
Starting items: 83, 99
Operation: new = old + 8
Test: divisible by 5
If true: throw to monkey 6
If false: throw to monkey 4
Monkey 4:
Starting items: 98, 98, 79
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 1
If false: throw to monkey 0
Monkey 5:
Starting items: 76
Operation: new = old + 4
Test: divisible by 13
If true: throw to monkey 6
If false: throw to monkey 3
Monkey 6:
Starting items: 52, 51, 84, 54
Operation: new = old * 17
Test: divisible by 19
If true: throw to monkey 4
If false: throw to monkey 1
Monkey 7:
Starting items: 82, 86, 91, 79, 94, 92, 59, 94
Operation: new = old + 7
Test: divisible by 2
If true: throw to monkey 5
If false: throw to monkey 3

98
2022/d11/ex2/ex2.py Executable file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env python
import dataclasses
import functools
import sys
from collections import Counter, deque
from typing import Literal, Optional
@dataclasses.dataclass
class Operation:
lhs: Optional[int]
operator: Literal["+", "*"]
rhs: Optional[int]
def __call__(self, old: int) -> int:
lhs = old if self.lhs is None else self.lhs
rhs = old if self.rhs is None else self.rhs
if self.operator == "*":
return lhs * rhs
if self.operator == "+":
return lhs + rhs
assert False
@classmethod
def from_input(cls, input: str) -> "Operation":
assert input.startswith(" Operation: new = ")
lhs, op, rhs = input.split()[-3:]
assert op in ("+", "*") # Sanity check
return cls(
None if lhs == "old" else int(lhs),
op, # type: ignore
None if rhs == "old" else int(rhs),
)
@dataclasses.dataclass
class Monkey:
items: deque[int]
operation: Operation
test_divisor: int
transfer: dict[bool, int]
@classmethod
def from_input(cls, input: list[str]) -> "Monkey":
# Sanity checks
assert input[0].startswith("Monkey ")
assert "divisible by" in input[3]
assert "true" in input[4]
assert "false" in input[5]
items = deque(
int(n) for n in input[1].removeprefix(" Starting items: ").split(",")
)
operation = Operation.from_input(input[2])
divisor = int(input[3].split()[-1])
transfer = {
True: int(input[4].split()[-1]),
False: int(input[5].split()[-1]),
}
return Monkey(items, operation, divisor, transfer)
def solve(input: list[str]) -> int:
def do_round(monkeys: list[Monkey], counts: dict[int, int]) -> None:
modulo = functools.reduce(
lambda lhs, rhs: lhs * rhs,
(monkey.test_divisor for monkey in monkeys),
)
for i, monkey in enumerate(monkeys):
counts[i] += len(monkey.items)
while monkey.items:
item = monkey.items.popleft()
item = monkey.operation(item)
item %= modulo # Keep a reasonable range on this value
target = monkey.transfer[(item % monkey.test_divisor) == 0]
monkeys[target].items.append(item)
monkeys = [Monkey.from_input(monkey_spec.splitlines()) for monkey_spec in input]
counts: Counter[int] = Counter()
for _ in range(10_000):
do_round(monkeys, counts)
((_, a), (_, b)) = counts.most_common(2)
return a * b
def main() -> None:
input = sys.stdin.read().split("\n\n")
print(solve(input))
if __name__ == "__main__":
main()

55
2022/d11/ex2/input Normal file
View file

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 50, 70, 54, 83, 52, 78
Operation: new = old * 3
Test: divisible by 11
If true: throw to monkey 2
If false: throw to monkey 7
Monkey 1:
Starting items: 71, 52, 58, 60, 71
Operation: new = old * old
Test: divisible by 7
If true: throw to monkey 0
If false: throw to monkey 2
Monkey 2:
Starting items: 66, 56, 56, 94, 60, 86, 73
Operation: new = old + 1
Test: divisible by 3
If true: throw to monkey 7
If false: throw to monkey 5
Monkey 3:
Starting items: 83, 99
Operation: new = old + 8
Test: divisible by 5
If true: throw to monkey 6
If false: throw to monkey 4
Monkey 4:
Starting items: 98, 98, 79
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 1
If false: throw to monkey 0
Monkey 5:
Starting items: 76
Operation: new = old + 4
Test: divisible by 13
If true: throw to monkey 6
If false: throw to monkey 3
Monkey 6:
Starting items: 52, 51, 84, 54
Operation: new = old * 17
Test: divisible by 19
If true: throw to monkey 4
If false: throw to monkey 1
Monkey 7:
Starting items: 82, 86, 91, 79, 94, 92, 59, 94
Operation: new = old + 7
Test: divisible by 2
If true: throw to monkey 5
If false: throw to monkey 3