diff --git a/2019/input/day9.txt b/2019/input/day9.txt new file mode 100644 index 0000000..93e30df --- /dev/null +++ b/2019/input/day9.txt @@ -0,0 +1 @@ +1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1102,1,3,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1101,39,0,1004,1101,0,37,1013,1101,0,28,1001,1101,0,38,1005,1101,23,0,1008,1102,1,0,1020,1102,1,26,1010,1102,31,1,1009,1101,29,0,1015,1102,459,1,1024,1101,33,0,1007,1101,0,30,1016,1101,32,0,1002,1102,1,494,1027,1101,0,216,1029,1101,497,0,1026,1101,0,303,1022,1102,1,21,1018,1102,1,36,1006,1102,1,27,1014,1102,296,1,1023,1102,454,1,1025,1102,35,1,1003,1101,22,0,1017,1102,225,1,1028,1102,1,20,1011,1101,1,0,1021,1101,0,24,1000,1101,0,25,1019,1101,0,34,1012,109,13,21102,40,1,0,1008,1013,40,63,1005,63,203,4,187,1106,0,207,1001,64,1,64,1002,64,2,64,109,5,2106,0,10,4,213,1001,64,1,64,1105,1,225,1002,64,2,64,109,-3,1206,6,241,1001,64,1,64,1105,1,243,4,231,1002,64,2,64,109,-17,2108,30,4,63,1005,63,259,1106,0,265,4,249,1001,64,1,64,1002,64,2,64,109,14,2108,35,-9,63,1005,63,283,4,271,1105,1,287,1001,64,1,64,1002,64,2,64,109,13,2105,1,-2,1001,64,1,64,1106,0,305,4,293,1002,64,2,64,109,-28,1208,5,32,63,1005,63,327,4,311,1001,64,1,64,1106,0,327,1002,64,2,64,109,12,2102,1,0,63,1008,63,31,63,1005,63,353,4,333,1001,64,1,64,1105,1,353,1002,64,2,64,109,7,21102,41,1,-6,1008,1010,40,63,1005,63,373,1105,1,379,4,359,1001,64,1,64,1002,64,2,64,109,-4,2102,1,-6,63,1008,63,35,63,1005,63,403,1001,64,1,64,1105,1,405,4,385,1002,64,2,64,109,11,21107,42,43,-4,1005,1019,427,4,411,1001,64,1,64,1105,1,427,1002,64,2,64,109,-10,1206,7,445,4,433,1001,64,1,64,1105,1,445,1002,64,2,64,109,10,2105,1,1,4,451,1105,1,463,1001,64,1,64,1002,64,2,64,109,-14,21108,43,42,4,1005,1013,479,1106,0,485,4,469,1001,64,1,64,1002,64,2,64,109,12,2106,0,6,1106,0,503,4,491,1001,64,1,64,1002,64,2,64,109,-10,2107,30,-2,63,1005,63,521,4,509,1106,0,525,1001,64,1,64,1002,64,2,64,109,-7,2101,0,-4,63,1008,63,26,63,1005,63,549,1001,64,1,64,1106,0,551,4,531,1002,64,2,64,109,13,21107,44,43,-3,1005,1014,571,1001,64,1,64,1105,1,573,4,557,1002,64,2,64,109,-6,21108,45,45,1,1005,1012,591,4,579,1106,0,595,1001,64,1,64,1002,64,2,64,109,8,1205,2,609,4,601,1106,0,613,1001,64,1,64,1002,64,2,64,109,-11,1208,-6,34,63,1005,63,629,1106,0,635,4,619,1001,64,1,64,1002,64,2,64,109,-15,2107,33,9,63,1005,63,651,1106,0,657,4,641,1001,64,1,64,1002,64,2,64,109,9,1207,2,38,63,1005,63,677,1001,64,1,64,1106,0,679,4,663,1002,64,2,64,109,8,21101,46,0,0,1008,1010,45,63,1005,63,703,1001,64,1,64,1106,0,705,4,685,1002,64,2,64,109,-5,1201,-3,0,63,1008,63,32,63,1005,63,727,4,711,1106,0,731,1001,64,1,64,1002,64,2,64,109,-6,1207,8,34,63,1005,63,753,4,737,1001,64,1,64,1106,0,753,1002,64,2,64,109,29,1205,-8,765,1106,0,771,4,759,1001,64,1,64,1002,64,2,64,109,-18,1202,-6,1,63,1008,63,39,63,1005,63,797,4,777,1001,64,1,64,1106,0,797,1002,64,2,64,109,8,21101,47,0,0,1008,1018,47,63,1005,63,823,4,803,1001,64,1,64,1105,1,823,1002,64,2,64,109,-12,2101,0,-3,63,1008,63,35,63,1005,63,845,4,829,1106,0,849,1001,64,1,64,1002,64,2,64,109,-9,1201,5,0,63,1008,63,30,63,1005,63,869,1105,1,875,4,855,1001,64,1,64,1002,64,2,64,109,8,1202,-2,1,63,1008,63,34,63,1005,63,899,1001,64,1,64,1105,1,901,4,881,4,64,99,21101,27,0,1,21101,0,915,0,1105,1,922,21201,1,45467,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21101,942,0,0,1106,0,922,21201,1,0,-1,21201,-2,-3,1,21102,1,957,0,1105,1,922,22201,1,-1,-2,1105,1,968,22101,0,-2,-2,109,-3,2106,0,0 diff --git a/2019/src/bin/day2.rs b/2019/src/bin/day2.rs index 1d5cb97..6c79768 100644 --- a/2019/src/bin/day2.rs +++ b/2019/src/bin/day2.rs @@ -78,6 +78,7 @@ mod tests { assert_eq!(program, &[2, 0, 0, 0, 99]) } + #[test] fn test_example4() { let input = "1,1,1,4,99,5,6,0,99"; diff --git a/2019/src/bin/day9.rs b/2019/src/bin/day9.rs new file mode 100644 index 0000000..f5b6989 --- /dev/null +++ b/2019/src/bin/day9.rs @@ -0,0 +1,53 @@ +use std::{fs, io}; + +use advent_of_code::computer::Computer; +use advent_of_code::input; + +fn main() -> io::Result<()> { + let input = fs::read_to_string("input/day9.txt")?; + let program = input::read_separated_line(',', &input)?; + + let mut computer = Computer::new('1', program.clone(), vec![1], vec![]); + computer.run(None, None); + println!("Part 1: {}", computer.output()); + + // let mut computer = computer::Computer::new('2', data.clone(), vec![5], vec![]); + // computer.run(None, None); + // println!("Part 2: {}", computer.output()); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_relative_example1() { + let program = vec![1102, 34915192, 34915192, 7, 4, 7, 99, 0]; + let mut computer = Computer::new('T', program, vec![], vec![]); + computer.run(None, None); + + assert_eq!(computer.output(), 1219070632396864); + } + + #[test] + fn test_relative_example2() { + let program = vec![ + 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99, + ]; + let mut computer = Computer::new('T', program, vec![], vec![]); + computer.run(None, None); + + assert_eq!(computer.output(), 99); + } + + #[test] + fn test_relative_example3() { + let program = vec![104, 1125899906842624, 99]; + let mut computer = Computer::new('T', program, vec![], vec![]); + computer.run(None, None); + + assert_eq!(computer.output(), 1125899906842624); + } +} diff --git a/2019/src/computer.rs b/2019/src/computer.rs index 2a550c5..7bf36ee 100644 --- a/2019/src/computer.rs +++ b/2019/src/computer.rs @@ -1,32 +1,35 @@ use std::cell::RefCell; use std::collections::VecDeque; +use std::convert::TryFrom; use std::rc::Rc; -type Address = i32; +type Address = i64; #[derive(Debug, Eq, PartialEq)] enum Instruction { - Add(Mode, Mode), - Multiply(Mode, Mode), - Input, + Add(Mode, Mode, Mode), + Multiply(Mode, Mode, Mode), + Input(Mode), Output(Mode), JumpIfTrue(Mode, Mode), JumpIfFalse(Mode, Mode), - LessThan(Mode, Mode), - Equals(Mode, Mode), + LessThan(Mode, Mode, Mode), + Equals(Mode, Mode, Mode), + AdjustRelativeBase(Mode), Halt, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Copy, Clone)] enum Mode { Immediate, Address, + Relative, } #[derive(Debug)] pub struct Pipe { - queue: VecDeque, - last: Option, + queue: VecDeque, + last: Option, } pub enum ComputeResult { @@ -36,57 +39,58 @@ pub enum ComputeResult { pub struct Computer { name: char, - ip: i32, - memory: Vec, + ip: i64, + memory: Vec, input: I, output: O, + relative_base: i64, } pub trait Input { - fn read(&mut self) -> Option; + fn read(&mut self) -> Option; } pub trait Output { - fn write(&mut self, value: i32); + fn write(&mut self, value: i64); - fn last_value(&self) -> i32; + fn last_value(&self) -> i64; } -impl Input for Vec { - fn read(&mut self) -> Option { +impl Input for Vec { + fn read(&mut self) -> Option { self.pop() } } -impl Output for Vec { - fn write(&mut self, value: i32) { +impl Output for Vec { + fn write(&mut self, value: i64) { self.push(value) } - fn last_value(&self) -> i32 { + fn last_value(&self) -> i64 { *self.last().unwrap() } } impl Input for Rc> { - fn read(&mut self) -> Option { + fn read(&mut self) -> Option { dbg!(self.borrow_mut().queue.pop_front()) } } impl Output for Rc> { - fn write(&mut self, value: i32) { + fn write(&mut self, value: i64) { let mut pipe = self.borrow_mut(); pipe.last = Some(value); pipe.queue.push_back(value); } - fn last_value(&self) -> i32 { + fn last_value(&self) -> i64 { self.borrow().last.unwrap() } } -fn decode(mut instruction: i32) -> Instruction { +fn decode(mut instruction: i64) -> Instruction { let opcode = divmod(&mut instruction, 100); match opcode { @@ -96,13 +100,15 @@ fn decode(mut instruction: i32) -> Instruction { Instruction::Add( Mode::from(divmod(&mut instruction, 10)), Mode::from(divmod(&mut instruction, 10)), + Mode::from(divmod(&mut instruction, 10)), ) } 2 => Instruction::Multiply( Mode::from(divmod(&mut instruction, 10)), Mode::from(divmod(&mut instruction, 10)), + Mode::from(divmod(&mut instruction, 10)), ), - 3 => Instruction::Input, + 3 => Instruction::Input(Mode::from(divmod(&mut instruction, 10))), 4 => Instruction::Output(Mode::from(divmod(&mut instruction, 10))), 5 => Instruction::JumpIfTrue( Mode::from(divmod(&mut instruction, 10)), @@ -115,11 +121,14 @@ fn decode(mut instruction: i32) -> Instruction { 7 => Instruction::LessThan( Mode::from(divmod(&mut instruction, 10)), Mode::from(divmod(&mut instruction, 10)), + Mode::from(divmod(&mut instruction, 10)), ), 8 => Instruction::Equals( Mode::from(divmod(&mut instruction, 10)), Mode::from(divmod(&mut instruction, 10)), + Mode::from(divmod(&mut instruction, 10)), ), + 9 => Instruction::AdjustRelativeBase(Mode::from(divmod(&mut instruction, 10))), 99 => Instruction::Halt, _ => panic!("Invalid opcode: {}", opcode), } @@ -130,13 +139,17 @@ where I: Input, O: Output, { - pub fn new(name: char, memory: Vec, input: I, output: O) -> Self { + pub fn new(name: char, mut memory: Vec, input: I, output: O) -> Self { + // HACK + let mut buffer = vec![0; 64 * 1024 * 1024]; + memory.append(&mut buffer); Computer { name, ip: 0, memory, input, output, + relative_base: 0, } } @@ -144,41 +157,52 @@ where self.name } - fn read(&self, value: i32, mode: Mode) -> i32 { + fn read(&self, value: i64, mode: Mode) -> i64 { match mode { Mode::Immediate => self.memory[value as usize], Mode::Address => self.memory[self.memory[value as usize] as usize], + // The address a relative mode parameter refers to is itself plus the current relative base. + Mode::Relative => { + self.memory + [usize::try_from(self.relative_base + self.memory[value as usize]).unwrap()] + } } } - fn write(&mut self, address: Address, value: i32) { - self.memory[address as usize] = value; + fn write(&mut self, address: Address, value: i64, mode: Mode) { + match mode { + Mode::Immediate => panic!("attempt to write with immediate mode"), + Mode::Address => self.memory[address as usize] = value, + Mode::Relative => { + self.memory[usize::try_from(self.relative_base + address).unwrap()] = value + } + } } - pub fn run(&mut self, noun: Option, verb: Option) -> ComputeResult { + pub fn run(&mut self, noun: Option, verb: Option) -> ComputeResult { println!("{}: resume ip = {}", self.name, self.ip); if let Some(noun) = noun { - self.write(1, noun); + self.write(1, noun, Mode::Address); } if let Some(verb) = verb { - self.write(2, verb); + self.write(2, verb, Mode::Address); } loop { match decode(self.read(self.ip, Mode::Immediate)) { - Instruction::Add(mode1, mode2) => { + Instruction::Add(mode1, mode2, write_mode) => { let result = self.read(self.ip + 1, mode1) + self.read(self.ip + 2, mode2); - self.write(self.read(self.ip + 3, Mode::Immediate), result); + self.write(self.read(self.ip + 3, Mode::Immediate), result, write_mode); self.ip += 4; } - Instruction::Multiply(mode1, mode2) => { + Instruction::Multiply(mode1, mode2, write_mode) => { let result = self.read(self.ip + 1, mode1) * self.read(self.ip + 2, mode2); - self.write(self.read(self.ip + 3, Mode::Immediate), result); + self.write(self.read(self.ip + 3, Mode::Immediate), result, write_mode); self.ip += 4; } - Instruction::Input => match self.input.read() { + Instruction::Input(write_mode) => match self.input.read() { Some(value) => { - self.write(self.read(self.ip + 1, Mode::Immediate), value); + self.write(self.read(self.ip + 1, Mode::Immediate), value, write_mode); self.ip += 2; } None => { @@ -206,53 +230,60 @@ where self.ip += 3; } } - Instruction::LessThan(mode1, mode2) => { + Instruction::LessThan(mode1, mode2, write_mode) => { if self.read(self.ip + 1, mode1) < self.read(self.ip + 2, mode2) { - self.write(self.read(self.ip + 3, Mode::Immediate), 1); + self.write(self.read(self.ip + 3, Mode::Immediate), 1, write_mode); } else { - self.write(self.read(self.ip + 3, Mode::Immediate), 0); + self.write(self.read(self.ip + 3, Mode::Immediate), 0, write_mode); } self.ip += 4; } - Instruction::Equals(mode1, mode2) => { + Instruction::Equals(mode1, mode2, write_mode) => { if self.read(self.ip + 1, mode1) == self.read(self.ip + 2, mode2) { - self.write(self.read(self.ip + 3, Mode::Immediate), 1); + self.write(self.read(self.ip + 3, Mode::Immediate), 1, write_mode); } else { - self.write(self.read(self.ip + 3, Mode::Immediate), 0); + self.write(self.read(self.ip + 3, Mode::Immediate), 0, write_mode); } self.ip += 4; } + Instruction::AdjustRelativeBase(mode) => { + let base = self.read(self.ip + 1, mode); + self.relative_base += base; + self.relative_base; + self.ip += 2; + } Instruction::Halt => return ComputeResult::Halted, } } } - pub fn output(&self) -> i32 { + pub fn output(&self) -> i64 { self.output.last_value() } } impl Pipe { - pub fn new(queue: VecDeque) -> Self { + pub fn new(queue: VecDeque) -> Self { Pipe { queue, last: None } } - pub fn push_front(&mut self, value: i32) { + pub fn push_front(&mut self, value: i64) { self.queue.push_front(value); } } -fn divmod(value: &mut i32, divisor: i32) -> i32 { +fn divmod(value: &mut i64, divisor: i64) -> i64 { let res = *value % divisor; *value /= divisor; res } -impl From for Mode { - fn from(mode: i32) -> Self { +impl From for Mode { + fn from(mode: i64) -> Self { match mode { 0 => Mode::Address, 1 => Mode::Immediate, + 2 => Mode::Relative, _ => unreachable!(), } } @@ -268,7 +299,7 @@ mod tests { fn test_decode() { assert_eq!( decode(1002), - Instruction::Multiply(Mode::Address, Mode::Immediate) + Instruction::Multiply(Mode::Address, Mode::Immediate, Mode::Address) ) } diff --git a/2019/src/input.rs b/2019/src/input.rs index f59098b..c99780f 100644 --- a/2019/src/input.rs +++ b/2019/src/input.rs @@ -18,7 +18,7 @@ pub fn read_number_list>(path: P) -> io::Result> { Ok(output) } -pub fn read_separated_line(sep: char, line: &str) -> io::Result> { +pub fn read_separated_line(sep: char, line: &str) -> io::Result> { line.trim() .split(sep) .map(|number| {