1use std::{
18 cmp::{Ordering, PartialEq},
19 collections::HashMap,
20 fmt,
21 ops::{Add, Index, IndexMut, Mul},
22 sync::mpsc::{Receiver, Sender},
23};
24
25#[derive(Clone, Copy, PartialEq, Debug)]
26enum Mode {
27 Position,
28 Immediate,
29 Relative,
30}
31
32type Cell = i64;
33type Tape = HashMap<usize, Cell>;
34
35#[derive(Debug, Default)]
65pub struct IntcodeVM {
66 tape: Tape,
67 ip: usize,
68 rp: isize,
69 input: Option<Receiver<Cell>>,
70 output: Option<Sender<Cell>>,
71 inp_wait_flag: Option<Sender<()>>,
72 halted: bool,
73}
74
75trait Flags {
76 fn flags(&self) -> Result<(Mode, Mode, Mode), IntcodeError>;
77}
78
79impl Flags for Cell {
80 fn flags(&self) -> Result<(Mode, Mode, Mode), IntcodeError> {
81 let flag = |argpos| match self / 10_i64.pow(argpos) % 10 {
82 0 => Ok(Mode::Position),
83 1 => Ok(Mode::Immediate),
84 2 => Ok(Mode::Relative),
85 _ => Err(IntcodeError::InvalidInstruction),
86 };
87 Ok((flag(2)?, flag(3)?, flag(4)?))
88 }
89}
90
91impl IntcodeVM {
92 pub fn from_prog(prog: &[Cell]) -> Self {
94 Self {
95 tape: prog.iter().copied().enumerate().collect(),
96 ..Default::default()
97 }
98 }
99
100 pub fn with_io(
102 prog: &[Cell],
103 input: Option<Receiver<Cell>>,
104 output: Option<Sender<Cell>>,
105 ) -> Self {
106 Self {
107 tape: prog.iter().copied().enumerate().collect(),
108 input,
109 output,
110 ..Default::default()
111 }
112 }
113
114 pub fn with_inp_flag(
118 prog: &[Cell],
119 input: Option<Receiver<Cell>>,
120 output: Option<Sender<Cell>>,
121 inp_wait_flag: Option<Sender<()>>,
122 ) -> Self {
123 Self {
124 tape: prog.iter().copied().enumerate().collect(),
125 input,
126 output,
127 inp_wait_flag,
128 ..Default::default()
129 }
130 }
131
132 pub fn run_prog(&mut self) -> Result<Tape, IntcodeError> {
134 if self.halted {
135 return Err(IntcodeError::RanHaltedVM);
136 }
137 while !self.halted {
138 self.step()?;
139 }
140 Ok(self.tape.clone())
141 }
142
143 pub fn step(&mut self) -> Result<Tape, IntcodeError> {
145 if self.halted {
146 return Err(IntcodeError::RanHaltedVM);
147 }
148 match self[self.ip] % 100 {
149 1 => self.maths(<i64 as Add>::add)?,
150 2 => self.maths(<i64 as Mul>::mul)?,
151 3 => self.input()?,
152 4 => self.outpt()?,
153 5 => self.test0(<i64 as PartialEq>::ne)?,
154 6 => self.test0(<i64 as PartialEq>::eq)?,
155 7 => self.test2(Ordering::Less)?,
156 8 => self.test2(Ordering::Equal)?,
157 9 => self.relpt()?,
158 99 => self.halted = true,
159 _ => return Err(IntcodeError::InvalidInstruction),
160 }
161
162 Ok(self.tape.clone())
163 }
164
165 fn get(&mut self, mode: Mode, pad: usize) -> Cell {
166 let in1 = self.ip + pad;
167 let in2 = match mode {
168 Mode::Immediate => return self[in1],
169 Mode::Position => self[in1] as usize,
170 Mode::Relative => (self[in1] as isize + self.rp) as usize,
171 };
172 *self.tape.entry(in2).or_default()
173 }
174
175 fn maths(&mut self, op: fn(Cell, Cell) -> Cell) -> Result<(), IntcodeError> {
176 let (one, two, three) = self[self.ip].flags()?;
177 let arg1 = self.get(one, 1);
178 let arg2 = self.get(two, 2);
179 let rel = if three == Mode::Relative { self.rp } else { 0 };
180 let loc = (self[self.ip + 3] + rel as i64) as usize;
181 self[loc] = op(arg1, arg2);
182 self.ip += 4;
183 Ok(())
184 }
185
186 fn input(&mut self) -> Result<(), IntcodeError> {
187 let (one, ..) = self[self.ip].flags()?;
188 let rel = if one == Mode::Relative { self.rp } else { 0 };
189 let loc = (self[self.ip + 1] + rel as i64) as usize;
190 if let Some(sender) = &self.inp_wait_flag {
191 sender.send(()).expect(
192 "Error: failed to send input waiting flag: \
193 receiver disconnected.",
194 );
195 }
196 self[loc] = self
197 .input
198 .as_ref()
199 .expect("Error: found input instruction but no receiver was created.")
200 .recv()
201 .expect("Error: failed to receive input: sender disconnected.");
202 self.ip += 2;
203 Ok(())
204 }
205
206 fn outpt(&mut self) -> Result<(), IntcodeError> {
207 let (one, ..) = self[self.ip].flags()?;
208 let val = self.get(one, 1);
209 self.output
210 .as_ref()
211 .expect("Error: found output instruction but no sender was created.")
212 .send(val)
213 .expect("Error: failed to send output: receiver disconnected.");
214 self.ip += 2;
215 Ok(())
216 }
217
218 fn test0(&mut self, checker: fn(&Cell, &Cell) -> bool) -> Result<(), IntcodeError> {
220 let (one, two, _) = self[self.ip].flags()?;
221 let test = self.get(one, 1);
222 if checker(&test, &0) {
223 self.ip = self.get(two, 2) as usize;
224 } else {
225 self.ip += 3;
226 }
227 Ok(())
228 }
229
230 fn test2(&mut self, mode: Ordering) -> Result<(), IntcodeError> {
232 let (one, two, three) = self[self.ip].flags()?;
233 let arg1 = self.get(one, 1);
234 let arg2 = self.get(two, 2);
235 let rel = if three == Mode::Relative { self.rp } else { 0 };
236 let loc = (self[self.ip + 3] + rel as i64) as usize;
237 let test = arg1.cmp(&arg2) == mode;
238 self[loc] = test as i64;
239 self.ip += 4;
240 Ok(())
241 }
242
243 fn relpt(&mut self) -> Result<(), IntcodeError> {
244 let (one, ..) = self[self.ip].flags()?;
245 let arg1 = self.get(one, 1);
246 self.rp += arg1 as isize;
247 self.ip += 2;
248 Ok(())
249 }
250}
251
252impl Index<usize> for IntcodeVM {
253 type Output = Cell;
254
255 fn index(&self, ix: usize) -> &Self::Output {
256 &self.tape[&ix]
257 }
258}
259
260impl IndexMut<usize> for IntcodeVM {
261 fn index_mut(&mut self, ix: usize) -> &mut Self::Output {
262 self.tape.entry(ix).or_default();
263 self.tape.get_mut(&ix).unwrap()
264 }
265}
266
267pub enum IntcodeError {
269 RanHaltedVM,
270 InvalidInstruction,
271}
272
273impl std::error::Error for IntcodeError {}
274
275impl fmt::Display for IntcodeError {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 let err_msg = match self {
278 IntcodeError::RanHaltedVM => "Error: tried to run an already halted intcode VM",
279 IntcodeError::InvalidInstruction => "Error: an instruction was malformed",
280 };
281 f.write_str(err_msg)
282 }
283}
284
285impl fmt::Debug for IntcodeError {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 let err_msg = match self {
288 IntcodeError::RanHaltedVM => "Error: tried to run an already halted intcode VM",
289 IntcodeError::InvalidInstruction => "Error: an instruction was malformed",
290 };
291 f.write_str(err_msg)
292 }
293}