diff --git a/2019/07/Cargo.toml b/2019/07/Cargo.toml index 567152c..0088cae 100644 --- a/2019/07/Cargo.toml +++ b/2019/07/Cargo.toml @@ -8,3 +8,4 @@ edition = "2018" [dependencies] itertools = "0.8.2" +intcode = { path = "../intcode" } diff --git a/2019/07/input b/2019/07/input new file mode 100644 index 0000000..d1550e6 --- /dev/null +++ b/2019/07/input @@ -0,0 +1 @@ +3,8,1001,8,10,8,105,1,0,0,21,30,51,76,101,118,199,280,361,442,99999,3,9,102,5,9,9,4,9,99,3,9,102,4,9,9,1001,9,3,9,102,2,9,9,101,2,9,9,4,9,99,3,9,1002,9,3,9,1001,9,4,9,102,5,9,9,101,3,9,9,1002,9,3,9,4,9,99,3,9,101,5,9,9,102,4,9,9,1001,9,3,9,1002,9,2,9,101,4,9,9,4,9,99,3,9,1002,9,2,9,1001,9,3,9,102,5,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,99 diff --git a/2019/07/src/main.rs b/2019/07/src/main.rs index b837d2b..b92fce7 100644 --- a/2019/07/src/main.rs +++ b/2019/07/src/main.rs @@ -1,212 +1,7 @@ -use itertools::Itertools; -use std::io; -use std::io::BufRead; -use std::ops::Range; - -#[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 next_operation(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 - } - } -} - -fn find_max(range: Range, input: &Vec) -> i64 { - range - .permutations(5) - .scan(0, |&mut mut acc, amps| { - let mut machines: Vec<_> = amps - .into_iter() - .map(|amp| Machine { - tape: input.clone(), - pos: 0, - params: vec![amp], - }) - .collect(); - for state in (0..5).cycle() { - let mut machine = machines.get_mut(state).unwrap(); - machine.params.insert(0, acc); - match execute_machine(&mut machine) { - Err(output) => acc = output, - Ok(_) => break, - } - } - Some(acc) - }) - .max() - .unwrap() -} - -fn execute_machine(machine: &mut Machine) -> Result { - loop { - match next_operation(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()), - } - } -} +use intcode::*; pub fn main() { - let input: Vec<_> = io::stdin() - .lock() - .lines() - .next() - .unwrap() - .unwrap() - .split(',') - .map(|n| n.parse().unwrap()) - .collect(); - + let input = read_input(); println!("Part 1: {}", find_max(0..5, &input)); println!("Part 2: {}", find_max(5..10, &input)); } diff --git a/2019/intcode/Cargo.toml b/2019/intcode/Cargo.toml new file mode 100644 index 0000000..dcd59b0 --- /dev/null +++ b/2019/intcode/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "intcode" +version = "0.1.0" +authors = ["kageru "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools = "0.8.2" diff --git a/2019/intcode/src/lib.rs b/2019/intcode/src/lib.rs new file mode 100644 index 0000000..d1294ab --- /dev/null +++ b/2019/intcode/src/lib.rs @@ -0,0 +1,204 @@ +use itertools::Itertools; +use std::io::{self, BufRead}; +use std::ops::Range; + +#[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 find_max(range: Range, input: &Vec) -> i64 { + range + .permutations(5) + .scan(0, |&mut mut acc, amps| { + let mut machines: Vec<_> = amps + .into_iter() + .map(|amp| Machine { + tape: input.clone(), + pos: 0, + params: vec![amp], + }) + .collect(); + for state in (0..5).cycle() { + let mut machine = machines.get_mut(state).unwrap(); + machine.params.insert(0, acc); + match execute_machine(&mut machine) { + Err(output) => acc = output, + Ok(_) => break, + } + } + Some(acc) + }) + .max() + .unwrap() +} + +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() +} + +#[cfg(test)] +mod tests; diff --git a/2019/intcode/src/tests.rs b/2019/intcode/src/tests.rs new file mode 100644 index 0000000..2a5bf3a --- /dev/null +++ b/2019/intcode/src/tests.rs @@ -0,0 +1,49 @@ +use super::*; + +#[allow(dead_code)] +fn parse_test_input(raw: &str) -> Vec { + raw.split(",").map(|x| x.parse().unwrap()).collect() +} + +#[test] +fn test_find_max() { + assert_eq!( + find_max( + 0..5, + &parse_test_input("3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0") + ), + 43210 + ); + assert_eq!( + find_max( + 0..5, + &parse_test_input("3,23,3,24,1002,24,10,24,1002,23,-1,23,101,5,23,23,1,24,23,23,4,23,99,0,0") + ), + 54321 + ); + assert_eq!( + find_max( + 0..5, + &parse_test_input("3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0") + ), + 65210 + ); +} + +#[test] +fn test_find_max_with_loops() { + assert_eq!( + find_max( + 5..10, + &parse_test_input("3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5") + ), + 139629729 + ); + assert_eq!( + find_max( + 5..10, + &parse_test_input("3,52,1001,52,-5,52,3,53,1,52,56,54,1007,54,5,55,1005,55,26,1001,54,-5,54,1105,1,12,1,53,54,53,1008,54,0,55,1001,55,1,55,2,53,55,53,4,53,1001,56,-1,56,1005,56,6,99,0,0,0,0,10") + ), + 18216 + ); +}