use std::env; use std::io::{self, BufRead, BufReader}; use std::fs::File; type BusWidth = i128; type IOType = i128; type IOData = Vec; fn read_program(filename: &str) -> Vec { let f = File::open(filename).expect(filename); let f = BufReader::new(f); f.lines() .filter(|line| match line { Ok(s) => { let b = s.as_bytes(); if b.len() == 0 || b.len() > 0 && b[0] == b'#' { false } else { true } }, _ => true, }) .map(|line| match line { Ok(line) => { let data:Vec = line.split(',').map(|e| { e.trim_start().trim_end() }) .filter(|v| match v.parse::() { Ok(_) => true, _ => { eprintln!("Could not parse: {:?}", v); false }, }) .map(|r| r.parse().unwrap()) .collect(); data }, Err(_) => panic!("Could not parse {}.", filename), }).flatten().collect() } #[derive(Clone, Debug)] struct Memory { mem: Vec, } impl Memory { fn new() -> Memory { Memory { mem: Vec::new(), } } fn init(&mut self, mem:Vec) { self.mem = mem; } fn read(&self, address: usize) -> BusWidth { match self.mem.get(address) { Some(value) => *value, None => 0, } } fn write(&mut self, address: usize, value: BusWidth) { if self.len() < address { self.mem.resize(address*2, 0); } self.mem[address] = value; } fn len(&self) -> usize { self.mem.len() } } #[derive(Debug)] struct Registers { ip: usize, base: usize, } #[derive(Debug)] enum Arithm { Add, Mul, LessThan, Equals, } #[derive(Debug)] enum IO { In, Out, } #[derive(Debug)] enum Jmp { JmpTrue, JmpFalse, } #[derive(Debug)] enum RegOp { BaseMod, } #[derive(Debug, PartialEq)] enum ParameterMode { Position, Immediate, Relative, } #[derive(Debug, PartialEq)] enum DataDirection { Read, Write, } #[derive(PartialEq)] enum IOMethod { StdIO, Variables, } struct Intcode { r: Registers, io_method: IOMethod, input: IOData, output: IOData, } impl Intcode { fn new() -> Intcode { Intcode { r: Registers { ip: 0, base: 0, }, io_method: IOMethod::Variables, input: vec![], output: vec![], } } fn param_to_argument(&self, mode: &ParameterMode, mem: &Memory, offset: usize, dir: &DataDirection) -> usize { match mode { ParameterMode::Position => { if mem.read(offset) < 0 { panic!("Attempt to address negative memory at IP: {}"); } mem.read(offset) as usize }, ParameterMode::Immediate => { if *dir == DataDirection::Write { panic!("Immediate parameter mode at IP: {}", self.r.ip); } offset }, ParameterMode::Relative => { if (mem.read(offset) + self.r.base as BusWidth) < 0 { panic!("Attempt to address negative memory at IP: {}"); } (mem.read(offset) + self.r.base as BusWidth) as usize }, } } fn arithm(&self, op: Arithm, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read, DataDirection::Read, DataDirection::Write]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let left = params[0]; let right = params[1]; let result = params[2]; match op { Arithm::Add => memory.write(result, memory.read(left) + memory.read(right)), Arithm::Mul => memory.write(result, memory.read(left) * memory.read(right)), Arithm::LessThan => memory.write(result, if memory.read(left) < memory.read(right) { 1 } else { 0 }), Arithm::Equals => memory.write(result, if memory.read(left) == memory.read(right) { 1 } else { 0 }), } } fn jmp(&mut self, op: Jmp, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read, DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let value = params[0]; let address = params[1]; self.r.ip = match op { Jmp::JmpTrue => (if memory.read(value) != 0 { memory.read(address) as usize } else { self.r.ip + 3 }), Jmp::JmpFalse => (if memory.read(value) == 0 { memory.read(address) as usize } else { self.r.ip + 3 }), }; } fn regop(&mut self, op: RegOp, modes: Vec, memory: &mut Memory) { let parameter_directions = [DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let value = memory.read(params[0]); match op { RegOp::BaseMod => { if value < 0 { self.r.base -= (-1 * value) as usize; } else { self.r.base += value as usize; } }, }; } fn io_in(&mut self, _op: IO, modes: Vec, memory:&mut Memory) { let parameter_directions = [DataDirection::Write]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let address = params[0]; let stdin = io::stdin(); match &self.io_method { IOMethod::StdIO => { let mut value: Option = None; while { stdin.lock().lines().next().map(|s| { match s { Ok(v) => { let parsed_string = v.trim_end().trim_start().parse::(); match parsed_string { Ok(v) => { value = Some(v); value }, _ => None, }; value }, _ => { None }, } }); if value == None { eprintln!("Could not parse input, please retry."); true } else { false } } {} match value { Some(v) => memory.write(address, v), _ => {}, }; }, IOMethod::Variables => { memory.write(address, self.input.remove(0) as BusWidth); }, } } fn io_out(&mut self, _op: IO, modes: Vec, memory:&mut Memory ) { let parameter_directions = [DataDirection::Read]; let params:Vec = (0..parameter_directions.len()) .zip(parameter_directions.iter()) .map(|(p, d)| { self.param_to_argument(&modes[p], memory, self.r.ip + 1 + p, d) }).collect(); let address = params[0]; let o = memory.read(address); println!("{}", o); match &self.io_method { IOMethod::Variables => self.output.push(o as IOType), _ => {}, } } fn intcode(&mut self, mut memory:Memory) { while self.r.ip < memory.len() { let op = memory.read(self.r.ip) % 100; let modes:Vec = (2..=4) .map(|e| memory.read(self.r.ip) / 10i32.pow(e) as BusWidth % 10) .map(|n| match n { 0 => ParameterMode::Position, 1 => ParameterMode::Immediate, 2 => ParameterMode::Relative, p => panic!("Invalid parameter mode {} at IP: {}", p, self.r.ip), }).collect(); match op { 1 => { self.arithm(Arithm::Add, modes, &mut memory); self.r.ip += 4 }, 2 => { self.arithm(Arithm::Mul, modes, &mut memory); self.r.ip += 4 }, 7 => { self.arithm(Arithm::LessThan, modes, &mut memory); self.r.ip += 4 }, 8 => { self.arithm(Arithm::Equals, modes, &mut memory); self.r.ip += 4 }, 3 => { self.io_in( IO::In, modes, &mut memory); self.r.ip += 2 }, 4 => { self.io_out(IO::Out, modes, &mut memory); self.r.ip += 2 }, 5 => { self.jmp( Jmp::JmpTrue, modes, &mut memory); }, 6 => { self.jmp( Jmp::JmpFalse, modes, &mut memory); }, 9 => { self.regop(RegOp::BaseMod, modes, &mut memory); self.r.ip += 2}, 99 => break, op => panic!("Invalid operation {} at IP: {}", op, self.r.ip), } } } } 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 mut memory = Memory::new(); memory.init(read_program(&program)); let mut cpu = Intcode::new(); if mode == "arg" { cpu.io_method = IOMethod::Variables; let input:IOData = matches.free[0].split(",").map(|v| { let i = v.trim_end().parse().unwrap(); i }).collect(); cpu.input = input; while cpu.input.len() > 1 { cpu.intcode(memory.clone()); } } else if mode == "stdio" { cpu.io_method = IOMethod::StdIO; cpu.intcode(memory.clone()); } else { panic!("Unknown mode: {}", mode); } }