import re
from math import floor
import numpy as np
Day 11
python
%
Part 1
Ok, maybe I’ll do this with some python classes…
This took a few iterations.
class Monkey:
def __init__(self, items, operation, test, test_true, test_false):
self.items = items
self.operation = operation
self.test = test
self.test_true = test_true
self.test_false = test_false
self.inspections = 0
self.test_results = []
self.throw_to = []
self.op_fun = lambda old: eval(self.operation)
self.test_fun = lambda x: x % self.test == 0
def update_worry(self):
self.items = [self.op_fun(x) for x in self.items]
self.items = [floor(x/3) for x in self.items]
self.inspections += len(self.items)
def test_items(self):
self.test_results = [self.test_fun(x) for x in self.items]
def set_throws(self):
self.throw_to = [self.test_true if x else self.test_false for x in self.test_results]
def receive(self, x):
self.items.append(x)
def set_thrown(self):
self.items = []
self.test_results = []
self.throw_to = []
The plan is to parse the input into a dictionary, to be passed to the Monkey()
class via **kwargs
.
def parse_monkeys(path):
with open(path) as f:
= f.read()
content = content.split("\n\n")
monkey_lines= [x.split("\n") for x in monkey_lines]
monkey_lines = [{} for _ in range(len(monkey_lines))]
monkey_rules for idx, ruleset in enumerate(monkey_lines):
= re.sub(r"\s+Starting items:\s+", "", ruleset[1]).split(",")
items = [int(x) for x in items]
items "items"] = items
monkey_rules[idx][
= re.sub(r"\s+Operation: new = ", "", ruleset[2])
operation "operation"] = operation
monkey_rules[idx][
= re.findall(r"\d+", ruleset[3])
test "test"] = int(test[0])
monkey_rules[idx][
= re.findall(r"\d+", ruleset[4])
test_true "test_true"] = int(test_true[0])
monkey_rules[idx][
= re.findall(r"\d+", ruleset[5])
test_false "test_false"] = int(test_false[0])
monkey_rules[idx][
return(monkey_rules)
Now, a function for one iteration.
def one_round(monkeys):
for m in monkeys:
m.update_worry()
m.test_items()
m.set_throws()for item, to_m in zip(m.items, m.throw_to):
monkeys[to_m].receive(item)
m.set_thrown()return(monkeys)
= parse_monkeys("2022-12-11_assets/example.txt") ex_monkey_rules
= [Monkey(**rules) for rules in ex_monkey_rules] ex_monkeys
= one_round(ex_monkeys)
ex_monkeys = one_round(ex_monkeys)
ex_monkeys for m in ex_monkeys] [m.items
[[695, 10, 71, 135, 350], [43, 49, 58, 55, 362], [], []]
That looks right. Time to test the 20 round example.
# reset
= parse_monkeys("2022-12-11_assets/example.txt")
ex_monkey_rules = [Monkey(**rules) for rules in ex_monkey_rules]
ex_monkeys for i in range(20):
= one_round(ex_monkeys)
ex_monkeys
for m in ex_monkeys] [m.inspections
[101, 95, 7, 105]
Ok, now the real deal
= parse_monkeys("2022-12-11_assets/input.txt")
monkey_rules = [Monkey(**rules) for rules in monkey_rules]
monkeys for i in range(20):
= one_round(monkeys)
monkeys
= [m.inspections for m in monkeys] inspection_counts
inspection_counts.sort()
= inspection_counts[-2:]
most_counts 0] * most_counts[1] most_counts[
108240
Part 2
It says the worry is no longer divided by 3, “you’ll need to find another way to keep your worry levels manageable” hmm. Is that integer overflow or something?
Caveat: I got hints from the subreddit for this one
class Monkey2:
def __init__(self, items, operation, test, test_true, test_false, const):
self.const = const
self.items = [x for x in items]
self.operation = operation
self.test = test
self.test_true = test_true
self.test_false = test_false
self.inspections = 0
self.test_results = []
self.throw_to = []
self.op_fun = lambda old: eval(self.operation)
self.test_fun = lambda x: x % self.test == 0
def update_worry(self):
self.items = [self.op_fun(x) for x in self.items]
self.items = [x%self.const for x in self.items]
self.inspections += len(self.items)
def test_items(self):
self.test_results = [self.test_fun(x) for x in self.items]
def set_throws(self):
self.throw_to = [self.test_true if x else self.test_false for x in self.test_results]
def receive(self, x):
self.items.append(x)
def set_thrown(self):
self.items = []
self.test_results = []
self.throw_to = []
= parse_monkeys("2022-12-11_assets/input.txt")
monkey_rules = np.prod([m["test"] for m in monkey_rules])
const = [Monkey2(**rules, const = const) for rules in monkey_rules]
monkeys for i in range(10_000):
= one_round(monkeys)
monkeys
= [m.inspections for m in monkeys] inspection_counts
inspection_counts.sort()-2:]) np.prod(inspection_counts[
25712998901
Just for Fun
def one_round(monkeys):
= []
from_m_l = []
to_m_lfor i, m in enumerate(monkeys):
m.update_worry()
m.test_items()
m.set_throws()for item, to_m in zip(m.items, m.throw_to):
monkeys[to_m].receive(item)
from_m_l.append(i)
to_m_l.append(to_m)
m.set_thrown()return(monkeys, from_m_l, to_m_l)
= parse_monkeys("2022-12-11_assets/input.txt")
monkey_rules = np.prod([m["test"] for m in monkey_rules])
const = [Monkey2(**rules, const = const) for rules in monkey_rules]
monkeys = []
all_from = []
all_to for i in range(10_000):
= one_round(monkeys)
monkeys, from_m, to_m += from_m
all_from += to_m all_to
```{r}
#| message: false
library(tidyverse)
library(ggdark)
library(khroma)
library(showtext)
library(scales)
library(tidygraph)
library(ggraph)
font_add_google(name = "Mountains of Christmas", family = "christmas")
font_add(family = "Noto Emoji", regular = file.path(font_paths()[2], "NotoEmoji-VariableFont_wght.ttf"))
showtext_auto()
theme_set(dark_theme_gray() +
theme(title = element_text(family = "christmas", size = 20)))
::knit_hooks$set(crop = knitr::hook_pdfcrop)
knitr```
```{r}
library(reticulate)
<-
monkey_network tibble(from = py$all_from, to = py$all_to)
```
```{r}
#| message: false
#| crop: true
#| label: fig-network
#| fig-cap: "Monkey network"
#| out-width: "60%"
|>
monkey_network group_by(from, to) |>
count() |>
as_tbl_graph() |>
ggraph(layout = "kk", weights = 1/n) +
geom_edge_link(color = "white",
arrow = arrow(type = 'closed',
length = unit(0.25, 'cm'),
angle = 25),
alpha = 0.6
+
)geom_node_text(label = emojifont::emoji("monkey"),
family = "Noto Emoji",
size = 9,
aes(colour = name))+
scale_color_brewer(palette = "Dark2", guide = "none")+
coord_fixed()+
scale_y_continuous(expand = expansion(mult = 0.1))+
scale_x_continuous(expand = expansion(mult = 0.1))+
labs(title = str_wrap("The monkey throwing pattern", width = 25))
```
Warning: Using the `size` aesthetic in this geom was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` in the `default_aes` field and elsewhere instead.