1use crate::intcode::features::Day09Features;
2use crate::intcode::{Event, Interpreter};
3use utils::prelude::*;
4
5#[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) []);