You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
6.0 KiB
244 lines
6.0 KiB
pub struct IntCode { |
|
addr_ptr: usize, |
|
memory: Vec<isize>, |
|
} |
|
|
|
pub struct Value { |
|
value: isize, |
|
address: Option<usize>, |
|
} |
|
|
|
struct Op { |
|
argc: u8, |
|
argv: Vec<Value>, |
|
function: OpCodeFn, |
|
} |
|
|
|
pub enum ResultAction { |
|
None, |
|
Terminate, |
|
Write { |
|
address: usize, |
|
value: isize, |
|
}, |
|
Jump { |
|
address: usize, |
|
} |
|
} |
|
|
|
enum ParamMode { |
|
Position, |
|
Imediate |
|
} |
|
|
|
type OpCodeFn = fn(&Vec<Value>) -> ResultAction; |
|
|
|
impl ParamMode { |
|
fn from_int(mode: usize) -> Self { |
|
match mode { |
|
0 => ParamMode::Position, |
|
1 => ParamMode::Imediate, |
|
_ => panic!("Unknown parameter mode {}", mode) |
|
} |
|
} |
|
} |
|
|
|
impl IntCode { |
|
pub fn from_vec(v: Vec<isize>) -> Self { |
|
IntCode { |
|
memory: v, |
|
addr_ptr: 0 |
|
} |
|
} |
|
|
|
pub fn from_str(s: &str) -> Self { |
|
Self::from_vec(s.trim().split(",").filter_map(|n| n.parse().ok()).collect()) |
|
} |
|
|
|
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] |
|
} |
|
|
|
fn read(&self, address: usize) -> isize { |
|
self.memory[address] |
|
} |
|
|
|
fn write(&mut self, address: usize, value: isize) { |
|
self.memory[address] = value; |
|
} |
|
|
|
fn jump(&mut self, address: usize) { |
|
self.addr_ptr = address; |
|
} |
|
|
|
pub fn run(&mut self) -> isize { |
|
loop { |
|
let op = Op::from_intcode(self); |
|
let res = op.run(); |
|
|
|
match res { |
|
ResultAction::Write { address, value } => self.write(address, value), |
|
ResultAction::Jump { address } => self.jump(address), |
|
ResultAction::None => (), |
|
ResultAction::Terminate => break, |
|
} |
|
} |
|
|
|
self.read(0) |
|
} |
|
} |
|
|
|
impl Op { |
|
fn from_intcode(intcode: &mut IntCode) -> 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) -> Self { |
|
Op { |
|
argc, |
|
argv: Vec::new(), |
|
function, |
|
} |
|
} |
|
|
|
fn run(&self) -> ResultAction { |
|
(self.function)(&self.argv) |
|
} |
|
} |
|
|
|
mod ops { |
|
use super::{Value, ResultAction}; |
|
use std::io; |
|
|
|
pub fn add(argv: &Vec<Value>) -> ResultAction { |
|
ResultAction::Write { |
|
address: argv[2].address.unwrap(), |
|
value: (argv[0].value + argv[1].value) as isize |
|
} |
|
} |
|
|
|
pub fn mul(argv: &Vec<Value>) -> ResultAction { |
|
ResultAction::Write { |
|
address: argv[2].address.unwrap(), |
|
value: (argv[0].value * argv[1].value) as isize |
|
} |
|
} |
|
|
|
pub fn exit(_argv: &Vec<Value>) -> ResultAction { |
|
ResultAction::Terminate |
|
} |
|
|
|
pub fn cin(argv: &Vec<Value>) -> ResultAction { |
|
let mut value = String::new(); |
|
let _ = io::stdin() |
|
.read_line(&mut value) |
|
.expect("Unable to read stdin"); |
|
ResultAction::Write { |
|
address: argv[0].address.unwrap(), |
|
value: value.trim().parse().unwrap(), |
|
} |
|
} |
|
|
|
pub fn cout(argv: &Vec<Value>) -> ResultAction { |
|
println!("{}", argv[0].value); |
|
ResultAction::None |
|
} |
|
|
|
pub fn jump_if_true(argv: &Vec<Value>) -> ResultAction { |
|
if argv[0].value != 0 { |
|
ResultAction::Jump { address: argv[1].value as usize } |
|
} else { |
|
ResultAction::None |
|
} |
|
} |
|
|
|
pub fn jump_if_false(argv: &Vec<Value>) -> ResultAction { |
|
if argv[0].value == 0 { |
|
ResultAction::Jump { address: argv[1].value as usize } |
|
} else { |
|
ResultAction::None |
|
} |
|
} |
|
|
|
pub fn lt(argv: &Vec<Value>) -> ResultAction { |
|
ResultAction::Write { |
|
address: argv[2].address.unwrap(), |
|
value: if argv[0].value < argv[1].value { 1 } else { 0 } |
|
} |
|
} |
|
|
|
pub fn eq(argv: &Vec<Value>) -> ResultAction { |
|
ResultAction::Write { |
|
address: argv[2].address.unwrap(), |
|
value: if argv[0].value == argv[1].value { 1 } else { 0 } |
|
} |
|
} |
|
} |
|
|
|
|
|
#[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]); |
|
} |
|
}
|
|
|