Created
December 9, 2019 18:30
-
-
Save 0e4ef622/0f44c1fa4145def86648b44398fb1b27 to your computer and use it in GitHub Desktop.
intcode vm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::collections::VecDeque; | |
#[derive(Copy, Clone, PartialEq, Eq, Debug)] | |
pub enum Status { | |
Ready, | |
WaitingForInput, | |
Finished, | |
} | |
#[derive(Clone, Debug)] | |
pub struct Icvm { | |
program: Vec<i128>, | |
input: VecDeque<i128>, | |
output: VecDeque<i128>, | |
pc: usize, | |
status: Status, | |
base: i128, | |
} | |
impl Icvm { | |
/// Create a new Intcode VM. | |
pub fn new(program: Vec<i128>) -> Self { | |
Self { | |
program, | |
input: VecDeque::new(), | |
output: VecDeque::new(), | |
pc: 0, | |
status: Status::Ready, | |
base: 0, | |
} | |
} | |
/// Run until completion, or until the program needs more input. | |
pub fn run(&mut self) { | |
while self.status == Status::Ready { | |
self.step(); | |
} | |
} | |
/// Step one instruction forward. | |
pub fn step(&mut self) { | |
// starting from 1 | |
let op = self.program[self.pc] % 100; | |
let j = match op { | |
1 => { *self.get_param(3) = *self.get_param(1) + *self.get_param(2); 4 } | |
2 => { *self.get_param(3) = *self.get_param(1) * *self.get_param(2); 4 } | |
3 => if let Some(i) = self.input.pop_front() { *self.get_param(1) = i; 2 } | |
else { self.status = Status::WaitingForInput; 0 } | |
4 => { let o = *self.get_param(1); self.output.push_back(o); 2 }, | |
5 => if *self.get_param(1) != 0 { self.pc = *self.get_param(2) as usize; 0 } else { 3 } | |
6 => if *self.get_param(1) == 0 { self.pc = *self.get_param(2) as usize; 0 } else { 3 } | |
7 => { *self.get_param(3) = (*self.get_param(1) < *self.get_param(2)) as i128; 4 } | |
8 => { *self.get_param(3) = (*self.get_param(1) == *self.get_param(2)) as i128; 4 } | |
9 => { self.base += *self.get_param(1); 2 } | |
99 => { self.status = Status::Finished; 0 } | |
_ => panic!("bad opcode"), | |
}; | |
self.pc += j; | |
} | |
fn get_param(&mut self, n: u32) -> &mut i128 { | |
let mode = self.program[self.pc] / 10i128.pow(n+1) % 10; | |
let pc = self.pc; | |
let pg = &mut self.program[..]; // magically avoids lifetime issues | |
match mode { | |
0 => &mut pg[pg[pc + n as usize] as usize], | |
1 => &mut pg[pc + n as usize], | |
2 => &mut pg[(pg[self.pc + n as usize] + self.base) as usize], | |
_ => panic!("bad mode"), | |
} | |
} | |
/// The current program counter, or instruction pointer. | |
pub fn pc(&self) -> usize { | |
self.pc | |
} | |
/// The current status. | |
pub fn status(&self) -> Status { | |
self.status | |
} | |
/// The program this VM contains. | |
pub fn program(&self) -> &[i128] { | |
&self.program | |
} | |
/// Add an input to the VM. | |
pub fn push_input(&mut self, i: i128) { | |
self.input.push_back(i); | |
if self.status == Status::WaitingForInput { | |
self.status = Status::Ready; | |
} | |
} | |
/// Add multiple inputs to the VM. | |
pub fn push_inputs<I>(&mut self, i: I) | |
where | |
I: IntoIterator<Item = i128> | |
{ | |
self.input.extend(i); | |
if self.status == Status::WaitingForInput && !self.input.is_empty() { | |
self.status = Status::Ready; | |
} | |
} | |
/// Read the current input queue. | |
pub fn inputs(&self) -> impl Iterator<Item = i128> + '_ { | |
self.input.iter().copied() | |
} | |
/// Pop an output from the output queue. | |
pub fn pop_output(&mut self) -> Option<i128> { | |
self.output.pop_front() | |
} | |
/// Read the outputs without popping them off the output queue. | |
pub fn outputs(&self) -> impl Iterator<Item = i128> + '_ { | |
self.output.iter().copied() | |
} | |
/// Read the outputs while popping them off the output queue. See [`Vec::drain`]. | |
/// | |
/// [`Vec::drain`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain | |
pub fn drain_outputs(&mut self) -> impl Iterator<Item = i128> + '_ { | |
self.output.drain(..) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment