year2016/
day10.rs

1use utils::prelude::*;
2
3/// Comparing chip values.
4#[derive(Clone, Debug)]
5pub struct Day10 {
6    part1: u8,
7    part2: u32,
8}
9
10#[derive(Copy, Clone, Debug)]
11enum Instruction {
12    Value { value: u8, bot: u8 },
13    Bot { num: u8, low: Output, high: Output },
14}
15
16#[derive(Copy, Clone, Debug)]
17struct Bot {
18    num: u8,
19    low: Output,
20    high: Output,
21    value: Option<u8>,
22}
23
24#[derive(Copy, Clone, Debug)]
25enum Output {
26    Bot(u8),
27    Output(u8),
28}
29
30impl Day10 {
31    pub fn new(input: &str, input_type: InputType) -> Result<Self, InputError> {
32        let output = parser::u8()
33            .with_prefix("bot ")
34            .map(Output::Bot)
35            .or(parser::u8().with_prefix("output ").map(Output::Output));
36        let bot = parser::u8()
37            .with_prefix("bot ")
38            .then(output.with_prefix(" gives low to "))
39            .then(output.with_prefix(" and high to "))
40            .map(|(num, low, high)| Instruction::Bot { num, low, high });
41        let value = parser::u8()
42            .with_prefix("value ")
43            .with_suffix(" goes to bot ")
44            .then(parser::u8())
45            .map(|(value, bot)| Instruction::Value { value, bot });
46
47        let instructions = bot.or(value).parse_lines(input)?;
48
49        let mut bots = instructions
50            .iter()
51            .filter_map(|&instruction| match instruction {
52                Instruction::Value { .. } => None,
53                Instruction::Bot { num, low, high } => Some(Bot {
54                    num,
55                    low,
56                    high,
57                    value: None,
58                }),
59            })
60            .collect::<Vec<_>>();
61        bots.sort_unstable_by_key(|b| b.num);
62        if let Some((i, _)) = bots.iter().enumerate().find(|&(i, b)| i != b.num as usize) {
63            return Err(InputError::new(input, input, format!("bot {i} missing")));
64        }
65
66        let mut part1 = 0;
67        let mut compare_fn = |index, min, max| {
68            if (input_type == InputType::Real && min == 17 && max == 61)
69                || (input_type == InputType::Example && min == 2 && max == 5)
70            {
71                part1 = index;
72            }
73        };
74
75        let mut part2 = 1;
76        let mut output_fn = |index, value| {
77            if index <= 2 {
78                part2 *= value as u32;
79            }
80        };
81
82        for &instruction in &instructions {
83            if let Instruction::Value { bot, value } = instruction {
84                Self::give(bot, value, &mut bots, &mut compare_fn, &mut output_fn);
85            }
86        }
87
88        Ok(Self { part1, part2 })
89    }
90
91    fn give(
92        bot: u8,
93        value: u8,
94        bots: &mut [Bot],
95        compare_fn: &mut impl FnMut(u8, u8, u8),
96        output_fn: &mut impl FnMut(u8, u8),
97    ) {
98        let bot = &mut bots[bot as usize];
99        if let Some(value2) = bot.value.take() {
100            let min = value.min(value2);
101            let max = value.max(value2);
102            compare_fn(bot.num, min, max);
103
104            for (output, value) in [(bot.low, min), (bot.high, max)] {
105                match output {
106                    Output::Bot(index) => Self::give(index, value, bots, compare_fn, output_fn),
107                    Output::Output(index) => output_fn(index, value),
108                }
109            }
110        } else {
111            bot.value = Some(value);
112        }
113    }
114
115    #[must_use]
116    pub fn part1(&self) -> u8 {
117        self.part1
118    }
119
120    #[must_use]
121    pub fn part2(&self) -> u32 {
122        self.part2
123    }
124}
125
126examples!(Day10 -> (u8, u32) [
127    {
128        input: "value 5 goes to bot 2\n\
129            bot 2 gives low to bot 1 and high to bot 0\n\
130            value 3 goes to bot 1\n\
131            bot 1 gives low to output 1 and high to bot 0\n\
132            bot 0 gives low to output 2 and high to output 0\n\
133            value 2 goes to bot 2",
134        part1: 2,
135        part2: 30,
136    },
137]);