Compare commits
No commits in common. "091dc34acdf10199ee86e40263fd3952597fce0a" and "44dafef2431e6dd3a4f61ad42e4143143219c01f" have entirely different histories.
091dc34acd
...
44dafef243
|
@ -1,93 +0,0 @@
|
||||||
#!/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()
|
|
|
@ -1,55 +0,0 @@
|
||||||
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
|
|
|
@ -1,98 +0,0 @@
|
||||||
#!/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()
|
|
|
@ -1,55 +0,0 @@
|
||||||
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
|
|
Loading…
Reference in a new issue