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 }