use std::env; use std::fs; use std::io::{self, BufRead}; type BusWidth = i32; type IOType = i32; type IOData = Vec; fn read_program(filename: &str) -> Vec { let contents = fs::read_to_string(filename) .expect(filename); contents.split(',').map(|v| { v.trim_end().parse().unwrap() }).collect() } #[derive(Debug)] enum Arithm { Add, Mul, LessThan, Equals, } #[derive(Debug)] enum IO { In, Out, } #[derive(Debug)] enum Jmp { JmpTrue, JmpFalse, } #[derive(Debug, PartialEq)] enum ParameterMode { Position, Immediate, } fn arithm(op:Arithm, ip:usize, modes:Vec, memory:&mut Vec) { if modes[2] == ParameterMode::Immediate { panic!("Instruction can not store in immediate mode parameter. (IP: {})", ip); } let params:Vec = (0..=2).map(|n| match &modes[n] { ParameterMode::Position => memory[ip + n + 1] as usize, ParameterMode::Immediate => ip + n + 1, }).collect(); let left = params[0]; let right = params[1]; let result = params[2]; match op { Arithm::Add => memory[result] = memory[left] + memory[right], Arithm::Mul => memory[result] = memory[left] * memory[right], Arithm::LessThan => memory[result] = if memory[left] < memory[right] { 1 } else { 0 }, Arithm::Equals => memory[result] = if memory[left] == memory[right] { 1 } else { 0 }, } } fn jmp(op:Jmp, ip:usize, modes:Vec, memory:&mut Vec) -> usize { let params:Vec = (0..=2).map(|n| match &modes[n] { ParameterMode::Position => memory[ip + n + 1] as usize, ParameterMode::Immediate => ip + n + 1, }).collect(); let value = params[0]; let address = params[1]; match op { Jmp::JmpTrue => (if memory[value] != 0 { memory[address] as usize } else { ip + 3 }), Jmp::JmpFalse => (if memory[value] == 0 { memory[address] as usize } else { ip + 3 }), } } fn io(op: IO, ip: usize, modes: Vec, memory:&mut Vec, input:&mut IOData, output:&mut IOData, var_io: bool) { let stdin = io::stdin(); match op { IO::In => { let address = match &modes[0] { ParameterMode::Position => memory[ip + 1] as usize, ParameterMode::Immediate => panic!("Cannot store input in immediate mode parameter. (IP: {})", ip), }; match var_io { false => { let invalue = stdin.lock().lines().next().map(|s| { match s { Ok(s) => Some(s.trim_end().trim_start().parse::().unwrap()), _ => None, } }); match invalue { Some(v) => memory[address] = v.unwrap() as BusWidth, _ => {}, }; }, true => { memory[address] = input.remove(0) as BusWidth; }, } }, IO::Out => { let o = match &modes[0] { ParameterMode::Position => memory[memory[ip + 1] as usize], ParameterMode::Immediate => memory[ip + 1], }; println!("{}", o); if var_io { output.push(o as IOType); } }, } } fn intcode(mut memory:Vec, mut input: &mut IOData, mut output:&mut IOData, f: F, var_io: bool) where F: Fn(&mut Vec, &mut IOData, &mut IOData) { let mut ip = 0; while ip < memory.len() { let op = memory[ip] % 100; let modes:Vec = (2..=4).map(|e| memory[ip] / 10i32.pow(e) as BusWidth % 10) .map(|n| match n { 0 => ParameterMode::Position, 1 => ParameterMode::Immediate, p => panic!("Invalid parameter mode {} at IP: {}", p, ip), }).collect(); match op { 1 => { arithm(Arithm::Add, ip, modes, &mut memory); ip += 4 }, 2 => { arithm(Arithm::Mul, ip, modes, &mut memory); ip += 4 }, 7 => { arithm(Arithm::LessThan, ip, modes, &mut memory); ip += 4 }, 8 => { arithm(Arithm::Equals, ip, modes, &mut memory); ip += 4 }, 3 => { io(IO::In, ip, modes, &mut memory, &mut input, &mut output, var_io); ip += 2 }, 4 => { io(IO::Out, ip, modes, &mut memory, &mut input, &mut output, var_io); ip += 2 }, 5 => { ip = jmp(Jmp::JmpTrue, ip, modes, &mut memory); }, 6 => { ip = jmp(Jmp::JmpFalse, ip, modes, &mut memory); }, 99 => break, op => panic!("Invalid operation {} at IP: {}", op, ip), } if var_io { f(&mut memory, &mut input, &mut output); } } } fn func(_memory:&mut Vec, input:&mut IOData, output:&mut IOData) { if output.len() > 0 && input.len() > 0 { let v = output.remove(0); input.insert(1, v); } } fn main() { let args: Vec = env::args().collect(); let mut opts = getopts::Options::new(); opts.reqopt("m", "mode", "Mode of IO operations", "[arg|stdio]"); opts.reqopt("p", "program", "File to load program from", "FILE"); let matches = match opts.parse(&args[1..]) { Ok(m) => { m } Err(f) => { panic!(f.to_string()) } }; let program:String = matches.opt_str("program").unwrap(); let mode = matches.opt_str("mode").unwrap(); let memory = read_program(&program); if mode == "arg" { let mut input:IOData = matches.free[0].split(",").map(|v| { let i = v.trim_end().parse().unwrap(); i }).collect(); let mut output = Vec::new(); while input.len() > 1 { intcode(memory.clone(), &mut input, &mut output, &func, true); } } else if mode == "stdio" { let mut input = vec![]; let mut output = vec![]; intcode(memory.clone(), &mut input, &mut output, &func, false); } else { panic!("Unknown mode: {}", mode); } }