use std::num::ParseIntError; use std::env; use std::fs::File; use std::io::{self, BufRead, Lines}; use std::collections::VecDeque; #[derive(Debug)] enum Operator { Add, Mul } #[derive(Debug)] enum Operand { Old, Imm(u64) } #[derive(Debug)] struct Operation { left: Operand, op: Operator, right: Operand } impl Operation { fn perform(&self, old: u64) -> u64 { let left = match self.left { Operand::Old => old, Operand::Imm(x) => x, }; let right = match self.right { Operand::Old => old, Operand::Imm(x) => x, }; match self.op { Operator::Add => left + right, Operator::Mul => left * right, } } } #[derive(Debug)] struct Monkey { id: u64, items: VecDeque, operation: Operation, div_by_test: u64, true_throw: u64, false_throw: u64, } #[derive(Debug)] enum MonkeyError { ParseError(), ParseIntError(ParseIntError), IoError(io::Error), } impl From for MonkeyError { fn from(e: ParseIntError) -> Self { MonkeyError::ParseIntError(e) } } impl From for MonkeyError { fn from(e: io::Error) -> Self { MonkeyError::IoError(e) } } fn parse_monkey( lines: &mut Lines ) -> Result, MonkeyError> { let id = match lines.next() { Some(line) => line? .strip_prefix("Monkey ").ok_or(MonkeyError::ParseError())? .strip_suffix(":").ok_or(MonkeyError::ParseError())? .parse::()?, None => return Ok(None), }; let starting_items = lines.next().ok_or(MonkeyError::ParseError())?? .strip_prefix(" Starting items: ").ok_or(MonkeyError::ParseError())? .split(", ") .map(|item| item.parse::()) .collect::, _>>()?; let op_line = lines.next().ok_or(MonkeyError::ParseError())??; let op_vec = op_line .strip_prefix(" Operation: new = ").ok_or(MonkeyError::ParseError())? .split(' ').collect::>(); let (left, operator, right) = (op_vec[0], op_vec[1], op_vec[2]); let operation = Operation { left: match left { "old" => Operand::Old, _ => Operand::Imm(left.parse()?) }, op: match operator { "+" => Ok(Operator::Add), "*" => Ok(Operator::Mul), _ => Err(MonkeyError::ParseError()) }?, right: match right { "old" => Operand::Old, _ => Operand::Imm(right.parse()?) }, }; let test_val = lines.next().ok_or(MonkeyError::ParseError())?? .strip_prefix(" Test: divisible by ").ok_or(MonkeyError::ParseError())? .parse::()?; let true_throw = lines.next().ok_or(MonkeyError::ParseError())?? .strip_prefix(" If true: throw to monkey ") .ok_or(MonkeyError::ParseError())? .parse::()?; let false_throw = lines.next().ok_or(MonkeyError::ParseError())?? .strip_prefix(" If false: throw to monkey ") .ok_or(MonkeyError::ParseError())? .parse::()?; // Remove empty line if exists match lines.next() { None => Ok(()), Some(Err(e)) => Err(e), Some(Ok(s)) => Ok(match s.as_str() { "" => Ok(()), _ => Err(MonkeyError::ParseError()), }?), }?; Ok(Some(Monkey { id: id, items: starting_items, operation: operation, div_by_test: test_val, true_throw: true_throw, false_throw: false_throw, }) )} fn main() { let file = File::open(&env::args().nth(1).unwrap()).unwrap(); let mut lines = io::BufReader::new(file).lines(); let mut monkeys: Vec = Vec::new(); loop { match parse_monkey(&mut lines) { Ok(None) => break, Ok(Some(m)) => { assert!(m.id == monkeys.len() as u64); monkeys.push(m) }, Err(e) => eprintln!("{:?}", e), } } let mut monkey_item_counts = vec![0; monkeys.len()]; for _round in 1..=20 { for i in 0..monkeys.len() { monkey_item_counts[i] += monkeys[i].items.len(); for _item in 0..monkeys[i].items.len() { let old_item = monkeys[i].items.pop_front().unwrap(); let new_item = monkeys[i].operation.perform(old_item) / 3; let new_i = if new_item % monkeys[i].div_by_test == 0 { monkeys[i].true_throw } else { monkeys[i].false_throw } as usize; monkeys[new_i].items.push_back(new_item); } } } monkey_item_counts.sort_by(|x, y| y.cmp(x)); println!("{:?}", monkey_item_counts[0]*monkey_item_counts[1]); }