use std::{
cmp::{Ordering, PartialEq},
fmt,
ops::{Add, Mul},
ops::{Index, IndexMut},
sync::mpsc::{Receiver, Sender},
};
#[derive(Clone, Copy, PartialEq, Debug)]
enum Mode {
Position,
Immediate,
Relative,
}
type Cell = i128;
type Tape = Vec<Cell>;
#[derive(Debug)]
pub struct IntcodeVM {
tape: Tape,
ip: usize,
rp: isize,
input: Option<Receiver<Cell>>,
output: Option<Sender<Cell>>,
inp_wait_flag: Option<Sender<()>>,
halted: bool,
}
impl Default for IntcodeVM {
fn default() -> Self {
Self {
tape: Vec::new(),
ip: 0,
rp: 0,
input: None,
output: None,
inp_wait_flag: None,
halted: false,
}
}
}
trait Flags {
fn flags(&self) -> (Mode, Mode, Mode);
}
impl Flags for Cell {
fn flags(&self) -> (Mode, Mode, Mode) {
let flag = |argpos| match self / 10_i128.pow(argpos) % 10 {
0 => Mode::Position,
1 => Mode::Immediate,
2 => Mode::Relative,
_ => unreachable!(),
};
(flag(2), flag(3), flag(4))
}
}
impl IntcodeVM {
pub fn from_prog(prog: &[Cell]) -> Self {
Self {
tape: prog.to_vec(),
..Default::default()
}
}
pub fn with_io(
prog: &[Cell],
input: Option<Receiver<Cell>>,
output: Option<Sender<Cell>>,
) -> Self {
Self {
tape: prog.to_vec(),
input,
output,
..Default::default()
}
}
pub fn with_inp_flag(
prog: &[Cell],
input: Option<Receiver<Cell>>,
output: Option<Sender<Cell>>,
inp_wait_flag: Option<Sender<()>>,
) -> Self {
Self {
tape: prog.to_vec(),
input,
output,
inp_wait_flag,
..Default::default()
}
}
pub fn run_prog(&mut self) -> Result<Tape, RanHaltedVMError> {
if self.halted {
return Err(RanHaltedVMError);
}
while !self.halted {
match self[self.ip] % 100 {
1 => self.maths(<i128 as Add>::add),
2 => self.maths(<i128 as Mul>::mul),
3 => self.input(),
4 => self.outpt(),
5 => self.test0(<i128 as PartialEq>::ne),
6 => self.test0(<i128 as PartialEq>::eq),
7 => self.test2(Ordering::Less),
8 => self.test2(Ordering::Equal),
9 => self.relpt(),
99 => self.halted = true,
_ => unreachable!(),
}
}
Ok(self.tape.to_vec())
}
fn get(&mut self, mode: Mode, pad: usize) -> Cell {
let in1 = self.ip + pad;
self.reserve(in1);
let in2 = match mode {
Mode::Immediate => return self[in1],
Mode::Position => self[in1] as usize,
Mode::Relative => (self[in1] as isize + self.rp) as usize,
};
self.reserve(in2);
self[in2]
}
#[inline]
fn reserve(&mut self, s: usize) {
if s >= self.tape.len() {
self.tape.resize(s + 1, 0);
}
}
fn maths(&mut self, op: fn(i128, i128) -> i128) {
let (one, two, three) = self[self.ip].flags();
let arg1 = self.get(one, 1);
let arg2 = self.get(two, 2);
let rel = if three == Mode::Relative { self.rp } else { 0 };
let loc = (self[self.ip + 3] + rel as i128) as usize;
self.reserve(loc);
self[loc] = op(arg1, arg2);
self.ip += 4;
}
fn input(&mut self) {
let (one, ..) = self[self.ip].flags();
let rel = if one == Mode::Relative { self.rp } else { 0 };
let loc = (self[self.ip + 1] + rel as i128) as usize;
self.reserve(loc);
if let Some(sender) = &self.inp_wait_flag {
sender.send(()).expect(
"Error: failed to send input waiting flag: \
receiver disconnected.",
);
}
self[loc] = self
.input
.as_ref()
.expect(
"Error: found input instruction but no receiver was created.",
)
.recv()
.expect("Error: failed to receive input: sender disconnected.");
self.ip += 2;
}
fn outpt(&mut self) {
let (one, ..) = self[self.ip].flags();
let val = self.get(one, 1);
self.output
.as_ref()
.expect(
"Error: found output instruction but no sender was created.",
)
.send(val)
.expect("Error: failed to send output: receiver disconnected.");
self.ip += 2;
}
fn test0(&mut self, checker: fn(&Cell, &Cell) -> bool) {
let (one, two, _) = self[self.ip].flags();
let test = self.get(one, 1);
if checker(&test, &0) {
self.ip = self.get(two, 2) as usize;
} else {
self.ip += 3;
}
}
fn test2(&mut self, mode: Ordering) {
let (one, two, three) = self[self.ip].flags();
let arg1 = self.get(one, 1);
let arg2 = self.get(two, 2);
let rel = if three == Mode::Relative { self.rp } else { 0 };
let loc = (self[self.ip + 3] + rel as i128) as usize;
self.reserve(loc);
let test = arg1.cmp(&arg2) == mode;
self[loc] = test as i128;
self.ip += 4;
}
fn relpt(&mut self) {
let (one, ..) = self[self.ip].flags();
let arg1 = self.get(one, 1);
self.rp += arg1 as isize;
self.ip += 2;
}
}
impl Index<usize> for IntcodeVM {
type Output = Cell;
fn index(&self, ix: usize) -> &Self::Output {
&self.tape[ix]
}
}
impl IndexMut<usize> for IntcodeVM {
fn index_mut(&mut self, ix: usize) -> &mut Self::Output {
&mut self.tape[ix]
}
}
pub struct RanHaltedVMError;
impl std::error::Error for RanHaltedVMError {}
impl fmt::Display for RanHaltedVMError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Error: tried to run an already halted intcode VM")
}
}
impl fmt::Debug for RanHaltedVMError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Error: tried to run an already halted intcode VM")
}
}