1use utils::prelude::*;
2
3#[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]);