Advent of Code 2019
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

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