From 4e908b8bf9a364969e08780947f6282061acc510 Mon Sep 17 00:00:00 2001 From: Stefan Schwarz Date: Tue, 21 Apr 2020 23:38:00 +0200 Subject: [PATCH] day9 part2 --- Cargo.toml | 6 +- src/day9/main.rs | 440 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 src/day9/main.rs diff --git a/Cargo.toml b/Cargo.toml index ad2002f..a5a2b2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,4 +28,8 @@ path = "src/day5/main.rs" [[bin]] name = "day7" -path = "src/day7/main.rs" \ No newline at end of file +path = "src/day7/main.rs" + +[[bin]] +name = "day9" +path = "src/day9/main.rs" \ No newline at end of file diff --git a/src/day9/main.rs b/src/day9/main.rs new file mode 100644 index 0000000..d33d842 --- /dev/null +++ b/src/day9/main.rs @@ -0,0 +1,440 @@ +use std::collections::VecDeque; +use std::env; +use std::fs::File; +use std::io::{self, 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: i64) -> 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: 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, + 9 => Opcode::RelativeAdj, + 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 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), + } + } +} + +struct Computer { + pos: usize, + rel: i64, + 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 i64 + 10 * s)) + * sign + }) + .collect(); + Computer::from(program) + } +} + +impl From> for Computer { + fn from(program: Vec) -> Self { + Computer { + pos: 0, + rel: 0, + program: program, + modes: Default::default(), + inputs: VecDeque::new(), + outputs: VecDeque::new(), + halted: false, + } + } +} + +impl Computer { + fn clone_with_modified_program(&self, u: Vec<(usize, i64)>) -> Computer { + let mut computer = self.clone(); + for (pos, val) in u { + computer.program[pos] = val; + } + computer + } + + fn clone_with_input(&self, input: Vec) -> Computer { + let mut computer = self.clone(); + computer.inputs = VecDeque::from(input); + computer + } + + fn run(&mut self) { + while !self.halted { + self.step(); + } + } + + 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; + } + } + } + + fn run_must_output(&mut self) -> i64 { + self.run_until_output() + .expect("computer halted without output") + } + + fn run_io(&mut self, input: Vec) -> Option { + input.iter().for_each(|i| self.inputs.push_back(*i)); + 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) -> i64 { + *self.program.get(pos).unwrap_or(&0) + } + + fn get(&self, v: &Var) -> i64 { + 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) -> i64 { + self.get(&Var::A) + } + + fn get_b(&self) -> i64 { + self.get(&Var::B) + } + + fn set(&mut self, v: &Var, value: i64) { + 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 { + let missing = (want - length) + 5; + self.program.extend_from_slice(&vec![0; missing]) + } + self.program[want] = value; + } + + fn set_a(&mut self, value: i64) { + self.set(&Var::A, value) + } + + fn set_c(&mut self, value: i64) { + 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 + } +} + +fn main() -> io::Result<()> { + let filename = env::args().nth(1).expect("provide file as first param"); + let computer = Computer::from(File::open(filename)?); + + // part1 + let result = computer.clone_with_input(vec![1]).run_must_output(); + println!("{:?}", result); + + // part2 + let result = computer.clone_with_input(vec![2]).run_must_output(); + println!("{:?}", result); + + Ok(()) +} + +#[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!(Vec::from(computer.outputs), program) + } + + #[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 + ); + } +}