use std::{str::FromStr, collections::VecDeque}; use regex::Regex; use simple_error::{bail, SimpleError}; #[derive(Debug)] enum Op { Add, Sub, Mul, Div } #[derive(Debug)] enum Var { C(u64), A } #[derive(Debug)] struct Monkey { items: VecDeque, op: Op, arg1: Var, arg2: Var, test: u64, // divisible by this number target_true: usize, // monkey to throw to on true target_false: usize, // monkey to throw to on false inspections: usize, // how many inspections has this monkey made? } impl Monkey { fn op(self: &Monkey, x: u64) -> u64 { let a: u64 = match self.arg1 { Var::C(c) => c, Var::A => x, }; let b = match self.arg2 { Var::C(c)=> c, Var::A => x, }; match self.op { Op::Add => a + b, Op::Sub => a - b, Op::Mul => a * b, Op::Div => a / b, } } fn test(self: &Monkey, x: u64) -> bool { x % self.test == 0 } fn business(monkeys: &mut [Monkey]) { let worry_level_modulo: u64 = monkeys.iter().map(|m| {m.test}).product(); for ix in 0..monkeys.len() { // TODO can probably change this, use a block // to scope the mutable borrow of the first monkey, // and give it back before mutably borrowing the second. // Instead use nightly API usage to get several mutable monkeys let idxs = [ix, monkeys[ix].target_true, monkeys[ix].target_false]; let [m, mut mtt, mut mtf] = monkeys.get_many_mut(idxs).unwrap(); // Start turn // Inspect all items, one at a time, from the start of the list while let Some(mut i) = m.items.pop_front() { // Increase the count of inspections this monkey has made m.inspections += 1; // Worry level increases... i = m.op(i); // Worry level decreases... // Uh oh, not any more! //i = i / 3; // instead, modulo the worry level by the ... // prod of all the monkeys divisors? to prevent integer overflow, // without changing the result i = i % worry_level_modulo; // Test the item, decide who it goes to let target = if m.test(i) { &mut mtt } else { &mut mtf }; // Throw it to the other monkey, on the end of their list target.items.push_back(i); } } } } fn nfrg(c: regex::Captures, index:usize) -> Result { let x = c.get(index).ok_or(SimpleError::new("missed capture group"))?; x.as_str().parse().map_err(|_| { SimpleError::new("wasn't a number")}) } impl FromStr for Monkey { type Err = simple_error::SimpleError; fn from_str(s: &str) -> Result { let ll: Vec<&str> = s.lines().collect(); if ll.len() != 6 { bail!("expected 6 lines for a monkey!"); } // Parse the starting items line let r1 = Regex::new(r"^ Starting items: ([0-9, ]+)$").unwrap(); let l1c = r1.captures(ll[1]).ok_or(SimpleError::new("starting regex no match"))?; let l1d = l1c.get(1).ok_or(SimpleError::new("starting no group"))?; let items: VecDeque = l1d.as_str().split(", ").map(|t| { t.parse() }) .try_collect().map_err(|_| SimpleError::new("error parsing items"))?; // Parse the operation / args line let r2 = Regex::new(r"^ Operation: new = ([a-z0-9]+) ([+\-*/]) ([a-z0-9]+)$").unwrap(); let l2c = r2.captures(ll[2]).ok_or(SimpleError::new("operation/args didn't match regex"))?; let l2t1 = l2c.get(1).ok_or(SimpleError::new("error parsing op"))?; let l2t2 = l2c.get(2).ok_or(SimpleError::new("error parsing op"))?; let l2t3 = l2c.get(3).ok_or(SimpleError::new("error parsing op"))?; let arg1 = match l2t1.as_str() { "old" => Ok(Var::A), st => st.parse().map(|i| {Var::C(i)}) }.map_err(|_| {SimpleError::new("error parsing arg1")})?; let op = match l2t2.as_str() { "+" => Ok(Op::Add), "-" => Ok(Op::Sub), "*" => Ok(Op::Mul), "/" => Ok(Op::Div), _ => Err(SimpleError::new("unexpected operator")) }?; let arg2 = match l2t3.as_str() { "old" => Ok(Var::A), st => st.parse().map(|i| {Var::C(i)}) }.map_err(|_| {SimpleError::new("error parsing arg1")})?; // Parse the test line let r3 = Regex::new(r"^ Test: divisible by (\d+)$").unwrap(); let l3c = r3.captures(ll[3]).ok_or(SimpleError::new("no match line 3"))?; let test = nfrg(l3c, 1)?; // Parse the target lines let r4 = Regex::new(r"^ If true: throw to monkey (\d+)$").unwrap(); let l4c = r4.captures(ll[4]).ok_or(SimpleError::new("no match line 4"))?; let target_true = nfrg(l4c, 1)?; let r5 = Regex::new(r"^ If false: throw to monkey (\d+)$").unwrap(); let l5c = r5.captures(ll[5]).ok_or(SimpleError::new("no match line 5"))?; let target_false = nfrg(l5c, 1)?; let inspections = 0; Ok(Monkey{ items, op, arg1, arg2, test, target_true, target_false, inspections, }) } } pub fn run(input: String) { let mut monkeys: Vec = input.split("\n\n") .map(|s| {Monkey::from_str(s).expect("couldn't parse monkey!")}) .collect(); //println!("{:?}", monkeys); //for _ in 0..20 { for _ in 0..10000 { Monkey::business(&mut monkeys); } monkeys.sort_by(|a, b| { a.inspections.cmp(&b.inspections).reverse() }); let m1 = &monkeys[0]; let m2 = &monkeys[1]; println!("Day 11: {}", m1.inspections*m2.inspections); }