advent-of-code/2022/src/bin/day11.rs
2022-12-11 22:51:58 +01:00

124 lines
3.8 KiB
Rust

#![feature(test)]
extern crate test;
use aoc2022::{boilerplate, common::*};
const DAY: usize = 11;
type Parsed = Vec<Monkey>;
#[derive(Clone, Debug)]
struct Monkey {
inspection_count: usize,
items: Vec<usize>,
op: MonkeyOp,
div_test: usize,
true_dst: usize,
false_dst: usize,
}
#[derive(Copy, Clone, Debug)]
enum MonkeyOp {
Add(Option<usize>),
Mul(Option<usize>),
}
fn parse_input(raw: &str) -> Parsed {
raw.split("\n\n")
.map(|raw_monkey| {
let mut lines = raw_monkey.lines();
let [_, start, op, test, if_true, if_false] = std::array::from_fn(|_| lines.next().unwrap());
let items = start[18..].split(", ").map(parse_num).collect();
let div_test = parse_num(&test[21..]);
let true_dst = parse_num(&if_true[29..]);
let false_dst = parse_num(&if_false[30..]);
let op = op.as_bytes();
let op = match (op[23], op[25]) {
(b'+', b'o') => MonkeyOp::Add(None),
(b'+', x) => MonkeyOp::Add(Some((x - b'0') as _)),
(b'*', b'o') => MonkeyOp::Mul(None),
(b'*', x) => MonkeyOp::Mul(Some((x - b'0') as _)),
_ => unreachable!(),
};
Monkey { inspection_count: 0, items, op, div_test, true_dst, false_dst }
})
.collect()
}
fn part1(parsed: &Parsed) -> usize {
monkey_business::<20, 3>(parsed)
}
fn part2(parsed: &Parsed) -> usize {
monkey_business::<10_000, 1>(parsed)
}
fn monkey_business<const ITERATIONS: usize, const STRESS_REDUCTION: usize>(parsed: &Parsed) -> usize {
// The checks are all prime numbers, so the lcm is just their product.
let lcm = parsed.iter().map(|m| m.div_test).product();
let mut monkeys: Vec<Monkey> = parsed.clone();
let mut moved = Vec::new();
for _ in 0..ITERATIONS {
let mut i = 0;
while i < monkeys.len() {
let monkey = monkeys.get_mut(i).unwrap();
monkey.inspection_count += monkey.items.len();
while !monkey.items.is_empty() {
moved.extend(monkey.items.drain(..).map(|mut stress| {
stress = match monkey.op {
MonkeyOp::Add(x) => stress + x.unwrap_or(stress),
MonkeyOp::Mul(x) => stress * x.unwrap_or(stress),
} / STRESS_REDUCTION;
if stress > lcm {
stress %= lcm;
}
(stress, if stress % monkey.div_test == 0 { monkey.true_dst } else { monkey.false_dst })
}));
}
drop(monkey);
for (item, dst) in moved.drain(..) {
monkeys.get_mut(dst).unwrap().items.push(item);
}
i += 1;
}
}
monkeys.sort_by_key(|m| m.inspection_count);
monkeys.into_iter().rev().map(|m| m.inspection_count).take(2).product()
}
boilerplate! {
TEST_INPUT == "\
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",
tests: {
part1: { TEST_INPUT => 10605 },
part2: { TEST_INPUT => 2713310158 },
},
bench1 == 56595,
bench2 == 15693274740,
bench_parse: Vec::len => 8,
}