diff --git a/aoc2019/input/day05.txt b/aoc2019/input/day05.txt new file mode 100644 index 0000000..c6d4caf --- /dev/null +++ b/aoc2019/input/day05.txt @@ -0,0 +1 @@ +3,225,1,225,6,6,1100,1,238,225,104,0,1101,90,60,224,1001,224,-150,224,4,224,1002,223,8,223,1001,224,7,224,1,224,223,223,1,57,83,224,1001,224,-99,224,4,224,1002,223,8,223,1001,224,5,224,1,223,224,223,1102,92,88,225,101,41,187,224,1001,224,-82,224,4,224,1002,223,8,223,101,7,224,224,1,224,223,223,1101,7,20,225,1101,82,64,225,1002,183,42,224,101,-1554,224,224,4,224,102,8,223,223,1001,224,1,224,1,224,223,223,1102,70,30,224,101,-2100,224,224,4,224,102,8,223,223,101,1,224,224,1,224,223,223,2,87,214,224,1001,224,-2460,224,4,224,1002,223,8,223,101,7,224,224,1,223,224,223,102,36,180,224,1001,224,-1368,224,4,224,1002,223,8,223,1001,224,5,224,1,223,224,223,1102,50,38,225,1102,37,14,225,1101,41,20,225,1001,217,7,224,101,-25,224,224,4,224,1002,223,8,223,101,2,224,224,1,224,223,223,1101,7,30,225,1102,18,16,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,7,226,226,224,102,2,223,223,1006,224,329,101,1,223,223,1107,677,226,224,102,2,223,223,1006,224,344,1001,223,1,223,8,677,226,224,1002,223,2,223,1005,224,359,101,1,223,223,107,677,677,224,1002,223,2,223,1006,224,374,101,1,223,223,7,677,226,224,1002,223,2,223,1006,224,389,101,1,223,223,108,677,226,224,1002,223,2,223,1005,224,404,101,1,223,223,1108,677,226,224,102,2,223,223,1005,224,419,101,1,223,223,8,226,677,224,102,2,223,223,1006,224,434,1001,223,1,223,1008,677,677,224,1002,223,2,223,1005,224,449,1001,223,1,223,1107,226,677,224,102,2,223,223,1006,224,464,101,1,223,223,107,226,677,224,1002,223,2,223,1006,224,479,1001,223,1,223,7,226,677,224,102,2,223,223,1005,224,494,1001,223,1,223,8,677,677,224,102,2,223,223,1006,224,509,1001,223,1,223,1108,677,677,224,102,2,223,223,1005,224,524,1001,223,1,223,1108,226,677,224,1002,223,2,223,1005,224,539,101,1,223,223,107,226,226,224,102,2,223,223,1006,224,554,1001,223,1,223,1007,226,226,224,102,2,223,223,1005,224,569,1001,223,1,223,1008,226,226,224,102,2,223,223,1005,224,584,101,1,223,223,1007,677,677,224,1002,223,2,223,1005,224,599,1001,223,1,223,108,677,677,224,1002,223,2,223,1006,224,614,1001,223,1,223,1007,226,677,224,1002,223,2,223,1006,224,629,101,1,223,223,1008,677,226,224,102,2,223,223,1005,224,644,101,1,223,223,1107,226,226,224,1002,223,2,223,1005,224,659,1001,223,1,223,108,226,226,224,1002,223,2,223,1005,224,674,101,1,223,223,4,223,99,226 diff --git a/aoc2019/src/day05.rs b/aoc2019/src/day05.rs new file mode 100644 index 0000000..4a291b9 --- /dev/null +++ b/aoc2019/src/day05.rs @@ -0,0 +1,222 @@ +use aoc::err; +use aoc::Result; + +const INPUT: &str = include_str!("../input/day05.txt"); + +pub fn run() -> Result<()> { + println!("part 1: {}", part1(INPUT)?); + Ok(()) +} + +fn part1(input: &str) -> Result { + let mut intcode = Intcode::new(input)?; + intcode.add_input(1); + intcode.run()?; + intcode + .get_output() + .ok_or_else(|| err!("intcode gave no output")) +} + +enum Parameter { + Position(usize), + Immediate(i64), +} + +impl Parameter { + fn new(mode: i64, val: Option<&i64>) -> 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]) -> Option { + match self { + Parameter::Position(address) => memory.get(*address).map(|v| *v), + Parameter::Immediate(value) => Some(*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), + Halt, +} + +struct Intcode { + memory: Vec, + input: Vec, + output: Vec, + ip: 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 { + memory, + input: Vec::new(), + output: Vec::new(), + ip: 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; + match opcode { + 1 => { + let op1 = Parameter::new(instruction / 100, self.memory.get(self.ip + 1))?; + let op2 = Parameter::new(instruction / 1000, self.memory.get(self.ip + 2))?; + let dst = Parameter::new(instruction / 10000, self.memory.get(self.ip + 3))?; + + 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(instruction / 100, self.memory.get(self.ip + 1))?; + let op2 = Parameter::new(instruction / 1000, self.memory.get(self.ip + 2))?; + let dst = Parameter::new(instruction / 10000, self.memory.get(self.ip + 3))?; + + 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(instruction / 100, self.memory.get(self.ip + 1))?; + + if let Parameter::Immediate(_) = dst { + Err(err!("input: destination parameter can't be immediate")) + } else { + Ok(Opcode::Input(dst)) + } + } + 4 => { + let op = Parameter::new(instruction / 100, self.memory.get(self.ip + 1))?; + + Ok(Opcode::Output(op)) + } + 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) + .ok_or_else(|| err!("add: op1 out of bounds"))?; + let val2 = op2 + .get(&self.memory) + .ok_or_else(|| err!("add: op1 out of bounds"))?; + + dst.set(&mut self.memory, val1 + val2)?; + + self.ip += 4; + } + Opcode::Multiply(op1, op2, dst) => { + let val1 = op1 + .get(&self.memory) + .ok_or_else(|| err!("add: op1 out of bounds"))?; + let val2 = op2 + .get(&self.memory) + .ok_or_else(|| err!("add: op1 out of bounds"))?; + + dst.set(&mut self.memory, val1 * val2)?; + + self.ip += 4; + } + Opcode::Input(dst) => { + let input = self + .input + .pop() + .ok_or_else(|| 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) + .ok_or_else(|| err!("output: op out of bounds"))?; + self.output.push(val); + + self.ip += 2; + } + 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::*; + + #[test] + fn part1_real() { + assert_eq!(part1(INPUT).unwrap(), 16225258); + } +} diff --git a/aoc2019/src/lib.rs b/aoc2019/src/lib.rs index e051fbd..f290640 100644 --- a/aoc2019/src/lib.rs +++ b/aoc2019/src/lib.rs @@ -2,3 +2,4 @@ pub mod day01; pub mod day02; pub mod day03; pub mod day04; +pub mod day05; diff --git a/aoc2019/src/main.rs b/aoc2019/src/main.rs index 4fa0ab4..c4ec62b 100644 --- a/aoc2019/src/main.rs +++ b/aoc2019/src/main.rs @@ -4,9 +4,10 @@ use aoc2019::day01; use aoc2019::day02; use aoc2019::day03; use aoc2019::day04; +use aoc2019::day05; fn main() -> Result<()> { - let days: &[fn() -> Result<()>] = &[day01::run, day02::run, day03::run, day04::run]; + let days: &[fn() -> Result<()>] = &[day01::run, day02::run, day03::run, day04::run, day05::run]; aoc::run(days) }