summaryrefslogtreecommitdiff
path: root/src/day11.rs
blob: 8040ccb7ac5675e3d857fd6775b3ef585b37c574 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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);
}