diff --git a/aoc2019/src/day02.rs b/aoc2019/src/day02.rs index 96717dd..776d738 100644 --- a/aoc2019/src/day02.rs +++ b/aoc2019/src/day02.rs @@ -3,67 +3,50 @@ use std::fmt::Write; use aoc::err; use aoc::Result; -const INPUT: &str = include_str!("../input/day02.txt"); -const PART2_EXPECTED: usize = 19_690_720; +use crate::intcode::{parse_memory, Intcode}; -fn parse_intcode(input: &str) -> Result> { - input - .trim_end() - .split(',') - .map(|x| x.parse().map_err(|e| err!("couldn't parse int: {}", e))) - .collect() -} +const INPUT: &str = include_str!("../input/day02.txt"); +const PART2_EXPECTED: i64 = 19_690_720; pub fn run() -> Result { let mut res = String::with_capacity(128); - let intcode = parse_intcode(INPUT)?; + let memory = parse_memory(INPUT)?; - writeln!(res, "part 1: {}", part1(&mut intcode.clone())?)?; - writeln!(res, "part 2: {}", part2(&intcode, PART2_EXPECTED)?)?; + writeln!(res, "part 1: {}", part1(memory.clone())?)?; + writeln!(res, "part 2: {}", part2(&memory, PART2_EXPECTED)?)?; Ok(res) } -fn eval(intcode: &mut [usize]) -> Result<()> { - let mut pc = 0; - - while intcode[pc] != 99 { - let op1 = intcode[pc + 1]; - let op2 = intcode[pc + 2]; - let res = intcode[pc + 3]; - - match intcode[pc] { - 1 => intcode[res] = intcode[op1] + intcode[op2], - 2 => intcode[res] = intcode[op1] * intcode[op2], - _ => return Err(err!("unknown opcode: {}", intcode[pc])), - }; - - pc += 4; - } - - Ok(()) -} - -fn part1(input: &mut Vec) -> Result { +fn part1(mut input: Vec) -> Result { input[1] = 12; input[2] = 2; - eval(input)?; + let mut intcode = Intcode::with_memory(input); + intcode.run()?; - Ok(input[0]) + intcode + .get_day02_output() + .ok_or_else(|| err!("intcode memory was empty!")) } -fn part2(input: &[usize], res: usize) -> Result { +fn part2(input: &[i64], res: i64) -> Result { for (noun, verb) in (0..=99).flat_map(|noun| (0..=99).map(move |verb| (noun, verb))) { let mut test_input = input.to_vec(); test_input[1] = noun; test_input[2] = verb; - eval(&mut test_input)?; + let mut intcode = Intcode::with_memory(test_input); + intcode.run()?; - if test_input[0] == res { - return Ok(noun * 100 + verb); + match intcode.get_day02_output() { + Some(val) => { + if val == res { + return Ok(noun * 100 + verb); + } + } + None => return Err(err!("intcode memory was empty!")), } } @@ -79,36 +62,39 @@ mod tests { #[test] fn part1_provided() { - let mut intcode = parse_intcode("1,9,10,3,2,3,11,0,99,30,40,50").unwrap(); - eval(&mut intcode).unwrap(); - assert_eq!(intcode, &[3500, 9, 10, 70, 2, 3, 11, 0, 99, 30, 40, 50]); + let mut intcode = Intcode::new("1,9,10,3,2,3,11,0,99,30,40,50").unwrap(); + intcode.run().unwrap(); + assert_eq!( + &intcode.memory, + &[3500, 9, 10, 70, 2, 3, 11, 0, 99, 30, 40, 50] + ); - let mut intcode = parse_intcode("1,0,0,0,99").unwrap(); - eval(&mut intcode).unwrap(); - assert_eq!(intcode, &[2, 0, 0, 0, 99]); + let mut intcode = Intcode::new("1,0,0,0,99").unwrap(); + intcode.run().unwrap(); + assert_eq!(&intcode.memory, &[2, 0, 0, 0, 99]); - let mut intcode = parse_intcode("2,3,0,3,99").unwrap(); - eval(&mut intcode).unwrap(); - assert_eq!(intcode, &[2, 3, 0, 6, 99]); + let mut intcode = Intcode::new("2,3,0,3,99").unwrap(); + intcode.run().unwrap(); + assert_eq!(&intcode.memory, &[2, 3, 0, 6, 99]); - let mut intcode = parse_intcode("2,4,4,5,99,0").unwrap(); - eval(&mut intcode).unwrap(); - assert_eq!(intcode, &[2, 4, 4, 5, 99, 9801]); + let mut intcode = Intcode::new("2,4,4,5,99,0").unwrap(); + intcode.run().unwrap(); + assert_eq!(&intcode.memory, &[2, 4, 4, 5, 99, 9801]); - let mut intcode = parse_intcode("1,1,1,4,99,5,6,0,99").unwrap(); - eval(&mut intcode).unwrap(); - assert_eq!(intcode, &[30, 1, 1, 4, 2, 5, 6, 0, 99]); + let mut intcode = Intcode::new("1,1,1,4,99,5,6,0,99").unwrap(); + intcode.run().unwrap(); + assert_eq!(&intcode.memory, &[30, 1, 1, 4, 2, 5, 6, 0, 99]); } #[test] fn part1_real() { - let mut intcode = parse_intcode(INPUT).unwrap(); - assert_eq!(part1(&mut intcode).unwrap(), 6568671); + let memory = parse_memory(INPUT).unwrap(); + assert_eq!(part1(memory).unwrap(), 6568671); } #[test] fn part2_real() { - let intcode = parse_intcode(INPUT).unwrap(); - assert_eq!(part2(&intcode, PART2_EXPECTED).unwrap(), 3951); + let memory = parse_memory(INPUT).unwrap(); + assert_eq!(part2(&memory, PART2_EXPECTED).unwrap(), 3951); } } diff --git a/aoc2019/src/day05.rs b/aoc2019/src/day05.rs index 212a3b7..c043f36 100644 --- a/aoc2019/src/day05.rs +++ b/aoc2019/src/day05.rs @@ -3,6 +3,8 @@ use std::fmt::Write; use aoc::err; use aoc::Result; +use crate::intcode::Intcode; + const INPUT: &str = include_str!("../input/day05.txt"); pub fn run() -> Result { @@ -19,7 +21,7 @@ fn part1(input: &str) -> Result { intcode.add_input(1); intcode.run()?; intcode - .get_output() + .get_day05_output() .ok_or_else(|| err!("intcode gave no output")) } @@ -28,291 +30,10 @@ fn part2(input: &str) -> Result { intcode.add_input(5); intcode.run()?; intcode - .get_output() + .get_day05_output() .ok_or_else(|| err!("intcode gave no output")) } -enum Parameter { - Position(usize), - Immediate(i64), -} - -impl Parameter { - fn new(mode: i64, val: Option) -> Result { - let val = val.ok_or_else(|| err!("parameter value out of bounds"))?; - let mode = mode % 10; - - match mode { - 0 => { - if val < 0 { - Err(err!("negative value for position parameter: {}", val)) - } else { - let val = val as usize; - Ok(Parameter::Position(val)) - } - } - 1 => Ok(Parameter::Immediate(val)), - _ => Err(err!("wrong mode for parameter: {}", mode)), - } - } - - fn get(&self, memory: &[i64]) -> Result { - match self { - Parameter::Position(address) => match memory.get(*address) { - Some(val) => Ok(*val), - None => Err(err!("read out of bounds at address {}", address)), - }, - Parameter::Immediate(value) => Ok(*value), - } - } - - fn set(&self, memory: &mut [i64], value: i64) -> Result<()> { - match self { - Parameter::Position(address) => { - let cell = memory - .get_mut(*address) - .ok_or_else(|| err!("memory write out of bounds"))?; - *cell = value; - Ok(()) - } - Parameter::Immediate(_) => Err(err!("cannot write to immediate parameter")), - } - } -} - -enum Opcode { - Add(Parameter, Parameter, Parameter), - Multiply(Parameter, Parameter, Parameter), - Input(Parameter), - Output(Parameter), - JumpTrue(Parameter, Parameter), - JumpFalse(Parameter, Parameter), - LessThan(Parameter, Parameter, Parameter), - Equals(Parameter, Parameter, Parameter), - Halt, -} - -struct Intcode { - memory: Vec, - input: Vec, - output: Vec, - ip: usize, - next_input: usize, -} - -impl Intcode { - fn new(input: &str) -> Result { - let memory = input - .trim_end() - .split(',') - .map(|x| x.parse().map_err(|e| err!("couldn't parse int: {}", e))) - .collect::>>()?; - - Ok(Intcode::with_memory(memory)) - } - - fn with_memory(memory: Vec) -> Self { - Intcode { - memory, - input: Vec::new(), - output: Vec::new(), - ip: 0, - next_input: 0, - } - } - - fn add_input(&mut self, value: i64) { - self.input.push(value); - } - - fn get_opcode(&self) -> Result { - let instruction = self.memory[self.ip]; - - let opcode = instruction % 100; - let mode1 = instruction / 100; - let mode2 = instruction / 1000; - let mode3 = instruction / 10000; - match opcode { - 1 => { - let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; - - if let Parameter::Immediate(_) = dst { - Err(err!("add: destination parameter can't be immediate")) - } else { - Ok(Opcode::Add(op1, op2, dst)) - } - } - 2 => { - let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; - - if let Parameter::Immediate(_) = dst { - Err(err!("multiply: destination parameter can't be immediate")) - } else { - Ok(Opcode::Multiply(op1, op2, dst)) - } - } - 3 => { - let dst = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - - if let Parameter::Immediate(_) = dst { - Err(err!("input: destination parameter can't be immediate")) - } else { - Ok(Opcode::Input(dst)) - } - } - 4 => { - let op = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - - Ok(Opcode::Output(op)) - } - 5 => { - let test = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let dst = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - - Ok(Opcode::JumpTrue(test, dst)) - } - 6 => { - let test = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let dst = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - - Ok(Opcode::JumpFalse(test, dst)) - } - 7 => { - let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; - - if let Parameter::Immediate(_) = dst { - Err(err!("less than: destination parameter can't be immediate")) - } else { - Ok(Opcode::LessThan(op1, op2, dst)) - } - } - 8 => { - let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; - let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; - let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; - - if let Parameter::Immediate(_) = dst { - Err(err!("equals: destination parameter can't be immediate")) - } else { - Ok(Opcode::Equals(op1, op2, dst)) - } - } - 99 => Ok(Opcode::Halt), - _ => Err(err!("unknown opcode: {}", opcode)), - } - } - - fn run(&mut self) -> Result<()> { - loop { - if self.ip >= self.memory.len() { - return Err(err!("reached end of program without halting")); - } - - let opcode = self.get_opcode()?; - - match opcode { - Opcode::Add(op1, op2, dst) => { - let val1 = op1.get(&self.memory)?; - let val2 = op2.get(&self.memory)?; - - dst.set(&mut self.memory, val1 + val2)?; - - self.ip += 4; - } - Opcode::Multiply(op1, op2, dst) => { - let val1 = op1.get(&self.memory)?; - let val2 = op2.get(&self.memory)?; - - dst.set(&mut self.memory, val1 * val2)?; - - self.ip += 4; - } - Opcode::Input(dst) => { - let input = if self.next_input < self.input.len() { - let res = self.input[self.next_input]; - self.next_input += 1; - Ok(res) - } else { - Err(err!("tried to read input but it was empty")) - }?; - dst.set(&mut self.memory, input)?; - - self.ip += 2; - } - Opcode::Output(op) => { - let val = op.get(&self.memory)?; - self.output.push(val); - - self.ip += 2; - } - Opcode::JumpTrue(test, dst) => { - let val = test.get(&self.memory)?; - let dst = dst.get(&self.memory)?; - if dst < 0 { - return Err(err!("dst must be a valid address: {}", dst)); - } - - if val != 0 { - self.ip = dst as usize; - } else { - self.ip += 3; - } - } - Opcode::JumpFalse(test, dst) => { - let val = test.get(&self.memory)?; - let dst = dst.get(&self.memory)?; - if dst < 0 { - return Err(err!("dst must be a valid address: {}", dst)); - } - - if val == 0 { - self.ip = dst as usize; - } else { - self.ip += 3; - } - } - Opcode::LessThan(op1, op2, dst) => { - let val1 = op1.get(&self.memory)?; - let val2 = op2.get(&self.memory)?; - - let res = if val1 < val2 { 1 } else { 0 }; - dst.set(&mut self.memory, res)?; - - self.ip += 4; - } - Opcode::Equals(op1, op2, dst) => { - let val1 = op1.get(&self.memory)?; - let val2 = op2.get(&self.memory)?; - - let res = if val1 == val2 { 1 } else { 0 }; - dst.set(&mut self.memory, res)?; - - self.ip += 4; - } - Opcode::Halt => break Ok(()), - } - } - } - - fn get_output(&self) -> Option { - for (i, out) in self.output.iter().enumerate() { - if i < self.output.len() - 1 { - assert_eq!(*out, 0); - } else { - return Some(*out); - } - } - - None - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/aoc2019/src/intcode/mod.rs b/aoc2019/src/intcode/mod.rs new file mode 100644 index 0000000..8a5ee13 --- /dev/null +++ b/aoc2019/src/intcode/mod.rs @@ -0,0 +1,246 @@ +use aoc::err; +use aoc::Result; + +mod parameter; + +use parameter::Parameter; + +pub fn parse_memory(s: &str) -> Result> { + s.trim_end() + .split(',') + .map(|x| x.parse().map_err(|e| err!("couldn't parse int: {}", e))) + .collect() +} + +enum Opcode { + Add(Parameter, Parameter, Parameter), + Multiply(Parameter, Parameter, Parameter), + Input(Parameter), + Output(Parameter), + JumpTrue(Parameter, Parameter), + JumpFalse(Parameter, Parameter), + LessThan(Parameter, Parameter, Parameter), + Equals(Parameter, Parameter, Parameter), + Halt, +} + +pub struct Intcode { + pub memory: Vec, + input: Vec, + pub output: Vec, + ip: usize, + next_input: usize, +} + +impl Intcode { + pub fn new(input: &str) -> Result { + let memory = parse_memory(input)?; + + Ok(Intcode::with_memory(memory)) + } + + pub fn with_memory(memory: Vec) -> Self { + Intcode { + memory, + input: Vec::new(), + output: Vec::new(), + ip: 0, + next_input: 0, + } + } + + pub fn add_input(&mut self, value: i64) { + self.input.push(value); + } + + fn get_opcode(&self) -> Result { + let instruction = self.memory[self.ip]; + + let opcode = instruction % 100; + let mode1 = instruction / 100; + let mode2 = instruction / 1000; + let mode3 = instruction / 10000; + match opcode { + 1 => { + let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; + + if let Parameter::Immediate(_) = dst { + Err(err!("add: destination parameter can't be immediate")) + } else { + Ok(Opcode::Add(op1, op2, dst)) + } + } + 2 => { + let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; + + if let Parameter::Immediate(_) = dst { + Err(err!("multiply: destination parameter can't be immediate")) + } else { + Ok(Opcode::Multiply(op1, op2, dst)) + } + } + 3 => { + let dst = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + + if let Parameter::Immediate(_) = dst { + Err(err!("input: destination parameter can't be immediate")) + } else { + Ok(Opcode::Input(dst)) + } + } + 4 => { + let op = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + + Ok(Opcode::Output(op)) + } + 5 => { + let test = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let dst = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + + Ok(Opcode::JumpTrue(test, dst)) + } + 6 => { + let test = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let dst = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + + Ok(Opcode::JumpFalse(test, dst)) + } + 7 => { + let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; + + if let Parameter::Immediate(_) = dst { + Err(err!("less than: destination parameter can't be immediate")) + } else { + Ok(Opcode::LessThan(op1, op2, dst)) + } + } + 8 => { + let op1 = Parameter::new(mode1, self.memory.get(self.ip + 1).copied())?; + let op2 = Parameter::new(mode2, self.memory.get(self.ip + 2).copied())?; + let dst = Parameter::new(mode3, self.memory.get(self.ip + 3).copied())?; + + if let Parameter::Immediate(_) = dst { + Err(err!("equals: destination parameter can't be immediate")) + } else { + Ok(Opcode::Equals(op1, op2, dst)) + } + } + 99 => Ok(Opcode::Halt), + _ => Err(err!("unknown opcode: {}", opcode)), + } + } + + pub fn run(&mut self) -> Result<()> { + loop { + if self.ip >= self.memory.len() { + return Err(err!("reached end of program without halting")); + } + + let opcode = self.get_opcode()?; + + match opcode { + Opcode::Add(op1, op2, dst) => { + let val1 = op1.get(&self.memory)?; + let val2 = op2.get(&self.memory)?; + + dst.set(&mut self.memory, val1 + val2)?; + + self.ip += 4; + } + Opcode::Multiply(op1, op2, dst) => { + let val1 = op1.get(&self.memory)?; + let val2 = op2.get(&self.memory)?; + + dst.set(&mut self.memory, val1 * val2)?; + + self.ip += 4; + } + Opcode::Input(dst) => { + let input = if self.next_input < self.input.len() { + let res = self.input[self.next_input]; + self.next_input += 1; + Ok(res) + } else { + Err(err!("tried to read input but it was empty")) + }?; + dst.set(&mut self.memory, input)?; + + self.ip += 2; + } + Opcode::Output(op) => { + let val = op.get(&self.memory)?; + self.output.push(val); + + self.ip += 2; + } + Opcode::JumpTrue(test, dst) => { + let val = test.get(&self.memory)?; + let dst = dst.get(&self.memory)?; + if dst < 0 { + return Err(err!("dst must be a valid address: {}", dst)); + } + + if val != 0 { + self.ip = dst as usize; + } else { + self.ip += 3; + } + } + Opcode::JumpFalse(test, dst) => { + let val = test.get(&self.memory)?; + let dst = dst.get(&self.memory)?; + if dst < 0 { + return Err(err!("dst must be a valid address: {}", dst)); + } + + if val == 0 { + self.ip = dst as usize; + } else { + self.ip += 3; + } + } + Opcode::LessThan(op1, op2, dst) => { + let val1 = op1.get(&self.memory)?; + let val2 = op2.get(&self.memory)?; + + let res = if val1 < val2 { 1 } else { 0 }; + dst.set(&mut self.memory, res)?; + + self.ip += 4; + } + Opcode::Equals(op1, op2, dst) => { + let val1 = op1.get(&self.memory)?; + let val2 = op2.get(&self.memory)?; + + let res = if val1 == val2 { 1 } else { 0 }; + dst.set(&mut self.memory, res)?; + + self.ip += 4; + } + Opcode::Halt => break Ok(()), + } + } + } + + pub fn get_day02_output(&self) -> Option { + self.memory.get(0).copied() + } + + pub fn get_day05_output(&self) -> Option { + for (i, out) in self.output.iter().enumerate() { + if i < self.output.len() - 1 { + assert_eq!(*out, 0); + } else { + return Some(*out); + } + } + + None + } +} diff --git a/aoc2019/src/intcode/parameter.rs b/aoc2019/src/intcode/parameter.rs new file mode 100644 index 0000000..a4600c8 --- /dev/null +++ b/aoc2019/src/intcode/parameter.rs @@ -0,0 +1,50 @@ +use aoc::err; +use aoc::Result; + +pub enum Parameter { + Position(usize), + Immediate(i64), +} + +impl Parameter { + pub fn new(mode: i64, val: Option) -> Result { + let val = val.ok_or_else(|| err!("parameter value out of bounds"))?; + let mode = mode % 10; + + match mode { + 0 => { + if val < 0 { + Err(err!("negative value for position parameter: {}", val)) + } else { + let val = val as usize; + Ok(Parameter::Position(val)) + } + } + 1 => Ok(Parameter::Immediate(val)), + _ => Err(err!("wrong mode for parameter: {}", mode)), + } + } + + pub fn get(&self, memory: &[i64]) -> Result { + match self { + Parameter::Position(address) => match memory.get(*address) { + Some(val) => Ok(*val), + None => Err(err!("read out of bounds at address {}", address)), + }, + Parameter::Immediate(value) => Ok(*value), + } + } + + pub fn set(&self, memory: &mut [i64], value: i64) -> Result<()> { + match self { + Parameter::Position(address) => { + let cell = memory + .get_mut(*address) + .ok_or_else(|| err!("memory write out of bounds"))?; + *cell = value; + Ok(()) + } + Parameter::Immediate(_) => Err(err!("cannot write to immediate parameter")), + } + } +} diff --git a/aoc2019/src/lib.rs b/aoc2019/src/lib.rs index d945a75..34aeeaa 100644 --- a/aoc2019/src/lib.rs +++ b/aoc2019/src/lib.rs @@ -1,3 +1,5 @@ +mod intcode; + pub mod day01; pub mod day02; pub mod day03;