2022-12-11 - Monkey in the Middle
(original .ipynb)
Day 11 puzzle input is a description of how some monkeys will throw some items around between them depending on some properties I'm not even going to begin to summarise (mine is here). Part 1 involves simulating how the monkeys will behave for 20 "rounds" of this throwing, then finding the two monkeys which handled/"inspected" each item and multiplying these numbers. Part 2 involves doing adjusting part of the calc (there's a point where we divide a "worriedness" number previously, that we now no longer do) and running for 10,000 rounds.
from functools import reduce
from math import lcm
puzzle_input_str = open("./puzzle_input/day11.txt").read()
test_input_str = open("./test_input/day11.txt").read()
operations = {
"*": lambda x,y: x*y,
"+": lambda x,y: x+y,
"-": lambda x,y: x-y,
"/": lambda x,y: x//y,
}
def parse_monkey_id(line):
return int(line[len("Monkey "):][:-1])
def parse_starting_items(line):
return [int(s) for s in line[len(" Starting items: "):].split(", ")]
def parse_operation(line):
tokens = line[len(" Operation: "):].split(" ")
func = operations[tokens[-2]]
rhs = tokens[-1]
return (func, rhs if not rhs.isnumeric() else int(rhs))
def parse_divisible_by(line):
tokens = line.split(" ")
return int(tokens[-1])
def parse_targets(lines):
true_line, false_line = lines
true_monkey = true_line.split(" ")[-1]
false_monkey = false_line.split(" ")[-1]
return int(true_monkey), int(false_monkey)
def parse_monkey(monkey_str):
lines = monkey_str.splitlines()
monkey_id = parse_monkey_id(lines[0])
starting_items = parse_starting_items(lines[1])
operation = parse_operation(lines[2])
divisible_by = parse_divisible_by(lines[3])
true_target, false_target = parse_targets(lines[4:])
return monkey_id, starting_items, operation, divisible_by, true_target, false_target
def get_value(v, old):
return v if type(v) == int else old
def part_one(input_str, worry_divisor=3, rounds=20):
monkeys = [parse_monkey(monkey_str) for monkey_str in input_str.split("\n\n")]
monkey_items = []
monkey_operations = []
monkey_divisible_by = []
monkey_targets = []
monkey_inspection = []
for monkey_id, starting_items, operation, divisible_by, true_target, false_target in monkeys:
monkey_items.append(starting_items)
monkey_operations.append(operation)
monkey_divisible_by.append(divisible_by)
monkey_targets.append({
True: true_target,
False: false_target
})
monkey_inspection.append(0)
foo = lcm(*monkey_divisible_by)
while rounds > 0:
for m in range(len(monkeys)):
next_items = []
func, rhs = monkey_operations[m]
while len(monkey_items[m]) > 0:
item = monkey_items[m].pop(0)
item = func(item, get_value(rhs, item)) // worry_divisor
res = item % monkey_divisible_by[m] == 0
monkey_items[monkey_targets[m][res]].append(item % foo)
monkey_inspection[m] += 1
rounds -= 1
return reduce(lambda x,y: x*y, list(sorted(monkey_inspection, reverse=True))[:2])
assert 10605 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 110888
def part_two(input_str):
return part_one(input_str, 1, 10_000)
assert 2713310158 == part_one(test_input_str, 1, 10_000)
print("part two", part_two(puzzle_input_str))
part two 25590400731