diff --git a/day11/common.rs b/day11/common.rs new file mode 100644 index 0000000..35f2cdc --- /dev/null +++ b/day11/common.rs @@ -0,0 +1,197 @@ +// Copyright 2022 Christian Ulrich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::io; + +#[derive(Debug, Clone)] +enum Operation { + Add { arg: u64 }, + Multiply { arg: u64 }, + Square, +} + +#[derive(Debug, Clone)] +pub struct Monkey { + items: Vec, + operation: Operation, + modulus: u64, + target_devisible: usize, + target_indevisible: usize, + item_count: usize, +} + +pub struct Game { + monkeys: Vec, + divisor: u64, +} + +pub struct Round { + monkeys: Vec, +} + +impl Monkey { + fn from_stdin() -> Result, &'static str> { + const ERROR: &str = "cannot parse monkey"; + let mut line = String::new(); + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if line.len() == 0 { + return Ok(None); + } + if !line.starts_with("Monkey ") { + return Err(ERROR); + } + line.clear(); + + // parse starting items + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if !line.starts_with(" Starting items: ") { + return Err(ERROR); + } + let mut items = vec![]; + for part in line[18..].split(',') { + items.push(part.trim().parse::().map_err(|_| ERROR)?); + } + line.clear(); + + // parse operation + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if !line.starts_with(" Operation: new = old ") { + return Err(ERROR); + } + let operation = match line[23..].trim() { + "* old" => Operation::Square, + arg if { arg.starts_with("+ ") } => Operation::Add { + arg: arg[2..].parse().map_err(|_| ERROR)?, + }, + arg if { arg.starts_with("* ") } => Operation::Multiply { + arg: arg[2..].parse().map_err(|_| ERROR)?, + }, + _ => return Err(ERROR), + }; + line.clear(); + + // parse modulus + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if !line.starts_with(" Test: divisible by ") { + return Err(ERROR); + } + let modulus = line[21..].trim().parse().map_err(|_| ERROR)?; + line.clear(); + + // parse action if devisible + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if !line.starts_with(" If true: throw to monkey ") { + return Err(ERROR); + } + let target_devisible = line[29..].trim().parse().map_err(|_| ERROR)?; + line.clear(); + + // parse action if indevisible + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if !line.starts_with(" If false: throw to monkey ") { + return Err(ERROR); + } + let target_indevisible = line[30..].trim().parse().map_err(|_| ERROR)?; + line.clear(); + let _ = io::stdin().read_line(&mut line).map_err(|_| ERROR)?; + if line.trim().len() > 0 { + return Err(ERROR); + } + Ok(Some(Self { + items: items, + operation: operation, + modulus: modulus, + target_devisible: target_devisible, + target_indevisible: target_indevisible, + item_count: 0, + })) + } + + fn throw_item(&mut self, divisor: u64) -> Result, &'static str> { + const ERROR: &str = "integer overflow"; + if self.items.len() == 0 { + return Ok(None); + } + let mut item = self.items.remove(0); + match self.operation { + Operation::Add { arg } => item = item.checked_add(arg).ok_or(ERROR)?, + Operation::Multiply { arg } => item = item.checked_mul(arg).ok_or(ERROR)?, + Operation::Square => item = item.checked_mul(item).ok_or(ERROR)?, + }; + item /= divisor; + let target = match item % self.modulus { + 0 => self.target_devisible, + _ => self.target_indevisible, + }; + self.item_count += 1; + Ok(Some((item, target))) + } + + pub fn add_item(&mut self, item: u64) { + self.items.push(item); + } + + pub fn items(&self) -> &[u64] { + &self.items[..] + } + + pub fn item_count(&self) -> usize { + self.item_count + } +} + +impl Game { + pub fn from_stdin(divisor: u64) -> Result { + let mut monkeys = vec![]; + while let Some(monkey) = Monkey::from_stdin()? { + monkeys.push(monkey); + } + Ok(Self { + monkeys: monkeys, + divisor: divisor, + }) + } + + pub fn monkeys(&self) -> &[Monkey] { + &self.monkeys[..] + } +} + +impl Iterator for Game { + type Item = Result; + + fn next<'a>(&'a mut self) -> Option { + for i in 0..self.monkeys.len() { + loop { + match self.monkeys[i].throw_item(self.divisor) { + Ok(None) => break, + Ok(Some((item, target))) => { + self.monkeys[target].add_item(item); + } + Err(e) => return Some(Err(e)), + } + } + } + Some(Ok(Round { + monkeys: self.monkeys.clone(), + })) + } +} + +impl Round { + pub fn monkeys(&self) -> &[Monkey] { + &self.monkeys[..] + } +} diff --git a/day11/input b/day11/input new file mode 100644 index 0000000..f7e9bf8 --- /dev/null +++ b/day11/input @@ -0,0 +1,56 @@ +Monkey 0: + Starting items: 98, 70, 75, 80, 84, 89, 55, 98 + Operation: new = old * 2 + Test: divisible by 11 + If true: throw to monkey 1 + If false: throw to monkey 4 + +Monkey 1: + Starting items: 59 + Operation: new = old * old + Test: divisible by 19 + If true: throw to monkey 7 + If false: throw to monkey 3 + +Monkey 2: + Starting items: 77, 95, 54, 65, 89 + Operation: new = old + 6 + Test: divisible by 7 + If true: throw to monkey 0 + If false: throw to monkey 5 + +Monkey 3: + Starting items: 71, 64, 75 + Operation: new = old + 2 + Test: divisible by 17 + If true: throw to monkey 6 + If false: throw to monkey 2 + +Monkey 4: + Starting items: 74, 55, 87, 98 + Operation: new = old * 11 + Test: divisible by 3 + If true: throw to monkey 1 + If false: throw to monkey 7 + +Monkey 5: + Starting items: 90, 98, 85, 52, 91, 60 + Operation: new = old + 7 + Test: divisible by 5 + If true: throw to monkey 0 + If false: throw to monkey 4 + +Monkey 6: + Starting items: 99, 51 + Operation: new = old + 1 + Test: divisible by 13 + If true: throw to monkey 5 + If false: throw to monkey 2 + +Monkey 7: + Starting items: 98, 94, 59, 76, 51, 65, 75 + Operation: new = old + 5 + Test: divisible by 2 + If true: throw to monkey 3 + If false: throw to monkey 6 + diff --git a/day11/part1.rs b/day11/part1.rs new file mode 100644 index 0000000..1b387d5 --- /dev/null +++ b/day11/part1.rs @@ -0,0 +1,36 @@ +// Copyright 2022 Christian Ulrich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// usage: ./part1 < input + +pub mod common; + +use common::{Game, Monkey}; + +fn main() -> Result<(), &'static str> { + let game = Game::from_stdin(3)?; + let last_round = game.take(20).last().unwrap(); + let mut item_counts: Vec<_> = last_round? + .monkeys() + .iter() + .map(Monkey::item_count) + .collect(); + item_counts.sort(); + println!( + "{}", + item_counts[item_counts.len() - 1] * item_counts[item_counts.len() - 2] + ); + Ok(()) +} diff --git a/day11/testinput b/day11/testinput new file mode 100644 index 0000000..477d33b --- /dev/null +++ b/day11/testinput @@ -0,0 +1,28 @@ +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 +