use std::env; use std::fs::File; use std::io::{self, BufRead, BufReader, Write}; #[derive(Copy, Clone, Debug)] enum Mode { Position, Immediate, } impl Default for Mode { fn default() -> Self { Mode::Position } } impl From for Mode { fn from(b: bool) -> Self { match b { false => Mode::Position, true => Mode::Immediate, } } } #[derive(Copy, Clone, Default, Debug)] struct Modes { a: Mode, b: Mode, c: Mode, } impl From<(Mode, Mode, Mode)> for Modes { fn from(modes: (Mode, Mode, Mode)) -> Self { Modes { a: modes.0, b: modes.1, c: modes.2, } } } impl From<&ModedOpcode> for Modes { fn from(mo: &ModedOpcode) -> Self { mo.modes } } #[derive(Copy, Clone)] enum Opcode { Add, Multiply, Input, Output, JumpIfTrue, JumpIfFalse, LessThan, Equals, Halt, } impl From<&ModedOpcode> for Opcode { fn from(mo: &ModedOpcode) -> Self { mo.op } } impl From for Opcode { fn from(i: i64) -> Self { match i { 1 => Opcode::Add, 2 => Opcode::Multiply, 3 => Opcode::Input, 4 => Opcode::Output, 5 => Opcode::JumpIfTrue, 6 => Opcode::JumpIfFalse, 7 => Opcode::LessThan, 8 => Opcode::Equals, 99 => Opcode::Halt, _ => panic!("invalid opcode {}", i), } } } struct ModedOpcode { modes: Modes, op: Opcode, } impl From for ModedOpcode { fn from(i: i64) -> Self { let code = i % 100; let a = i % 1000 >= 100; let b = i % 10000 >= 1000; let c = i > 10000; let modes = (Mode::from(a), Mode::from(b), Mode::from(c)).into(); ModedOpcode { modes: modes, op: Opcode::from(code), } } } fn main() -> io::Result<()> { println!("Hello, i am {}.", env::args().next().unwrap()); let filename = env::args().nth(1).expect("provide file as first param"); let computer = Computer::from(File::open(filename)?); computer.clone().run(); Ok(()) } struct Computer { pos: usize, program: Vec, modes: Modes, } impl Clone for Computer { fn clone(&self) -> Self { Computer { pos: 0, program: self.program.clone(), modes: Default::default(), } } } impl From for Computer { fn from(f: File) -> Self { let f = BufReader::new(f); let program: Vec = f .split(b',') .map(|o| { let o = o.unwrap(); let mut o = o.iter().peekable(); let sign = if o.peek() == Some(&&b'-') { -1 } else { 1 }; o.filter(|i| **i >= b'0' && **i <= b'9') .fold(0, |s, i| ((i - b'0') as i64 + 10 * s)) * sign }) .collect(); program.into() } } impl From> for Computer { fn from(program: Vec) -> Self { Computer { pos: 0, program: program, modes: Default::default(), } } } impl Computer { fn update(&mut self, u: Vec<(usize, i64)>) -> Computer { let mut computer = self.clone(); for (pos, val) in u { computer.program[pos] = val; } computer } fn run(&mut self) -> i64 { loop { let instruction = self.program[self.pos]; let instruction = ModedOpcode::from(instruction); self.modes = (&instruction).into(); let advance = match (&instruction).into() { Opcode::Add => self.add(), Opcode::Multiply => self.multiply(), Opcode::Input => self.input(), Opcode::Output => self.output(), Opcode::JumpIfTrue => self.jump_if_true(), Opcode::JumpIfFalse => self.jump_if_false(), Opcode::LessThan => self.less_than(), Opcode::Equals => self.equals(), Opcode::Halt => return self.program[0], }; self.pos += advance; } } fn get_a(&self) -> i64 { let a = self.program[self.pos + 1]; match self.modes.a { Mode::Position => self.program[a as usize], Mode::Immediate => a, } } fn set_a(&mut self, value: i64) { let a = self.program[self.pos + 1]; match self.modes.a { Mode::Position => self.program[a as usize] = value, Mode::Immediate => self.program[self.pos] = value, } } fn get_b(&self) -> i64 { let b = self.program[self.pos + 2]; match self.modes.b { Mode::Position => self.program[b as usize], Mode::Immediate => b, } } fn set_c(&mut self, value: i64) { let c = self.program[self.pos + 3]; match self.modes.c { Mode::Position => self.program[c as usize] = value, Mode::Immediate => self.program[self.pos] = value, } } fn add(&mut self) -> usize { self.set_c(self.get_a() + self.get_b()); 4 } fn multiply(&mut self) -> usize { self.set_c(self.get_a() * self.get_b()); 4 } fn input(&mut self) -> usize { let mut line = String::new(); print!("input: "); io::stdout().flush().expect("unable to flush output"); io::stdin() .lock() .read_line(&mut line) .expect("input failed"); let value = line.trim().parse().expect("numeric input required"); self.set_a(value); 2 } fn output(&mut self) -> usize { println!("output: {}", self.get_a()); 2 } fn jump_if_true(&mut self) -> usize { if self.get_a() > 0 { self.pos = self.get_b() as usize; return 0; } 3 } fn jump_if_false(&mut self) -> usize { if self.get_a() == 0 { self.pos = self.get_b() as usize; return 0; } 3 } fn less_than(&mut self) -> usize { if self.get_a() < self.get_b() { self.set_c(1) } else { self.set_c(0) } 4 } fn equals(&mut self) -> usize { if self.get_a() == self.get_b() { self.set_c(1) } else { self.set_c(0) } 4 } } #[cfg(test)] mod tests { use super::*; #[test] fn example1() { let program: Vec = vec![1, 9, 10, 3, 2, 3, 11, 0, 99, 30, 40, 50]; assert_eq!(Computer::from(program).run(), 3500) } #[test] fn example2() { let program: Vec = vec![1, 0, 0, 0, 99]; assert_eq!(Computer::from(program).run(), 2) } #[test] fn example3() { let program: Vec = vec![1, 1, 1, 4, 99, 5, 6, 0, 99]; assert_eq!(Computer::from(program).run(), 30) } }