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
use std::collections::HashMap;
use utils::prelude::*;

/// Evaluating conditional add instructions.
#[derive(Clone, Debug)]
pub struct Day08 {
    part1: i32,
    part2: i32,
}

impl Day08 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        let parse_iterator = parser::take_while1(u8::is_ascii_lowercase)
            .with_suffix(" ")
            .then(
                parser::one_of((
                    parser::i32().with_prefix("inc "),
                    parser::i32().with_prefix("dec ").map(|x| -x),
                ))
                .with_suffix(" if "),
            )
            .then(parser::take_while1(u8::is_ascii_lowercase).with_suffix(" "))
            .then(
                parser::literal_map!(
                    "==" => i32::eq as fn(&i32, &i32) -> bool,
                    "!=" => i32::ne as fn(&i32, &i32) -> bool,
                    "<=" => i32::le as fn(&i32, &i32) -> bool,
                    ">=" => i32::ge as fn(&i32, &i32) -> bool,
                    "<" => i32::lt as fn(&i32, &i32) -> bool,
                    ">" => i32::gt as fn(&i32, &i32) -> bool,
                )
                .with_suffix(" "),
            )
            .then(parser::i32())
            .with_suffix(parser::eol())
            .parse_iterator(input);

        let mut registers = HashMap::new();
        let mut max = 0;
        for item in parse_iterator {
            let (reg, value, cond_reg, comparison, cond_value) = item?;
            if comparison(registers.entry(cond_reg).or_insert(0), &cond_value) {
                let entry = registers.entry(reg).or_insert(0);
                *entry += value;
                max = max.max(*entry);
            }
        }

        Ok(Self {
            part1: registers.into_values().max().unwrap_or(0),
            part2: max,
        })
    }

    #[must_use]
    pub fn part1(&self) -> i32 {
        self.part1
    }

    #[must_use]
    pub fn part2(&self) -> i32 {
        self.part2
    }
}

examples!(Day08 -> (i32, i32) [
    {
        input: "b inc 5 if a > 1\n\
            a inc 1 if b < 5\n\
            c dec -10 if a >= 1\n\
            c inc -20 if c == 10",
        part1: 1,
        part2: 10
    },
]);