use std::collections::VecDeque; use std::fs::File; use std::io::{BufRead, BufReader}; #[derive(Copy, Clone)] enum Var { A = 1, B, C, } #[derive(Copy, Clone, Debug)] enum Mode { Position, Immediate, Relative, } impl Default for Mode { fn default() -> Self { Mode::Position } } impl From for Mode { fn from(b: isize) -> Self { match b { 0 => Mode::Position, 1 => Mode::Immediate, 2 => Mode::Relative, _ => panic!("invalid mode {}", b), } } } #[derive(Copy, Clone, Default, Debug)] struct Modes { a: Mode, b: Mode, c: Mode, } impl Modes { fn mode_for(&self, v: &Var) -> Mode { match v { Var::A => self.a, Var::B => self.b, Var::C => self.c, } } } 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, RelativeAdj, Halt, } impl From<&ModedOpcode> for Opcode { fn from(mo: &ModedOpcode) -> Self { mo.op } } impl From for Opcode { fn from(i: isize) -> 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, 9 => Opcode::RelativeAdj, 99 => Opcode::Halt, _ => panic!("invalid opcode {}", i), } } } struct ModedOpcode { modes: Modes, op: Opcode, } impl From for ModedOpcode { fn from(i: isize) -> Self { let code = i % 100; let modes = i / 100; let a = (modes % 10) / 1; let b = (modes % 100) / 10; let c = (modes % 1000) / 100; let modes = (Mode::from(a), Mode::from(b), Mode::from(c)); ModedOpcode { modes: Modes::from(modes), op: Opcode::from(code), } } } pub struct Computer { pos: usize, rel: isize, program: Vec, modes: Modes, inputs: VecDeque, outputs: VecDeque, halted: bool, } impl Clone for Computer { fn clone(&self) -> Self { Computer::from(self.program.clone()) } } 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 isize + 10 * s)) * sign }) .collect(); Computer::from(program) } } impl From> for Computer { fn from(program: Vec) -> Self { Computer { pos: 0, rel: 0, program, modes: Default::default(), inputs: VecDeque::new(), outputs: VecDeque::new(), halted: false, } } } impl Computer { pub fn get_outputs(&self) -> &VecDeque { &self.outputs } pub fn pop_output(&mut self) -> Option { self.outputs.pop_front() } pub fn clone_with_modified_program(&self, u: Vec<(usize, isize)>) -> Computer { let mut computer = self.clone(); for (pos, val) in u { computer.program[pos] = val; } computer } pub fn clone_with_input(&self, input: Vec) -> Computer { let mut computer = self.clone(); computer.inputs = VecDeque::from(input); computer } pub fn run(&mut self) { while !self.halted { self.step(); } } pub fn run_until_output(&mut self) -> Option { loop { self.step(); if self.outputs.len() > 0 { return self.outputs.pop_front(); } if self.halted { return None; } } } pub fn run_must_output(&mut self) -> isize { self.run_until_output() .expect("computer halted without output") } pub fn run_io(&mut self, input: isize) -> Option { self.inputs.push_back(input); self.run_until_output() } fn step(&mut self) { let instruction = self.program[self.pos]; let instruction = ModedOpcode::from(instruction); self.modes = Modes::from(&instruction); let advance = match Opcode::from(&instruction) { 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::RelativeAdj => self.relative_adj(), Opcode::Halt => self.halt(), }; self.pos += advance; } fn get_pos(&self, pos: usize) -> isize { *self.program.get(pos).unwrap_or(&0) } fn get(&self, v: &Var) -> isize { let want = self.pos + *v as usize; let want = match self.modes.mode_for(v) { Mode::Position => self.get_pos(want as usize) as usize, Mode::Immediate => want, Mode::Relative => (self.rel + self.get_pos(want)) as usize, }; self.get_pos(want) } fn get_a(&self) -> isize { self.get(&Var::A) } fn get_b(&self) -> isize { self.get(&Var::B) } fn set(&mut self, v: &Var, value: isize) { let length = self.program.len(); let want = self.pos + *v as usize; let want = match self.modes.mode_for(v) { Mode::Position => self.get_pos(want) as usize, Mode::Immediate => want, Mode::Relative => (self.rel + self.get_pos(want)) as usize, }; if length <= want { self.program.resize(want + 1, 0); } self.program[want] = value; } fn set_a(&mut self, value: isize) { self.set(&Var::A, value) } fn set_c(&mut self, value: isize) { self.set(&Var::C, 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 value = self.inputs.pop_front().expect("no input provided"); self.set_a(value); 2 } fn output(&mut self) -> usize { self.outputs.push_back(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 } fn relative_adj(&mut self) -> usize { self.rel = self.rel + self.get_a(); 2 } fn halt(&mut self) -> usize { self.halted = true; 0 } } #[cfg(test)] mod tests { use super::*; #[test] fn example1() { let program = vec![ 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99, ]; let mut computer = Computer::from(program.clone()); computer.run(); assert_eq!(computer.get_outputs().iter().eq(program.iter()), true) } #[test] fn example2() { assert_eq!( Computer::from(vec![1102, 34915192, 34915192, 7, 4, 7, 99, 0]).run_must_output(), 1219070632396864 ) } #[test] fn example3() { assert_eq!( Computer::from(vec![104, 1125899906842624, 99]).run_must_output(), 1125899906842624 ) } #[test] fn day5exampe1() { let computer = Computer::from(vec![3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8]); assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); } #[test] fn day5exampe2() { let computer = Computer::from(vec![3, 3, 1108, -1, 8, 3, 4, 3, 99]); assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); } #[test] fn day5exampe3() { let computer = Computer::from(vec![ 3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9, ]); assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); let computer = Computer::from(vec![3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]); assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); } #[test] fn day5exampe4() { let computer = Computer::from(vec![ 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, 20, 1105, 1, 46, 98, 99, ]); assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 999); assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1000); assert_eq!(computer.clone_with_input(vec![9]).run_must_output(), 1001); } #[test] fn day9part1() { let computer = Computer::from(File::open("input9").unwrap()); assert_eq!( computer.clone_with_input(vec![1]).run_must_output(), 3063082071 ); } }