aoc2022

Advent of Code 2022 solutions in Rust
git clone git://code.mfashby.net:/aoc2022
Log | Files | Refs

day11.rs (5995B)


      1 use std::{str::FromStr, collections::VecDeque};
      2 
      3 use regex::Regex;
      4 use simple_error::{bail, SimpleError};
      5 
      6 #[derive(Debug)]
      7 enum Op { Add, Sub, Mul, Div }
      8 
      9 #[derive(Debug)]
     10 enum Var { C(u64), A }
     11 
     12 #[derive(Debug)]
     13 struct Monkey {
     14     items: VecDeque<u64>,
     15     op: Op,
     16     arg1: Var,
     17     arg2: Var,
     18     test: u64, // divisible by this number
     19     target_true: usize, // monkey to throw to on true
     20     target_false: usize, // monkey to throw to on false
     21 
     22     inspections: usize, // how many inspections has this monkey made?
     23 }
     24 
     25 impl Monkey {
     26     fn op(self: &Monkey, x: u64) -> u64 {
     27         let a: u64 = match self.arg1 {
     28             Var::C(c) => c,
     29             Var::A => x,
     30         };
     31         let b = match self.arg2 {
     32             Var::C(c)=> c,
     33             Var::A => x,
     34         };
     35         match self.op {
     36             Op::Add => a + b,
     37             Op::Sub => a - b,
     38             Op::Mul => a * b,
     39             Op::Div => a / b,
     40         }
     41     }
     42     fn test(self: &Monkey, x: u64) -> bool {
     43         x % self.test == 0
     44     }
     45     fn business(monkeys: &mut [Monkey]) {
     46         let worry_level_modulo: u64 = monkeys.iter().map(|m| {m.test}).product();
     47         for ix in 0..monkeys.len() {
     48             // TODO can probably change this, use a block 
     49             // to scope the mutable borrow of the first monkey, 
     50             // and give it back before mutably borrowing the second.
     51             // Instead use nightly API usage to get several mutable monkeys
     52             let idxs = [ix, monkeys[ix].target_true, monkeys[ix].target_false];
     53             let [m, mut mtt, mut mtf] = monkeys.get_many_mut(idxs).unwrap();
     54             // Start turn
     55             // Inspect all items, one at a time, from the start of the list
     56             while let Some(mut i) = m.items.pop_front() {
     57                 // Increase the count of inspections this monkey has made
     58                 m.inspections += 1;
     59                 // Worry level increases...
     60                 i = m.op(i);
     61                 // Worry level decreases...
     62                 // Uh oh, not any more!
     63                 //i = i / 3;
     64                 // instead, modulo the worry level by the ... 
     65                 // prod of all the monkeys divisors? to prevent integer overflow, 
     66                 // without changing the result
     67                 i = i % worry_level_modulo;
     68                 // Test the item, decide who it goes to
     69                 let target = if m.test(i) { &mut mtt } else { &mut mtf };
     70                 // Throw it to the other monkey, on the end of their list
     71                 target.items.push_back(i);
     72             }
     73         }
     74     }
     75 }
     76 
     77 fn nfrg<T: FromStr>(c: regex::Captures, index:usize) -> Result<T, SimpleError> {
     78     let x = c.get(index).ok_or(SimpleError::new("missed capture group"))?;
     79     x.as_str().parse().map_err(|_| { SimpleError::new("wasn't a number")})
     80 }
     81 
     82 impl FromStr for Monkey {
     83     type Err = simple_error::SimpleError;
     84 
     85     fn from_str(s: &str) -> Result<Self, Self::Err> {
     86         let ll: Vec<&str> = s.lines().collect();
     87         if ll.len() != 6 {
     88             bail!("expected 6 lines for a monkey!");
     89         }
     90         // Parse the starting items line
     91         let r1 = Regex::new(r"^  Starting items: ([0-9, ]+)$").unwrap();
     92         let l1c = r1.captures(ll[1]).ok_or(SimpleError::new("starting regex no match"))?;
     93         let l1d = l1c.get(1).ok_or(SimpleError::new("starting no group"))?;
     94         let items: VecDeque<u64> = l1d.as_str().split(", ").map(|t| { t.parse() })
     95             .try_collect().map_err(|_| SimpleError::new("error parsing items"))?;
     96 
     97         // Parse the operation / args line
     98         let r2 = Regex::new(r"^  Operation: new = ([a-z0-9]+) ([+\-*/]) ([a-z0-9]+)$").unwrap();
     99         let l2c = r2.captures(ll[2]).ok_or(SimpleError::new("operation/args didn't match regex"))?;
    100         let l2t1 = l2c.get(1).ok_or(SimpleError::new("error parsing op"))?;
    101         let l2t2 = l2c.get(2).ok_or(SimpleError::new("error parsing op"))?;
    102         let l2t3 = l2c.get(3).ok_or(SimpleError::new("error parsing op"))?;
    103         let arg1 = match l2t1.as_str() {
    104             "old" => Ok(Var::A),
    105             st => st.parse().map(|i| {Var::C(i)})
    106         }.map_err(|_| {SimpleError::new("error parsing arg1")})?;
    107         let op = match l2t2.as_str() {
    108             "+" => Ok(Op::Add),
    109             "-" => Ok(Op::Sub),
    110             "*" => Ok(Op::Mul),
    111             "/" => Ok(Op::Div),
    112             _ => Err(SimpleError::new("unexpected operator"))
    113         }?;
    114         let arg2 = match l2t3.as_str() {
    115             "old" => Ok(Var::A),
    116             st => st.parse().map(|i| {Var::C(i)})
    117         }.map_err(|_| {SimpleError::new("error parsing arg1")})?;
    118 
    119         // Parse the test line
    120         let r3 = Regex::new(r"^  Test: divisible by (\d+)$").unwrap();
    121         let l3c = r3.captures(ll[3]).ok_or(SimpleError::new("no match line 3"))?;
    122         let test = nfrg(l3c, 1)?;
    123 
    124         // Parse the target lines
    125         let r4 = Regex::new(r"^    If true: throw to monkey (\d+)$").unwrap();
    126         let l4c = r4.captures(ll[4]).ok_or(SimpleError::new("no match line 4"))?;
    127         let target_true = nfrg(l4c, 1)?;
    128         let r5 = Regex::new(r"^    If false: throw to monkey (\d+)$").unwrap();
    129         let l5c = r5.captures(ll[5]).ok_or(SimpleError::new("no match line 5"))?;
    130         let target_false = nfrg(l5c, 1)?;
    131 
    132         let inspections = 0;
    133         Ok(Monkey{
    134             items,
    135             op,
    136             arg1,
    137             arg2,
    138             test,
    139             target_true,
    140             target_false,
    141             inspections,
    142         })
    143     }
    144 }
    145 
    146 pub fn run(input: String) {
    147     let mut monkeys: Vec<Monkey> = input.split("\n\n")
    148         .map(|s| {Monkey::from_str(s).expect("couldn't parse monkey!")})
    149         .collect();
    150     //println!("{:?}", monkeys);
    151     //for _ in 0..20 {
    152     for _ in 0..10000 {
    153         Monkey::business(&mut monkeys);
    154     }
    155     monkeys.sort_by(|a, b| { a.inspections.cmp(&b.inspections).reverse() });
    156     let m1 = &monkeys[0];
    157     let m2 = &monkeys[1];
    158     println!("Day 11: {}", m1.inspections*m2.inspections);
    159 }