advent_of_code/year2016/
day10.rs

1use crate::input::Input;
2
3type BotId = u8;
4
5#[derive(Copy, Clone)]
6enum OnDone {
7    GiveTo(BotId),
8    OutputTo(u8),
9}
10
11#[derive(Copy, Clone)]
12struct Bot {
13    low_to: OnDone,
14    high_to: OnDone,
15    received_chip: Option<u8>,
16}
17
18impl Default for Bot {
19    fn default() -> Self {
20        Self {
21            low_to: OnDone::OutputTo(0),
22            high_to: OnDone::OutputTo(0),
23            received_chip: None,
24        }
25    }
26}
27
28fn receive(
29    bots: &mut [Bot; 256],
30    first_three_outputs: &mut [u8; 3],
31    microchip: u8,
32    to_bot: BotId,
33    part1: bool,
34) -> Option<BotId> {
35    let bot = &mut bots[usize::from(to_bot)];
36
37    if let Some(first_microchip) = bot.received_chip {
38        let low_microchip = std::cmp::min(first_microchip, microchip);
39        let high_microchip = std::cmp::max(first_microchip, microchip);
40
41        if part1 && (low_microchip, high_microchip) == (17, 61) {
42            return Some(to_bot);
43        }
44
45        let low_to = bot.low_to;
46        let high_to = bot.high_to;
47
48        match low_to {
49            OnDone::GiveTo(recipient) => {
50                let desired_bot =
51                    receive(bots, first_three_outputs, low_microchip, recipient, part1);
52                if desired_bot.is_some() {
53                    return desired_bot;
54                }
55            }
56            OnDone::OutputTo(output_idx) => {
57                if output_idx < 3 {
58                    first_three_outputs[output_idx as usize] = low_microchip;
59                }
60            }
61        }
62
63        match high_to {
64            OnDone::GiveTo(recipient) => {
65                let desired_bot =
66                    receive(bots, first_three_outputs, high_microchip, recipient, part1);
67                if desired_bot.is_some() {
68                    return desired_bot;
69                }
70            }
71            OnDone::OutputTo(output_idx) => {
72                if output_idx < 3 {
73                    first_three_outputs[output_idx as usize] = high_microchip;
74                }
75            }
76        }
77    } else {
78        bot.received_chip = Some(microchip);
79    }
80
81    None
82}
83
84pub fn solve(input: &Input) -> Result<u32, String> {
85    let error_mapper = |_| "Invalid input";
86    let mut bots = [Bot::default(); 256];
87    let mut initial_values = Vec::new();
88
89    for line in input.text.lines() {
90        let parts = line.split(' ').collect::<Vec<_>>();
91        if parts[0] == "value" {
92            // "value X goes to bot Y"
93            let value = parts[1].parse::<u8>().map_err(error_mapper)?;
94            let to_bot_id = parts[5].parse::<BotId>().map_err(error_mapper)?;
95            initial_values.push((value, to_bot_id));
96        } else if parts[0] == "bot" {
97            // "bot X gives low to bot|output Y and high to bot|output Z"
98            let bot_id = parts[1].parse::<u8>().map_err(error_mapper)?;
99            let low_to_number = parts[6].parse::<u8>().map_err(error_mapper)?;
100            let high_to_number = parts[11].parse::<u8>().map_err(error_mapper)?;
101
102            let low_to = if parts[5] == "bot" {
103                OnDone::GiveTo(low_to_number)
104            } else {
105                OnDone::OutputTo(low_to_number)
106            };
107
108            let high_to = if parts[10] == "bot" {
109                OnDone::GiveTo(high_to_number)
110            } else {
111                OnDone::OutputTo(high_to_number)
112            };
113
114            let bot = Bot {
115                low_to,
116                high_to,
117                received_chip: None,
118            };
119
120            bots[usize::from(bot_id)] = bot;
121        } else {
122            return Err("Invalid input".to_string());
123        }
124    }
125
126    let mut first_three_outputs = [0_u8; 3];
127    for &(value, to_bot_id) in &initial_values {
128        if let Some(desired_bot_id) = receive(
129            &mut bots,
130            &mut first_three_outputs,
131            value,
132            to_bot_id,
133            input.is_part_one(),
134        ) {
135            return Ok(u32::from(desired_bot_id));
136        }
137    }
138
139    if input.is_part_one() {
140        Err("Not bot comparing chips 17 and 61".to_string())
141    } else {
142        Ok(first_three_outputs.iter().map(|&v| u32::from(v)).product())
143    }
144}
145
146#[test]
147pub fn tests() {
148    use crate::input::{test_part_one, test_part_two};
149
150    let real_input = include_str!("day10_input.txt");
151    test_part_one!(real_input => 98);
152    test_part_two!(real_input => 4042);
153}