use std::collections::VecDeque; use nom::{ character::complete::{self, multispace1, one_of, multispace0}, multi::{separated_list1, many1}, bytes::complete::tag, sequence::{delimited, preceded}, branch::alt, *, }; #[derive(Debug)] enum Value { Old, Val(u64), } use Value::*; #[derive(Debug)] enum Operation { Mult(Value), Add(Value), } use Operation::*; #[derive(Debug)] pub struct Test { divisible_by: u64, on_false: usize, on_true: usize, } #[derive(Debug)] pub struct Monkey { items: VecDeque, operation: Operation, test: Test, number_of_inspections: u64, } impl Monkey { pub fn handle_items(&mut self, magic_number: u64) -> Vec<(u64, usize)> { let mut result = Vec::new(); while !self.items.is_empty() { self.number_of_inspections += 1; let mut item = self.items.pop_front().unwrap(); item = match self.operation { Mult(Old) => {item * item}, Mult(Val(num)) => {item * num}, Add(Old) => {item * 2}, Add(Val(num)) => {item + num}, }; // item /= 3; item = item % magic_number; let throw_to_monkey = match item % self.test.divisible_by == 0 { true => self.test.on_true, false => self.test.on_false, }; result.push((item, throw_to_monkey)); } result } } fn parse_operation(input: &str) -> IResult<&str, Operation> { let (input, op) = preceded( tag("Operation: new = old "), one_of("+*"), )(input)?; let (input, num) : (&str, Value) = preceded( multispace1, alt(( complete::u64.map(|num| Value::Val(num)), tag("old").map(|_| Value::Old), )), )(input)?; let func = match op { '*' => Operation::Mult(num), '+' => Operation::Add(num), _ => unreachable!(), }; Ok((input, func)) } fn parse_test(input: &str) -> IResult<&str, Test> { let (input, divisible_by) = preceded( tag("Test: divisible by "), complete::u64, )(input)?; let (input, _) = multispace1(input)?; let (input, on_true) = preceded( tag("If true: throw to monkey "), complete::u32, )(input)?; let (input, _) = multispace1(input)?; let (input, on_false) = preceded( tag("If false: throw to monkey "), complete::u32, )(input)?; // Generate Test let t = Test { divisible_by, on_false: on_false as usize, on_true: on_true as usize, }; Ok((input, t)) } fn parse_input(input: &str) -> IResult<&str, Monkey> { // Parse: Monkey _id: let (input, _id) = delimited( tag("Monkey "), complete::u32, tag(":"), )(input)?; let (input, _) = multispace1(input)?; // Parse: Starting items: num1, num2 let (input, levels) = preceded( tag("Starting items: "), separated_list1(tag(", "), complete::u64), )(input)?; let (input, _) = multispace1(input)?; //Parse: Operation: new = old {+ num|old}, {* num|old} let (input, operation) = parse_operation(input)?; let (input, _) = multispace1(input)?; //Parse: Test: divisible by num // If true: throw to monkey # // If false: throw to monkey # let (input, test) = parse_test(input)?; let (input, _) = multispace0(input)?; // Generate a monkey here let items = VecDeque::from_iter(levels); let m = Monkey { items, operation, test, number_of_inspections: 0, }; Ok((input, m)) } fn part1and2() { let input_text = include_str!("../input.txt"); let (_, mut monkeys) = many1(parse_input)(input_text).unwrap(); let magic_number = monkeys .iter() .map(|m| m.test.divisible_by) .product(); for _i in 0..10000 { for monkey_index in 0..monkeys.len() { let monkey = monkeys.get_mut(monkey_index).unwrap(); let new_item_list = monkey.handle_items(magic_number); for (item, thrown_to_monkey) in new_item_list { monkeys[thrown_to_monkey].items.push_back(item); } } } let mut results = monkeys .iter() .map(|m| m.number_of_inspections) .collect::>(); results.sort_by(|a, b| b.cmp(a)); println!("{} * {} = {}", results[0], results[1], results[0] * results[1]); } fn main() { part1and2(); }