From b07f615aec8802ae494c289ca8e7951b25ae7d8b Mon Sep 17 00:00:00 2001 From: kageru Date: Mon, 9 Dec 2019 19:18:20 +0100 Subject: [PATCH] Use intcode module for day 5 --- 2019/05/Cargo.toml | 1 + 2019/05/src/main.rs | 162 +--------------------------- 2019/intcode/src/amplifier.rs | 167 +++++++++++++++++++++++++++++ 2019/intcode/src/lib.rs | 193 ++++------------------------------ 2019/intcode/src/tests.rs | 80 ++++++++++++++ 5 files changed, 271 insertions(+), 332 deletions(-) create mode 100644 2019/intcode/src/amplifier.rs diff --git a/2019/05/Cargo.toml b/2019/05/Cargo.toml index 5e681e5..c483773 100644 --- a/2019/05/Cargo.toml +++ b/2019/05/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +intcode = { path = "../intcode" } diff --git a/2019/05/src/main.rs b/2019/05/src/main.rs index 1a3601c..4aed928 100644 --- a/2019/05/src/main.rs +++ b/2019/05/src/main.rs @@ -1,161 +1,7 @@ -use std::io; -use std::io::BufRead; - -#[derive(Debug)] -enum Operation { - Add { x: i32, y: i32, addr: usize }, - Multiply { x: i32, y: i32, addr: usize }, - Input { value: i32, addr: usize }, - Output { value: i32 }, - JumpIfTrue { value: i32, addr: i32 }, - JumpIfFalse { value: i32, addr: i32 }, - LessThan { first: i32, second: i32, addr: i32 }, - Equals { first: i32, second: i32, addr: i32 }, -} - -#[derive(Debug)] -enum Mode { - Immediate, - Position, -} - -enum ParameterPosition { - First, - Second, -} - -impl Into for ParameterPosition { - fn into(self) -> usize { - match self { - ParameterPosition::First => 2, - ParameterPosition::Second => 3, - } - } -} - -impl Into for &char { - fn into(self) -> Mode { - match self { - '0' => Mode::Position, - '1' => Mode::Immediate, - _ => unreachable!(), - } - } -} - -fn get_next(input: &[i32], pos: &mut i32, mode: Mode) -> i32 { - let value = input[*pos as usize]; - let next = match mode { - Mode::Position => input[value as usize], - Mode::Immediate => value, - }; - *pos += 1; - next -} - -fn get_mode(raw_opcode: &[char], pos: ParameterPosition) -> Mode { - raw_opcode.get::(pos.into()).unwrap_or(&'0').into() -} - -fn next_operation(input: &[i32], pos: &mut i32, inp: i32) -> Option { - let next = get_next(input, pos, Mode::Immediate); - let mut raw_opcode: Vec<_> = next.to_string().chars().collect(); - raw_opcode.reverse(); - match raw_opcode[0] { - '1' => Some(Operation::Add { - x: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - y: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(input, pos, Mode::Immediate) as usize, - }), - '2' => Some(Operation::Multiply { - x: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - y: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(input, pos, Mode::Immediate) as usize, - }), - '3' => Some(Operation::Input { - value: inp, - addr: get_next(input, pos, Mode::Immediate) as usize, - }), - '4' => Some(Operation::Output { - value: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - }), - '5' => Some(Operation::JumpIfTrue { - value: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - addr: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - }), - '6' => Some(Operation::JumpIfFalse { - value: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - addr: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - }), - '7' => Some(Operation::LessThan { - first: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - second: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(input, pos, Mode::Immediate), - }), - '8' => Some(Operation::Equals { - first: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::First)), - second: get_next(input, pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(input, pos, Mode::Immediate), - }), - '9' => None, - _ => unreachable!(), - } -} - -fn execute(op: Operation, input: &mut Vec, pos: &mut i32) { - match op { - Operation::Add { x, y, addr } => input[addr as usize] = x + y, - Operation::Multiply { x, y, addr } => input[addr as usize] = x * y, - Operation::Input { value, addr } => input[addr] = value, - Operation::Output { value } => { - if value != 0 { - println!("{}", value) - } - } - Operation::JumpIfTrue { value, addr } => { - if value != 0 { - *pos = addr - } - } - Operation::JumpIfFalse { value, addr } => { - if value == 0 { - *pos = addr - } - } - Operation::LessThan { - first, - second, - addr, - } => input[addr as usize] = (first < second) as i32, - Operation::Equals { - first, - second, - addr, - } => input[addr as usize] = (first == second) as i32, - } -} +use intcode::*; pub fn main() { - let mut input: Vec = io::stdin() - .lock() - .lines() - .next() - .unwrap() - .unwrap() - .split(',') - .map(|n| n.parse().unwrap()) - .collect(); - let mut pos = 0; - let mut part2_input = input.clone(); - - println!("Part 1:"); - while let Some(op) = next_operation(&input, &mut pos, 1) { - execute(op, &mut input, &mut pos); - } - - pos = 0; - println!("Part 2:"); - while let Some(op) = next_operation(&part2_input, &mut pos, 5) { - execute(op, &mut part2_input, &mut pos); - } + let input = read_input(); + println!("Part 1: {}", run_for_input(&input, &mut 0, vec![1])); + println!("Part 2: {}", run_for_input(&input, &mut 0, vec![5])); } diff --git a/2019/intcode/src/amplifier.rs b/2019/intcode/src/amplifier.rs new file mode 100644 index 0000000..aec095c --- /dev/null +++ b/2019/intcode/src/amplifier.rs @@ -0,0 +1,167 @@ +pub struct Amplifier { + pub pos: i64, + pub tape: Vec, + pub params: Vec, +} + +impl Amplifier { + pub fn run(&mut self) -> Result { + loop { + match self.decode_next() { + Some(op) => { + if let Some(o) = self.execute(op) { + return Err(o); + } + } + None => return Ok(self.params.pop().unwrap()), + } + } + } + + fn get_next(&mut self, mode: Mode) -> i64 { + let value = self.tape[self.pos as usize]; + let next = match mode { + Mode::Position => self.tape[value as usize], + Mode::Immediate => value, + }; + self.pos += 1; + next + } + + fn decode_next(&mut self) -> Option { + let next = self.get_next(Mode::Immediate); + let mut raw_opcode: Vec<_> = next.to_string().chars().collect(); + raw_opcode.reverse(); + match raw_opcode[0] { + '1' => Some(Operation::Add { + x: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + y: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + addr: self.get_next(Mode::Immediate) as usize, + }), + '2' => Some(Operation::Multiply { + x: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + y: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + addr: self.get_next(Mode::Immediate) as usize, + }), + '3' => Some(Operation::Input { + value: self.params.pop().unwrap(), + addr: self.get_next(Mode::Immediate) as usize, + }), + '4' => Some(Operation::Output { + value: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + }), + '5' => Some(Operation::JumpIfTrue { + value: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + addr: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + }), + '6' => Some(Operation::JumpIfFalse { + value: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + addr: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + }), + '7' => Some(Operation::LessThan { + first: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + second: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + addr: self.get_next(Mode::Immediate), + }), + '8' => Some(Operation::Equals { + first: self.get_next(get_mode(&raw_opcode, ParameterPosition::First)), + second: self.get_next(get_mode(&raw_opcode, ParameterPosition::Second)), + addr: self.get_next(Mode::Immediate), + }), + '9' => None, + _ => unreachable!(), + } + } + + fn execute(&mut self, op: Operation) -> Option { + match op { + Operation::Add { x, y, addr } => { + self.tape[addr as usize] = x + y; + None + } + Operation::Multiply { x, y, addr } => { + self.tape[addr as usize] = x * y; + None + } + Operation::Input { value, addr } => { + self.tape[addr] = value; + None + } + Operation::Output { value } => Some(value), + Operation::JumpIfTrue { value, addr } => { + if value != 0 { + self.pos = addr + } + None + } + Operation::JumpIfFalse { value, addr } => { + if value == 0 { + self.pos = addr + } + None + } + Operation::LessThan { + first, + second, + addr, + } => { + self.tape[addr as usize] = (first < second) as i64; + None + } + Operation::Equals { + first, + second, + addr, + } => { + self.tape[addr as usize] = (first == second) as i64; + None + } + } + } +} + +fn get_mode(raw_opcode: &[char], pos: ParameterPosition) -> Mode { + raw_opcode.get::(pos.into()).unwrap_or(&'0').into() +} + +#[derive(Debug)] +enum Operation { + Add { x: i64, y: i64, addr: usize }, + Multiply { x: i64, y: i64, addr: usize }, + Input { value: i64, addr: usize }, + Output { value: i64 }, + JumpIfTrue { value: i64, addr: i64 }, + JumpIfFalse { value: i64, addr: i64 }, + LessThan { first: i64, second: i64, addr: i64 }, + Equals { first: i64, second: i64, addr: i64 }, +} + +#[derive(Debug)] +enum Mode { + Immediate, + Position, +} + +enum ParameterPosition { + First, + Second, +} + +impl Into for ParameterPosition { + fn into(self) -> usize { + match self { + ParameterPosition::First => 2, + ParameterPosition::Second => 3, + } + } +} + +impl Into for &char { + fn into(self) -> Mode { + match self { + '0' => Mode::Position, + '1' => Mode::Immediate, + _ => unreachable!(), + } + } +} diff --git a/2019/intcode/src/lib.rs b/2019/intcode/src/lib.rs index 2ff62c5..5cf1262 100644 --- a/2019/intcode/src/lib.rs +++ b/2019/intcode/src/lib.rs @@ -2,174 +2,28 @@ use itertools::Itertools; use std::convert::TryFrom; use std::io::{self, BufRead}; use std::ops::Range; +use amplifier::*; +mod amplifier; -#[derive(Debug)] -enum Operation { - Add { x: i64, y: i64, addr: usize }, - Multiply { x: i64, y: i64, addr: usize }, - Input { value: i64, addr: usize }, - Output { value: i64 }, - JumpIfTrue { value: i64, addr: i64 }, - JumpIfFalse { value: i64, addr: i64 }, - LessThan { first: i64, second: i64, addr: i64 }, - Equals { first: i64, second: i64, addr: i64 }, -} - -#[derive(Debug)] -enum Mode { - Immediate, - Position, -} - -enum ParameterPosition { - First, - Second, -} - -struct Machine { - pos: i64, - tape: Vec, - params: Vec, -} - -impl Into for ParameterPosition { - fn into(self) -> usize { - match self { - ParameterPosition::First => 2, - ParameterPosition::Second => 3, - } - } -} - -impl Into for &char { - fn into(self) -> Mode { - match self { - '0' => Mode::Position, - '1' => Mode::Immediate, - _ => unreachable!(), - } - } -} - -fn get_next(input: &[i64], pos: &mut i64, mode: Mode) -> i64 { - let value = input[*pos as usize]; - let next = match mode { - Mode::Position => input[value as usize], - Mode::Immediate => value, - }; - *pos += 1; - next -} - -fn get_mode(raw_opcode: &[char], pos: ParameterPosition) -> Mode { - raw_opcode.get::(pos.into()).unwrap_or(&'0').into() -} - -#[rustfmt::skip] -fn decode_next(machine: &mut Machine) -> Option { - let next = get_next(&machine.tape, &mut machine.pos, Mode::Immediate); - let mut raw_opcode: Vec<_> = next.to_string().chars().collect(); - raw_opcode.reverse(); - match raw_opcode[0] { - '1' => Some(Operation::Add { - x: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - y: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize, - }), - '2' => Some(Operation::Multiply { - x: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - y: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize, - }), - '3' => Some(Operation::Input { - value: machine.params.pop().unwrap(), - addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate) as usize, - }), - '4' => Some(Operation::Output { - value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - }), - '5' => Some(Operation::JumpIfTrue { - value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - addr: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - }), - '6' => Some(Operation::JumpIfFalse { - value: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - addr: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - }), - '7' => Some(Operation::LessThan { - first: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - second: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate), - }), - '8' => Some(Operation::Equals { - first: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::First)), - second: get_next(&machine.tape, &mut machine.pos, get_mode(&raw_opcode, ParameterPosition::Second)), - addr: get_next(&machine.tape, &mut machine.pos, Mode::Immediate), - }), - '9' => None, - _ => unreachable!(), - } -} - -fn execute(op: Operation, input: &mut Vec, pos: &mut i64) -> Option { - match op { - Operation::Add { x, y, addr } => { - input[addr as usize] = x + y; - None - } - Operation::Multiply { x, y, addr } => { - input[addr as usize] = x * y; - None - } - Operation::Input { value, addr } => { - input[addr] = value; - None - } - Operation::Output { value } => Some(value), - Operation::JumpIfTrue { value, addr } => { - if value != 0 { - *pos = addr - } - None - } - Operation::JumpIfFalse { value, addr } => { - if value == 0 { - *pos = addr - } - None - } - Operation::LessThan { - first, - second, - addr, - } => { - input[addr as usize] = (first < second) as i64; - None - } - Operation::Equals { - first, - second, - addr, - } => { - input[addr as usize] = (first == second) as i64; - None - } - } -} - -pub fn run_for_input(input: &Vec, acc: &mut i64, amps: Vec) -> i64 { - let mut machines: Vec<_> = amps +/** + * Construct amplifiers and run all of them for a single given input tape + * and a vector of amplifier configurations. + * + * Returns the return value of the first halted amplifier. + */ +pub fn run_for_input(input: &Vec, acc: &mut i64, amp_phases: Vec) -> i64 { + let mut amplifiers: Vec<_> = amp_phases .into_iter() - .map(|amp| Machine { + .map(|amp| Amplifier { tape: input.clone(), pos: 0, params: vec![amp], }) .collect(); - for state in (0..machines.len()).cycle() { - let mut machine = machines.get_mut(state).unwrap(); - machine.params.insert(0, *acc); - match execute_machine(&mut machine) { + for state in (0..amplifiers.len()).cycle() { + let amplifier = amplifiers.get_mut(state).unwrap(); + amplifier.params.insert(0, *acc); + match amplifier.run() { Err(output) => *acc = output, Ok(_) => break, } @@ -177,6 +31,10 @@ pub fn run_for_input(input: &Vec, acc: &mut i64, amps: Vec) -> i64 { *acc } +/** + * Convenience method for day 7. + * Will try all combinations of parameters in the given range and return the maximum result. + */ pub fn find_max(range: Range, input: &Vec) -> Option { usize::try_from(range.end - range.start) .ok() @@ -190,19 +48,6 @@ pub fn find_max(range: Range, input: &Vec) -> Option { }) } -fn execute_machine(machine: &mut Machine) -> Result { - loop { - match decode_next(machine) { - Some(op) => { - if let Some(o) = execute(op, &mut machine.tape, &mut machine.pos) { - return Err(o); - } - } - None => return Ok(machine.params.pop().unwrap()), - } - } -} - #[rustfmt::skip] pub fn read_input() -> Vec { io::stdin().lock().lines().next().unwrap().unwrap().split(',').map(|n| n.parse().unwrap()).collect() diff --git a/2019/intcode/src/tests.rs b/2019/intcode/src/tests.rs index 39d6cdc..986de99 100644 --- a/2019/intcode/src/tests.rs +++ b/2019/intcode/src/tests.rs @@ -49,3 +49,83 @@ fn test_find_max_with_loops() { Some(18216) ); } + +#[test] +fn test_position_less_than() { + assert_eq!( + run_for_input( + &parse_test_input("3,9,8,9,10,9,4,9,99,-1,8"), + &mut 0, + vec![8] + ), + 1 + ); + assert_eq!( + run_for_input( + &parse_test_input("3,9,8,9,10,9,4,9,99,-1,8"), + &mut 0, + vec![7] + ), + 0 + ); +} + +#[test] +fn test_position_equals() { + assert_eq!( + run_for_input( + &parse_test_input("3,9,7,9,10,9,4,9,99,-1,8"), + &mut 0, + vec![8] + ), + 0 + ); + assert_eq!( + run_for_input( + &parse_test_input("3,9,7,9,10,9,4,9,99,-1,8"), + &mut 0, + vec![7] + ), + 1 + ); +} + +#[test] +fn test_immediate_less_than() { + assert_eq!( + run_for_input( + &parse_test_input("3,3,1107,-1,8,3,4,3,99"), + &mut 0, + vec![8] + ), + 0 + ); + assert_eq!( + run_for_input( + &parse_test_input("3,3,1107,-1,8,3,4,3,99"), + &mut 0, + vec![7] + ), + 1 + ); +} + +#[test] +fn test_immediate_equals() { + assert_eq!( + run_for_input( + &parse_test_input("3,3,1108,-1,8,3,4,3,99"), + &mut 0, + vec![8] + ), + 1 + ); + assert_eq!( + run_for_input( + &parse_test_input("3,3,1108,-1,8,3,4,3,99"), + &mut 0, + vec![7] + ), + 0 + ); +}