181 lines
4.2 KiB
Rust
181 lines
4.2 KiB
Rust
mod ops;
|
|
mod icio;
|
|
|
|
pub use icio::{IntCodeIO, StdAdapter};
|
|
|
|
pub struct IntCode<I: IntCodeIO> {
|
|
addr_ptr: usize,
|
|
memory: Vec<isize>,
|
|
running: bool,
|
|
io: I
|
|
}
|
|
|
|
pub struct Value {
|
|
value: isize,
|
|
address: Option<usize>,
|
|
}
|
|
|
|
struct Op<I: IntCodeIO> {
|
|
argc: u8,
|
|
argv: Vec<Value>,
|
|
function: OpCodeFn<I>,
|
|
}
|
|
|
|
enum ParamMode {
|
|
Position,
|
|
Imediate
|
|
}
|
|
|
|
type OpCodeFn<I> = fn(&mut IntCode<I>, &Vec<Value>);
|
|
|
|
impl ParamMode {
|
|
fn from_int(mode: usize) -> Self {
|
|
match mode {
|
|
0 => ParamMode::Position,
|
|
1 => ParamMode::Imediate,
|
|
_ => panic!("Unknown parameter mode {}", mode)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IntCode<StdAdapter> {
|
|
#[cfg(test)]
|
|
pub fn from_vec(v: Vec<isize>) -> Self {
|
|
Self::from_vec_io(v, StdAdapter)
|
|
}
|
|
|
|
pub fn from_str(s: &str) -> Self {
|
|
Self::from_str_io(s, StdAdapter)
|
|
}
|
|
}
|
|
|
|
impl<I: IntCodeIO> IntCode<I> {
|
|
pub fn from_vec_io(v: Vec<isize>, io: I) -> Self {
|
|
IntCode {
|
|
memory: v,
|
|
addr_ptr: 0,
|
|
running: true,
|
|
io
|
|
}
|
|
}
|
|
|
|
pub fn from_str_io(s: &str, io: I) -> Self {
|
|
Self::from_vec_io(s.trim().split(",").filter_map(|n| n.parse().ok()).collect(), io)
|
|
}
|
|
|
|
fn get_op_modes(&mut self) -> (u8, Vec<ParamMode>) {
|
|
let code = self.read_next();
|
|
let op = (code % 100) as u8;
|
|
let mut modes = code / 100;
|
|
let mut vmodes = Vec::new();
|
|
|
|
while modes > 0 {
|
|
vmodes.push(ParamMode::from_int(modes as usize % 10));
|
|
modes = modes / 10;
|
|
}
|
|
(op, vmodes)
|
|
}
|
|
|
|
fn read_next(&mut self) -> isize {
|
|
self.addr_ptr += 1;
|
|
self.memory[self.addr_ptr - 1]
|
|
}
|
|
|
|
pub fn read(&self, address: usize) -> isize {
|
|
self.memory[address]
|
|
}
|
|
|
|
pub fn write(&mut self, address: usize, value: isize) {
|
|
self.memory[address] = value;
|
|
}
|
|
|
|
pub fn jump(&mut self, address: usize) {
|
|
self.addr_ptr = address;
|
|
}
|
|
|
|
pub fn terminate(&mut self) {
|
|
self.running = false;
|
|
}
|
|
|
|
pub fn run(&mut self) -> isize {
|
|
while self.running {
|
|
let op = Op::from_intcode(self);
|
|
op.run(self);
|
|
}
|
|
|
|
self.read(0)
|
|
}
|
|
}
|
|
|
|
impl<I: IntCodeIO> Op<I> {
|
|
fn from_intcode(intcode: &mut IntCode<I>) -> Self {
|
|
let (code, vmodes) = intcode.get_op_modes();
|
|
let mut op = Op::from_code(code);
|
|
for idx in 0..(op.argc) {
|
|
let code = intcode.read_next();
|
|
let arg = match vmodes.get(idx as usize).unwrap_or(&ParamMode::Position) {
|
|
ParamMode::Position => {
|
|
let value = intcode.read(code as usize);
|
|
Value { address: Some(code as usize), value }
|
|
},
|
|
ParamMode::Imediate => Value { address: None, value: code }
|
|
};
|
|
op.argv.push(arg);
|
|
}
|
|
op
|
|
}
|
|
|
|
fn from_code(code: u8) -> Self {
|
|
match code {
|
|
1 => Self::new(3, ops::add),
|
|
2 => Self::new(3, ops::mul),
|
|
3 => Self::new(1, ops::cin),
|
|
4 => Self::new(1, ops::cout),
|
|
5 => Self::new(2, ops::jump_if_true),
|
|
6 => Self::new(2, ops::jump_if_false),
|
|
7 => Self::new(3, ops::lt),
|
|
8 => Self::new(3, ops::eq),
|
|
99 => Self::new(0, ops::exit),
|
|
c => panic!("Unknown opcode {}", c)
|
|
}
|
|
}
|
|
|
|
fn new(argc: u8, function: OpCodeFn<I>) -> Self {
|
|
Op {
|
|
argc,
|
|
argv: Vec::new(),
|
|
function,
|
|
}
|
|
}
|
|
|
|
fn run(&self, mut intcode: &mut IntCode<I>) {
|
|
(self.function)(&mut intcode, &self.argv)
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_run_intcode_position_mode() {
|
|
let input1 = vec![1, 0, 0, 0, 99];
|
|
let input2 = vec![2, 0, 5, 0, 99, 3];
|
|
|
|
let mut intcode = IntCode::from_vec(input1);
|
|
assert_eq!(intcode.run(), 2);
|
|
intcode = IntCode::from_vec(input2);
|
|
assert_eq!(intcode.run(), 6);
|
|
}
|
|
|
|
#[test]
|
|
fn test_run_intcode_imediate_mode() {
|
|
let input = vec![1002, 4, 3, 4, 33];
|
|
|
|
let mut intcode = IntCode::from_vec(input);
|
|
intcode.run();
|
|
assert_eq!(99, intcode.memory[4]);
|
|
}
|
|
}
|