1use std::collections::VecDeque;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day18 {
7 instructions: Vec<Instruction>,
8}
9
10#[derive(Copy, Clone, Debug, PartialEq)]
13enum Register {
14 A,
15 B,
16 C,
17 D,
18 F,
19 I,
20 P,
21}
22
23#[derive(Copy, Clone, Debug, PartialEq)]
26enum Instruction {
27 Snd(Register),
28 SndN(i32), Set(Register, Register),
30 SetN(Register, i32),
31 Add(Register, Register),
32 AddN(Register, i32),
33 Mul(Register, Register), MulN(Register, i32),
35 Mod(Register, Register),
36 ModN(Register, i32),
37 Rcv(Register),
38 Jgz(Register, Register),
39 JgzN(Register, i32),
40 Jmp(i32), Noop(), }
43
44#[derive(Debug)]
45struct Program<'a> {
46 instructions: &'a [Instruction],
47 pc: usize,
48 reg: [i64; 7],
49}
50
51impl Day18 {
52 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
53 let register = parser::literal_map!(
54 "a" => Register::A,
55 "b" => Register::B,
56 "c" => Register::C,
57 "d" => Register::D,
58 "f" => Register::F,
59 "i" => Register::I,
60 "p" => Register::P,
61 );
62
63 let instruction = parser::parse_tree!(
64 ("snd ") =>> {
65 (r @ register) => Instruction::Snd(r),
66 (v @ parser::i32()) => Instruction::SndN(v),
67 },
68 ("set ", r @ register, b' ') =>> {
69 (r2 @ register) => Instruction::Set(r, r2),
70 (v @ parser::i32()) => Instruction::SetN(r, v),
71 },
72 ("add ", r @ register, b' ') =>> {
73 (r2 @ register) => Instruction::Add(r, r2),
74 (v @ parser::i32()) => Instruction::AddN(r, v),
75 },
76 ("mul ", r @ register, b' ') =>> {
77 (r2 @ register) => Instruction::Mul(r, r2),
78 (v @ parser::i32()) => Instruction::MulN(r, v),
79 },
80 ("mod ", r @ register, b' ') =>> {
81 (r2 @ register) => Instruction::Mod(r, r2),
82 (v @ parser::i32()) => Instruction::ModN(r, v),
83 },
84 ("rcv ", r @ register) => Instruction::Rcv(r),
85 ("jgz ") =>> {
86 (r @ register, b' ') =>> {
87 (r2 @ register) => Instruction::Jgz(r, r2),
88 (v @ parser::i32()) => Instruction::JgzN(r, v),
89 },
90 (x @ parser::i32(), b' ', v @ parser::i32()) => {
91 if x > 0 {
92 Instruction::Jmp(v)
93 } else {
94 Instruction::Noop()
95 }
96 },
97 },
98 );
99
100 Ok(Self {
101 instructions: instruction.parse_lines(input)?,
102 })
103 }
104
105 #[must_use]
106 pub fn part1(&self) -> i64 {
107 let mut program = Program::from_instructions(&self.instructions);
108 let mut last = None;
109
110 program.run(
111 |v| last = Some(v),
112 |v| {
113 if v == 0 {
114 Some(0)
116 } else {
117 None
120 }
121 },
122 );
123
124 last.unwrap()
125 }
126
127 #[must_use]
128 pub fn part2(&self) -> u32 {
129 let mut program0 = Program::from_instructions(&self.instructions);
130 let mut inbound0 = VecDeque::new();
131
132 let mut program1 = Program::from_instructions(&self.instructions);
133 let mut inbound1 = VecDeque::new();
134 let mut sent = 0;
135 program1.reg[Register::P as usize] = 1;
136
137 while program0.run(
140 |v| {
141 inbound1.push_back(v)
143 },
144 |_| {
145 inbound0.pop_front()
147 },
148 ) | program1.run(
149 |v| {
150 sent += 1;
152 inbound0.push_back(v)
153 },
154 |_| {
155 inbound1.pop_front()
157 },
158 ) {}
159
160 sent
161 }
162}
163
164impl<'a> Program<'a> {
165 fn from_instructions(instructions: &'a [Instruction]) -> Self {
166 Program {
167 instructions,
168 pc: 0,
169 reg: [0; 7],
170 }
171 }
172
173 fn run(&mut self, mut snd: impl FnMut(i64), mut rcv: impl FnMut(i64) -> Option<i64>) -> bool {
174 let mut progress = false;
175
176 while let Some(&instruction) = self.instructions.get(self.pc) {
177 match instruction {
178 Instruction::Snd(r) => snd(self.reg[r as usize]),
179 Instruction::SndN(v) => snd(v as i64),
180 Instruction::Set(r, r2) => self.reg[r as usize] = self.reg[r2 as usize],
181 Instruction::SetN(r, v) => self.reg[r as usize] = v as i64,
182 Instruction::Add(r, r2) => self.reg[r as usize] += self.reg[r2 as usize],
183 Instruction::AddN(r, v) => self.reg[r as usize] += v as i64,
184 Instruction::Mul(r, r2) => self.reg[r as usize] *= self.reg[r2 as usize],
185 Instruction::MulN(r, v) => self.reg[r as usize] *= v as i64,
186 Instruction::Mod(r, r2) => {
187 self.reg[r as usize] = self.reg[r as usize].rem_euclid(self.reg[r2 as usize])
188 }
189 Instruction::ModN(r, v) => {
190 self.reg[r as usize] = self.reg[r as usize].rem_euclid(v as i64)
191 }
192 Instruction::Rcv(r) => {
193 if let Some(value) = rcv(self.reg[r as usize]) {
194 self.reg[r as usize] = value;
195 } else {
196 break;
197 }
198 }
199 Instruction::Jgz(r, ro) => {
200 if self.reg[r as usize] > 0 {
201 self.pc = self.pc.wrapping_add_signed(self.reg[ro as usize] as isize) - 1;
202 }
203 }
204 Instruction::JgzN(r, v) => {
205 if self.reg[r as usize] > 0 {
206 self.pc = self.pc.wrapping_add_signed(v as isize) - 1;
207 }
208 }
209 Instruction::Jmp(v) => {
210 self.pc = self.pc.wrapping_add(v as usize) - 1;
211 }
212 Instruction::Noop() => {}
213 }
214
215 self.pc += 1;
216 progress = true;
217 }
218
219 progress
220 }
221}
222
223examples!(Day18 -> (i64, u32) [
224 {
225 input: "set a 1\n\
226 add a 2\n\
227 mul a a\n\
228 mod a 5\n\
229 snd a\n\
230 set a 0\n\
231 rcv a\n\
232 jgz a -1\n\
233 set a 1\n\
234 jgz a -2",
235 part1: 4,
236 },
237 {
238 input: "snd 1\n\
239 snd 2\n\
240 snd p\n\
241 rcv a\n\
242 rcv b\n\
243 rcv c\n\
244 rcv d",
245 part2: 3,
246 },
247]);