Skip to main content

year2019/
day23.rs

1use crate::intcode::features::Day09Features;
2use crate::intcode::{Event, Interpreter};
3use utils::prelude::*;
4
5/// Interpreting machine code and routing packets between instances.
6#[derive(Clone, Debug)]
7pub struct Day23 {
8    part1: i64,
9    part2: i64,
10}
11
12const COMPUTER_COUNT: usize = 50;
13const NAT_ADDRESS: usize = 255;
14
15impl Day23 {
16    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
17        let base = Interpreter::parse(input, 1)?;
18        let mut computers: [Interpreter; COMPUTER_COUNT] = std::array::from_fn(|address| {
19            let mut computer = base.clone();
20            computer.push_input(address as i64);
21            computer
22        });
23
24        let mut pending_indexes = (0..COMPUTER_COUNT).rev().collect::<Vec<_>>();
25        let mut part1 = None;
26        let mut nat_packet = None;
27        let mut last_nat_y = None;
28
29        loop {
30            let index = match pending_indexes.pop() {
31                Some(index) => index,
32                None if let Some((_, y)) = nat_packet
33                    && Some(y) == last_nat_y =>
34                {
35                    return Ok(Self {
36                        part1: part1.expect("packet has been sent to NAT address"),
37                        part2: y,
38                    });
39                }
40                None if let Some((x, y)) = nat_packet => {
41                    last_nat_y = Some(y);
42                    computers[0].input.extend([x, y]);
43                    0
44                }
45                None => {
46                    return Err(InputError::new(
47                        input,
48                        0,
49                        "expected packet to be sent to the NAT before the network became idle",
50                    ));
51                }
52            };
53
54            let mut sent_idle = false;
55            loop {
56                match computers[index].run::<Day09Features>() {
57                    Event::Halt => {
58                        return Err(InputError::new(
59                            input,
60                            0,
61                            "expected program to output or request input, but it halted",
62                        ));
63                    }
64                    Event::Input if sent_idle => break,
65                    Event::Input => {
66                        computers[index].push_input(-1);
67                        sent_idle = true;
68                    }
69                    Event::Output(address) => {
70                        let mut next_output = || match computers[index].run::<Day09Features>() {
71                            Event::Output(value) => Ok(value),
72                            Event::Halt | Event::Input => Err(InputError::new(
73                                input,
74                                0,
75                                "expected program to output three values",
76                            )),
77                        };
78                        let (x, y) = (next_output()?, next_output()?);
79
80                        match usize::try_from(address) {
81                            Ok(address @ 0..COMPUTER_COUNT) => {
82                                let was_idle = computers[address].input.is_empty();
83                                computers[address].input.extend([x, y]);
84                                if address != index && was_idle {
85                                    pending_indexes.push(address);
86                                }
87                            }
88                            Ok(NAT_ADDRESS) => {
89                                part1.get_or_insert(y);
90                                nat_packet = Some((x, y));
91                            }
92                            _ => {
93                                return Err(InputError::new(
94                                    input,
95                                    0,
96                                    "expected packet address from 0 to 49 or 255",
97                                ));
98                            }
99                        }
100                    }
101                }
102            }
103        }
104    }
105
106    #[must_use]
107    pub fn part1(&self) -> i64 {
108        self.part1
109    }
110
111    #[must_use]
112    pub fn part2(&self) -> i64 {
113        self.part2
114    }
115}
116
117examples!(Day23 -> (i64, i64) []);