diff --git a/2022/inputs/day11 b/2022/inputs/day11 new file mode 100644 index 0000000..0a6a52e --- /dev/null +++ b/2022/inputs/day11 @@ -0,0 +1,55 @@ +Monkey 0: + Starting items: 96, 60, 68, 91, 83, 57, 85 + Operation: new = old * 2 + Test: divisible by 17 + If true: throw to monkey 2 + If false: throw to monkey 5 + +Monkey 1: + Starting items: 75, 78, 68, 81, 73, 99 + Operation: new = old + 3 + Test: divisible by 13 + If true: throw to monkey 7 + If false: throw to monkey 4 + +Monkey 2: + Starting items: 69, 86, 67, 55, 96, 69, 94, 85 + Operation: new = old + 6 + Test: divisible by 19 + If true: throw to monkey 6 + If false: throw to monkey 5 + +Monkey 3: + Starting items: 88, 75, 74, 98, 80 + Operation: new = old + 5 + Test: divisible by 7 + If true: throw to monkey 7 + If false: throw to monkey 1 + +Monkey 4: + Starting items: 82 + Operation: new = old + 8 + Test: divisible by 11 + If true: throw to monkey 0 + If false: throw to monkey 2 + +Monkey 5: + Starting items: 72, 92, 92 + Operation: new = old * 5 + Test: divisible by 3 + If true: throw to monkey 6 + If false: throw to monkey 3 + +Monkey 6: + Starting items: 74, 61 + Operation: new = old * old + Test: divisible by 2 + If true: throw to monkey 3 + If false: throw to monkey 1 + +Monkey 7: + Starting items: 76, 86, 83, 55 + Operation: new = old + 4 + Test: divisible by 5 + If true: throw to monkey 4 + If false: throw to monkey 0 diff --git a/2022/src/bin/day11.rs b/2022/src/bin/day11.rs new file mode 100644 index 0000000..a2d0ac3 --- /dev/null +++ b/2022/src/bin/day11.rs @@ -0,0 +1,134 @@ +#![feature(test)] +extern crate test; +use aoc2022::{boilerplate, common::*}; + +const DAY: usize = 11; +type Parsed = Vec; + +#[derive(Clone, Debug)] +struct Monkey { + inspection_count: usize, + items: Vec, + op: MonkeyOp, + div_test: usize, + true_dst: usize, + false_dst: usize, +} + +#[derive(Copy, Clone, Debug)] +enum MonkeyOp { + Add(Option, Option), + Mul(Option, Option), +} + +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 = match op[19..].split(' ').collect::>().as_slice() { + [x, "+", y] => MonkeyOp::Add(x.parse().ok(), y.parse().ok()), + [x, "*", y] => MonkeyOp::Mul(x.parse().ok(), y.parse().ok()), + _ => unreachable!(), + }; + Monkey { inspection_count: 0, items, op, div_test, true_dst, false_dst } + }) + .collect() +} + +// Stolen from my 2019 day 12 solution +fn lcm(x: usize, y: usize) -> usize { + fn gcd(mut x: usize, mut y: usize) -> usize { + let mut remainder; + while y != 0 { + remainder = x % y; + x = y; + y = remainder; + } + x + } + x * y / gcd(x, y) +} + +fn part1(parsed: &Parsed) -> usize { + monkey_business::<20, 3>(parsed) +} + +fn part2(parsed: &Parsed) -> usize { + monkey_business::<10_000, 1>(parsed) +} + +fn monkey_business(parsed: &Parsed) -> usize { + let lcm = parsed.iter().map(|m| m.div_test).reduce(lcm).unwrap(); + println!("lcm: {lcm}"); + let mut monkeys: Vec = 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, y) => x.unwrap_or(stress) + y.unwrap_or(stress), + MonkeyOp::Mul(x, y) => x.unwrap_or(stress) * y.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, +}