AoC2019/src/intcode/mod.rs

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]);
}
}