diff --git a/input/day11.txt b/input/day11.txt new file mode 100644 index 0000000..fffbaec --- /dev/null +++ b/input/day11.txt @@ -0,0 +1,55 @@ +Monkey 0: + Starting items: 61 + Operation: new = old * 11 + Test: divisible by 5 + If true: throw to monkey 7 + If false: throw to monkey 4 + +Monkey 1: + Starting items: 76, 92, 53, 93, 79, 86, 81 + Operation: new = old + 4 + Test: divisible by 2 + If true: throw to monkey 2 + If false: throw to monkey 6 + +Monkey 2: + Starting items: 91, 99 + Operation: new = old * 19 + Test: divisible by 13 + If true: throw to monkey 5 + If false: throw to monkey 0 + +Monkey 3: + Starting items: 58, 67, 66 + Operation: new = old * old + Test: divisible by 7 + If true: throw to monkey 6 + If false: throw to monkey 1 + +Monkey 4: + Starting items: 94, 54, 62, 73 + Operation: new = old + 1 + Test: divisible by 19 + If true: throw to monkey 3 + If false: throw to monkey 7 + +Monkey 5: + Starting items: 59, 95, 51, 58, 58 + Operation: new = old + 3 + Test: divisible by 11 + If true: throw to monkey 0 + If false: throw to monkey 4 + +Monkey 6: + Starting items: 87, 69, 92, 56, 91, 93, 88, 73 + Operation: new = old + 8 + Test: divisible by 3 + If true: throw to monkey 5 + If false: throw to monkey 2 + +Monkey 7: + Starting items: 71, 57, 86, 67, 96, 95 + Operation: new = old + 7 + Test: divisible by 17 + If true: throw to monkey 3 + If false: throw to monkey 1 \ No newline at end of file diff --git a/input/day11_example.txt b/input/day11_example.txt new file mode 100644 index 0000000..c04eddb --- /dev/null +++ b/input/day11_example.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/day11.rs b/src/day11.rs new file mode 100644 index 0000000..3739582 --- /dev/null +++ b/src/day11.rs @@ -0,0 +1,193 @@ +use crate::day_solver::DaySolver; + +use super::util; + +pub struct Day11 { + monkeys: Vec +} + +impl Day11 { + + pub fn create() -> Self { + // let lines = util::read_file("input/day11_example.txt"); + let lines = util::read_file("input/day11.txt"); + + let mut lines_it = lines.iter(); + let mut monkeys = Vec::new(); + while let Some(line) = lines_it.next() { + if line.starts_with("Monkey") { + // Let's parse the whole monkey + let start_items_line = lines_it.next().unwrap(); + assert!(start_items_line.starts_with(" Starting items: ")); + let items: Vec = (&start_items_line[" Starting items: ".len()..]) + .split(", ").filter_map(|s| s.parse::().ok()).collect(); + + let operation_line = lines_it.next().unwrap(); + assert!(operation_line.starts_with(" Operation: new = ")); + let mut operation_split = (&operation_line[" Operation: new = ".len()..]).split_whitespace(); + let operation = Operation { + arg1: OperationVar::parse(operation_split.next().unwrap()), + op: operation_split.next().unwrap().chars().next().unwrap(), + arg2: OperationVar::parse(operation_split.next().unwrap()) + }; + + let test_line = lines_it.next().unwrap(); + assert!(test_line.starts_with(" Test: divisible by ")); + let check_divisor = (&test_line[" Test: divisible by ".len()..]).parse::().unwrap(); + + let if_true_line = lines_it.next().unwrap(); + assert!(if_true_line.starts_with(" If true: throw to monkey ")); + let if_true_throw_to_monkey = (&if_true_line[" If true: throw to monkey ".len()..]).parse::().unwrap(); + + let if_false_line = lines_it.next().unwrap(); + assert!(if_false_line.starts_with(" If false: throw to monkey ")); + let if_false_throw_to_monkey = (&if_false_line[" If false: throw to monkey ".len()..]).parse::().unwrap(); + + monkeys.push(Monkey { + items, + operation, + test: MonkeyTest { check_divisor, if_true_throw_to_monkey, if_false_throw_to_monkey } + }) + } + } + // Put the input into the day struct + return Day11 { + monkeys + } + } +} + +impl DaySolver for Day11 { + + + fn solve_part1(&mut self) -> String { + + // println!("{:?}", self.monkeys); + + let mut monkey_items: Vec> = self.monkeys.iter() + .map(|m| m.items.to_owned()).collect(); + let mut monkey_inspection_counts: Vec = vec![0; self.monkeys.len()]; + + for _round in 0..20 { + + let mut monkey_id = 0; + for monkey in &self.monkeys { + let cur_monkey_items = monkey_items[monkey_id].to_owned(); + monkey_inspection_counts[monkey_id] += cur_monkey_items.len(); + monkey_items[monkey_id].clear(); + for item in &cur_monkey_items { + let item_worry_level = (monkey.operation.execute(item)) / 3; + if item_worry_level % monkey.test.check_divisor == 0 { + monkey_items[monkey.test.if_true_throw_to_monkey].push(item_worry_level); + } else { + monkey_items[monkey.test.if_false_throw_to_monkey].push(item_worry_level); + } + } + monkey_id += 1; + } + + // println!("After round {}, monkey items: ", _round); + // for (i, monkey_items) in monkey_items.iter().enumerate() { + // println!("Monkey {}: {:?}", i, monkey_items); + // } + + } + + // println!("{:?}", monkey_inspection_counts); + monkey_inspection_counts.sort_by_cached_key(|k| usize::MAX - k); + + return (monkey_inspection_counts[0] * monkey_inspection_counts[1]).to_string(); + } + + fn solve_part2(&mut self) -> String { + + let mut monkey_items: Vec> = self.monkeys.iter() + .map(|m| m.items.to_owned()).collect(); + let mut monkey_inspection_counts: Vec = vec![0; self.monkeys.len()]; + let normalizer = self.monkeys.iter().map(|m| m.test.check_divisor) + .reduce(|a, b| a * b).unwrap(); + + for _round in 0..10000 { + + let mut monkey_id = 0; + for monkey in &self.monkeys { + let cur_monkey_items = monkey_items[monkey_id].to_owned(); + monkey_inspection_counts[monkey_id] += cur_monkey_items.len(); + monkey_items[monkey_id].clear(); + for item in &cur_monkey_items { + let item_worry_level = monkey.operation.execute(item) % normalizer; + if item_worry_level % monkey.test.check_divisor == 0 { + monkey_items[monkey.test.if_true_throw_to_monkey].push(item_worry_level); + } else { + monkey_items[monkey.test.if_false_throw_to_monkey].push(item_worry_level); + } + } + monkey_id += 1; + } + + } + + monkey_inspection_counts.sort_by_cached_key(|k| usize::MAX - k); + + return (monkey_inspection_counts[0] as u64 * monkey_inspection_counts[1] as u64).to_string(); + } +} + +#[derive(Debug, Clone)] +struct Monkey { + items: Vec, + operation: Operation, + test: MonkeyTest +} + +#[derive(Debug, Clone)] +struct Operation { + op: char, + arg1: OperationVar, + arg2: OperationVar +} + +#[derive(Debug, Clone)] +enum OperationVar { + Old, + Num(u64) +} + +impl OperationVar { + fn concrete<'a>(&'a self, old: &'a u64) -> &'a u64 { + match self { + OperationVar::Old => old, + OperationVar::Num(x) => x + } + } + + fn parse(str: &str) -> Self { + if str == "old" { + OperationVar::Old + } else { + OperationVar::Num(str.parse().unwrap()) + } + } +} + +impl Operation { + fn execute(&self, old: &u64) -> u64 { + + let arg1 = self.arg1.concrete(old); + let arg2 = self.arg2.concrete(old); + + match self.op { + '*' => arg1 * arg2, + '+' => arg1 + arg2, + _ => panic!("Unsupported operation: {}", self.op) + } + + } +} + +#[derive(Debug, Clone)] +struct MonkeyTest { + check_divisor: u64, + if_true_throw_to_monkey: usize, + if_false_throw_to_monkey: usize +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5eee336..bf52858 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use crate::day7::Day7; use crate::day8::Day8; use crate::day9::Day9; use crate::day10::Day10; +use crate::day11::Day11; use crate::day_solver::DaySolver; mod util; @@ -23,8 +24,9 @@ mod day7; mod day8; mod day9; mod day10; +mod day11; -const MAX_DAY: u8 = 9; +const MAX_DAY: u8 = 11; const DEFAULT_BENCHMARK_AMOUNT: u32 = 100; fn main() { @@ -98,6 +100,7 @@ fn build_day_solver(day: u8) -> Option> { 8 => Some(Box::new(Day8::create())), 9 => Some(Box::new(Day9::create())), 10 => Some(Box::new(Day10::create())), + 11 => Some(Box::new(Day11::create())), _ => None } }