summaryrefslogtreecommitdiff
path: root/src/day11.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/day11.rs')
-rw-r--r--src/day11.rs159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/day11.rs b/src/day11.rs
new file mode 100644
index 0000000..8040ccb
--- /dev/null
+++ b/src/day11.rs
@@ -0,0 +1,159 @@
+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<u64>,
+ 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<T: FromStr>(c: regex::Captures, index:usize) -> Result<T, SimpleError> {
+ 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<Self, Self::Err> {
+ 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<u64> = 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<Monkey> = 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);
+} \ No newline at end of file