diff --git a/samples/day11.txt b/samples/day11.txt new file mode 100644 index 0000000..c04eddb --- /dev/null +++ b/samples/day11.txt @@ -0,0 +1,27 @@ +Monkey 0: + Starting items: 79, 98 + Operation: new = old * 19 + Test: divisible by 23 + If true: throw to monkey 2 + If false: throw to monkey 3 + +Monkey 1: + Starting items: 54, 65, 75, 74 + Operation: new = old + 6 + Test: divisible by 19 + If true: throw to monkey 2 + If false: throw to monkey 0 + +Monkey 2: + Starting items: 79, 60, 97 + Operation: new = old * old + Test: divisible by 13 + If true: throw to monkey 1 + If false: throw to monkey 3 + +Monkey 3: + Starting items: 74 + Operation: new = old + 3 + Test: divisible by 17 + If true: throw to monkey 0 + If false: throw to monkey 1 \ No newline at end of file diff --git a/src/bin/day11.rs b/src/bin/day11.rs new file mode 100644 index 0000000..46c5f03 --- /dev/null +++ b/src/bin/day11.rs @@ -0,0 +1,232 @@ +use std::{cmp::Reverse, collections::VecDeque, mem, str::FromStr}; + +use aoc_2022::*; + +use color_eyre::{eyre::eyre, Report}; +use itertools::Itertools; +use tracing::debug; + +type Input = Vec; +type Output = usize; + +#[derive(Clone)] +enum Operation { + Add(usize), + AddSelf, + Mul(usize), + MulSelf, +} + +impl Operation { + fn apply(&self, x: usize) -> usize { + match self { + Self::Add(y) => x + y, + Self::AddSelf => x + x, + Self::Mul(y) => x * y, + Self::MulSelf => x * x, + } + } +} + +impl FromStr for Operation { + type Err = Report; + + fn from_str(s: &str) -> Result { + let (_, rhs) = s + .split(" = ") + .collect_tuple() + .ok_or(eyre!("couldn't split operation"))?; + let (l, op, r) = rhs + .split_ascii_whitespace() + .collect_tuple() + .ok_or(eyre!("couldn't split the expression"))?; + + if l == r { + match op { + "+" => Ok(Self::AddSelf), + "*" => Ok(Self::MulSelf), + _ => Err(eyre!("invalid operator for ‹old›")), + } + } else { + match op { + "+" => Ok(Self::Add(r.parse()?)), + "*" => Ok(Self::Mul(r.parse()?)), + _ => Err(eyre!("invalid operator for ‹old›")), + } + } + } +} + +#[derive(Clone)] +struct Test(usize, usize, usize); + +impl Test { + fn get(&self, worry: usize) -> usize { + let &Test(div, t, f) = self; + + if worry % div == 0 { + t + } else { + f + } + } +} + +impl FromStr for Test { + type Err = Report; + + fn from_str(s: &str) -> Result { + let (divisor, t_branch, f_branch) = s + .split('\n') + .map(|l| l.split_ascii_whitespace().last().unwrap()) + .map(|n| n.parse().unwrap()) + .collect_tuple() + .ok_or(eyre!("couldn't parse the test"))?; + + Ok(Test(divisor, t_branch, f_branch)) + } +} + +#[derive(Clone)] +struct Monkey { + items: Vec, + operation: Operation, + test: Test, + + inspections: usize, +} + +impl FromStr for Monkey { + type Err = Report; + + fn from_str(s: &str) -> Result { + let (_, items_s, op_s, test_s, t_s, f_s) = s + .split('\n') + .collect_tuple() + .ok_or(eyre!("Couldn't split string correctly"))?; + + let items = items_s + .split(": ") + .nth(1) + .ok_or(eyre!("No items present"))? + .split(", ") + .map(|x| x.parse().unwrap()) + .collect_vec(); + let operation = op_s.parse()?; + let test = vec![test_s, t_s, f_s].join("\n").parse()?; + + Ok(Monkey { + items, + operation, + test, + + inspections: 0, + }) + } +} + +fn round(monkeys: &mut Input, lcm: usize, div: bool) { + let mut q: VecDeque<(usize, usize)> = VecDeque::new(); + + for i in 0..monkeys.len() { + let m = &mut monkeys[i]; + + for j in 0..m.items.len() { + let mut worry = m.operation.apply(m.items[j]); + if div { + worry /= 3; + } + worry %= lcm; + + q.push_back((m.test.get(worry), worry)); + m.inspections += 1; + } + + m.items.clear(); + + while let Some((i, w)) = q.pop_front() { + monkeys[i].items.push(w); + } + } +} + +fn euclid(a: usize, b: usize) -> usize { + // variable names based off euclidean division equation: a = b · q + r + let (mut a, mut b) = if a > b { (a, b) } else { (b, a) }; + + while b != 0 { + mem::swap(&mut a, &mut b); + b %= a; + } + + a +} + +struct Day11; +impl Solution for Day11 { + fn parse_input>(pathname: P) -> Input { + file_to_string(pathname) + .split("\n\n") + .map(|m| m.parse().unwrap()) + .collect_vec() + } + + fn part_1(input: &Input) -> Output { + let mut monkeys = input.clone(); + + let lcm = monkeys + .iter() + .map(|m| m.test.0) + .fold(1, |x, y| x * y / euclid(x, y)); + + for _ in 0..20 { + round(&mut monkeys, lcm, true); + } + + monkeys + .iter() + .map(|m| m.inspections) + .sorted_by_key(|&i| Reverse(i)) + .take(2) + .product() + } + + fn part_2(input: &Input) -> Output { + let mut monkeys = input.clone(); + + let lcm = monkeys + .iter() + .map(|m| m.test.0) + .fold(1, |x, y| (x * y) / euclid(x, y)); + + let inspect = vec![ + 1, 20, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 1000, + ]; + + for r in 0..10000 { + round(&mut monkeys, lcm, false); + + if inspect.contains(&(r + 1)) { + debug!( + "Monkeys: {:?}", + monkeys.iter().map(|m| m.inspections).collect_vec() + ); + } + } + + monkeys + .iter() + .map(|m| m.inspections) + // .inspect(|x| debug!("inspections of monkey: {x}")) + .sorted_by_key(|&i| Reverse(i)) + .take(2) + .product() + } +} + +fn main() -> Result<()> { + // Day11::run("sample") + Day11::main() +} + +test_sample!(day_11, Day11, 10605, 2713310158);