227 lines
5.2 KiB
Rust
227 lines
5.2 KiB
Rust
use std::{collections::VecDeque, str::FromStr};
|
|
|
|
use aoc_runner_derive::{aoc, aoc_generator};
|
|
use itertools::Itertools;
|
|
|
|
struct Monkey {
|
|
num: u32,
|
|
starting_items: Vec<u64>,
|
|
operation: Operation,
|
|
test: u64,
|
|
if_true: u32,
|
|
if_false: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Operation {
|
|
lhs: Val,
|
|
op: Operator,
|
|
rhs: Val,
|
|
}
|
|
|
|
impl Operation {
|
|
fn eval(self, old: u64) -> u64 {
|
|
match self.op {
|
|
Operator::Add => self.lhs.eval(old).checked_add(self.rhs.eval(old)).unwrap(),
|
|
Operator::Mul => self.lhs.eval(old).checked_mul(self.rhs.eval(old)).unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for Operation {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let (lhs, op, rhs) = s.split(' ').collect_tuple().unwrap();
|
|
|
|
Ok(Self {
|
|
lhs: lhs.parse()?,
|
|
op: op.parse()?,
|
|
rhs: rhs.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum Val {
|
|
Old,
|
|
Num(u64),
|
|
}
|
|
|
|
impl Val {
|
|
fn eval(self, old: u64) -> u64 {
|
|
match self {
|
|
Val::Old => old,
|
|
Val::Num(num) => num,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for Val {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
if s == "old" {
|
|
Ok(Self::Old)
|
|
} else if let Ok(num) = s.parse() {
|
|
Ok(Self::Num(num))
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
enum Operator {
|
|
Add,
|
|
Mul,
|
|
}
|
|
|
|
impl FromStr for Operator {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"+" => Ok(Self::Add),
|
|
"*" => Ok(Self::Mul),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[aoc_generator(day11)]
|
|
fn generator(input: &str) -> Vec<Monkey> {
|
|
input
|
|
.split("\n\n")
|
|
.enumerate()
|
|
.map(|(i, s)| {
|
|
let lines: (_, _, _, _, _, _) = s.lines().collect_tuple().unwrap();
|
|
|
|
let num = lines
|
|
.0
|
|
.strip_prefix("Monkey ")
|
|
.unwrap()
|
|
.strip_suffix(':')
|
|
.unwrap()
|
|
.parse()
|
|
.unwrap();
|
|
|
|
assert_eq!(num, i as u32);
|
|
|
|
let starting_items = lines
|
|
.1
|
|
.strip_prefix(" Starting items: ")
|
|
.unwrap()
|
|
.split(',')
|
|
.map(|v| v.trim().parse().unwrap())
|
|
.collect();
|
|
|
|
let operation = lines
|
|
.2
|
|
.strip_prefix(" Operation: new = ")
|
|
.unwrap()
|
|
.parse()
|
|
.unwrap();
|
|
|
|
let test = lines
|
|
.3
|
|
.strip_prefix(" Test: divisible by ")
|
|
.unwrap()
|
|
.parse()
|
|
.unwrap();
|
|
|
|
let if_true = lines
|
|
.4
|
|
.strip_prefix(" If true: throw to monkey ")
|
|
.unwrap()
|
|
.parse()
|
|
.unwrap();
|
|
|
|
let if_false = lines
|
|
.5
|
|
.strip_prefix(" If false: throw to monkey ")
|
|
.unwrap()
|
|
.parse()
|
|
.unwrap();
|
|
|
|
Monkey {
|
|
num,
|
|
starting_items,
|
|
operation,
|
|
test,
|
|
if_true,
|
|
if_false,
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
struct MonkeyState {
|
|
items: VecDeque<u64>,
|
|
inspection_count: u64,
|
|
}
|
|
|
|
fn stuff_slinging_simian_shenanigans(
|
|
input: &[Monkey],
|
|
rounds: u32,
|
|
mut post_inspection_cb: impl FnMut(&mut u64),
|
|
) -> u64 {
|
|
let mut states = input
|
|
.iter()
|
|
.map(|v| MonkeyState {
|
|
items: v.starting_items.iter().copied().collect(),
|
|
inspection_count: 0,
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
for _ in 0..rounds {
|
|
for monkey in input {
|
|
loop {
|
|
let state = &mut states[monkey.num as usize];
|
|
|
|
if let Some(mut item) = state.items.pop_front() {
|
|
item = monkey.operation.eval(item);
|
|
state.inspection_count += 1;
|
|
post_inspection_cb(&mut item);
|
|
|
|
if item % monkey.test == 0 {
|
|
states[monkey.if_true as usize].items.push_back(item);
|
|
} else {
|
|
states[monkey.if_false as usize].items.push_back(item);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let (a, b) = states.iter().fold((0, 0), |(mut a, mut b), state| {
|
|
let mut inspection_count = state.inspection_count;
|
|
|
|
if inspection_count > a {
|
|
std::mem::swap(&mut a, &mut inspection_count);
|
|
}
|
|
|
|
if inspection_count > b {
|
|
std::mem::swap(&mut b, &mut inspection_count);
|
|
}
|
|
|
|
(a, b)
|
|
});
|
|
|
|
a * b
|
|
}
|
|
|
|
#[aoc(day11, part1)]
|
|
fn part1(input: &[Monkey]) -> u64 {
|
|
stuff_slinging_simian_shenanigans(input, 20, |item| *item /= 3)
|
|
}
|
|
|
|
#[aoc(day11, part2)]
|
|
fn part2(input: &[Monkey]) -> u64 {
|
|
let max_modulo: u64 = input.iter().map(|v| v.test).product();
|
|
|
|
stuff_slinging_simian_shenanigans(input, 10000, |item| *item %= max_modulo)
|
|
}
|