diff --git a/Cargo.toml b/Cargo.toml index 719d289..c59f6f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" authors = ["Stefan Schwarz "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [[bin]] name = "day1" path = "src/day1/main.rs" @@ -38,6 +36,10 @@ path = "src/day9/main.rs" name = "day10" path = "src/day10/main.rs" +[[bin]] +name = "day11" +path = "src/day11/main.rs" + [[bin]] name = "day12" path = "src/day12/main.rs" diff --git a/README.md b/README.md new file mode 100644 index 0000000..b81605e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Advent of Code 2019 diff --git a/input11 b/input11 new file mode 100644 index 0000000..d6581e1 --- /dev/null +++ b/input11 @@ -0,0 +1 @@ +3,8,1005,8,320,1106,0,11,0,0,0,104,1,104,0,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,102,1,8,29,2,1005,1,10,1006,0,11,3,8,1002,8,-1,10,101,1,10,10,4,10,108,0,8,10,4,10,102,1,8,57,1,8,15,10,1006,0,79,1,6,3,10,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,101,0,8,90,2,103,18,10,1006,0,3,2,105,14,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,123,2,9,2,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,1001,8,0,150,1,2,2,10,2,1009,6,10,1,1006,12,10,1006,0,81,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,102,1,8,187,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,209,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,231,1,1008,11,10,1,1001,4,10,2,1104,18,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,264,1,8,14,10,1006,0,36,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,293,1006,0,80,1006,0,68,101,1,9,9,1007,9,960,10,1005,10,15,99,109,642,104,0,104,1,21102,1,846914232732,1,21102,1,337,0,1105,1,441,21102,1,387512115980,1,21101,348,0,0,1106,0,441,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,209533824219,1,1,21102,1,395,0,1106,0,441,21101,0,21477985303,1,21102,406,1,0,1106,0,441,3,10,104,0,104,0,3,10,104,0,104,0,21101,868494234468,0,1,21101,429,0,0,1106,0,441,21102,838429471080,1,1,21102,1,440,0,1106,0,441,99,109,2,21201,-1,0,1,21101,0,40,2,21102,472,1,3,21101,0,462,0,1106,0,505,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,467,468,483,4,0,1001,467,1,467,108,4,467,10,1006,10,499,1102,1,0,467,109,-2,2106,0,0,0,109,4,2101,0,-1,504,1207,-3,0,10,1006,10,522,21101,0,0,-3,21202,-3,1,1,22101,0,-2,2,21102,1,1,3,21102,541,1,0,1106,0,546,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,569,2207,-4,-2,10,1006,10,569,22102,1,-4,-4,1105,1,637,22102,1,-4,1,21201,-3,-1,2,21202,-2,2,3,21102,588,1,0,1105,1,546,22101,0,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,607,21101,0,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,629,21201,-1,0,1,21102,629,1,0,105,1,504,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0 diff --git a/src/day11/main.rs b/src/day11/main.rs new file mode 100644 index 0000000..3417d6b --- /dev/null +++ b/src/day11/main.rs @@ -0,0 +1,186 @@ +use std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io; + +use aoc2019::intcode; + +#[derive(Hash, Default, PartialEq, Eq, Clone)] +struct Panel { + x: isize, + y: isize, +} + +enum Color { + Black, + White, +} + +impl From for Color { + fn from(color: isize) -> Self { + match color { + 0 => Color::Black, + 1 => Color::White, + _ => panic!("invalid color"), + } + } +} + +impl Into for &Color { + fn into(self) -> isize { + match self { + Color::Black => 0, + Color::White => 1, + } + } +} + +impl Color { + fn char(&self) -> char { + match self { + Color::Black => ' ', + Color::White => '\u{2588}', + } + } +} + +enum Direction { + Up, + Down, + Left, + Right, +} + +enum Turn { + Left, + Right, +} + +impl From for Turn { + fn from(turn: isize) -> Self { + match turn { + 0 => Turn::Left, + 1 => Turn::Right, + _ => panic!("invalid turn direction"), + } + } +} + +struct Robot { + panel: Panel, + direction: Direction, +} + +impl Robot { + fn turn(&mut self, direction: Turn) { + self.direction = match self.direction { + Direction::Up => match direction { + Turn::Left => Direction::Left, + Turn::Right => Direction::Right, + }, + Direction::Right => match direction { + Turn::Left => Direction::Up, + Turn::Right => Direction::Down, + }, + Direction::Down => match direction { + Turn::Left => Direction::Right, + Turn::Right => Direction::Left, + }, + Direction::Left => match direction { + Turn::Left => Direction::Down, + Turn::Right => Direction::Up, + }, + } + } + + fn step(&mut self) { + self.panel = match self.direction { + Direction::Up => Panel { + x: self.panel.x, + y: self.panel.y + 1, + }, + Direction::Left => Panel { + x: self.panel.x + 1, + y: self.panel.y, + }, + Direction::Down => Panel { + x: self.panel.x, + y: self.panel.y - 1, + }, + Direction::Right => Panel { + x: self.panel.x - 1, + y: self.panel.y, + }, + } + } +} + +fn main() -> io::Result<()> { + let filename = env::args().nth(1).expect("provide file as first param"); + let mut computer = intcode::Computer::from(File::open(filename)?); + + let mut canvas: HashMap = HashMap::new(); + canvas.insert(Panel { x: 0, y: 0 }, Color::White); + let mut robot = Robot { + panel: Panel::default(), + direction: Direction::Up, + }; + + let mut input: isize = canvas + .get(&robot.panel) + .or_else(|| Some(&Color::Black)) + .unwrap() + .into(); + while let Some(output) = computer.run_io(input) { + canvas.insert(robot.panel.clone(), Color::from(output)); + + let output = computer.run_must_output(); + robot.turn(Turn::from(output)); + robot.step(); + + input = canvas + .get(&robot.panel) + .or_else(|| Some(&Color::Black)) + .unwrap() + .into(); + } + + let mut xmin: isize = 0; + let mut xmax: isize = 0; + let mut ymin: isize = 0; + let mut ymax: isize = 0; + let painted = canvas + .iter() + .map(|(k, _)| { + if k.x < xmin { + xmin = k.x; + } + if k.x > xmax { + xmax = k.x; + } + if k.y < ymin { + ymin = k.y; + } + if k.y > ymax { + ymax = k.y; + } + }) + .count(); + println!("painted: {}", painted); + println!("canvas: x:{}-{} y:{}-{}", xmin, xmax, ymin, ymax); + + for y in (ymin..=ymax).rev() { + for x in (xmin..=xmax).rev() { + print!( + "{}", + canvas + .get(&Panel { x, y }) + .unwrap_or_else(|| &Color::Black) + .char() + ) + } + println!() + } + + Ok(()) +} diff --git a/src/intcode.rs b/src/intcode.rs new file mode 100644 index 0000000..7befcc2 --- /dev/null +++ b/src/intcode.rs @@ -0,0 +1,432 @@ +use std::collections::VecDeque; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +#[derive(Copy, Clone)] +enum Var { + A = 1, + B, + C, +} + +#[derive(Copy, Clone, Debug)] +enum Mode { + Position, + Immediate, + Relative, +} + +impl Default for Mode { + fn default() -> Self { + Mode::Position + } +} + +impl From for Mode { + fn from(b: isize) -> Self { + match b { + 0 => Mode::Position, + 1 => Mode::Immediate, + 2 => Mode::Relative, + _ => panic!("invalid mode {}", b), + } + } +} + +#[derive(Copy, Clone, Default, Debug)] +struct Modes { + a: Mode, + b: Mode, + c: Mode, +} + +impl Modes { + fn mode_for(&self, v: &Var) -> Mode { + match v { + Var::A => self.a, + Var::B => self.b, + Var::C => self.c, + } + } +} + +impl From<(Mode, Mode, Mode)> for Modes { + fn from(modes: (Mode, Mode, Mode)) -> Self { + Modes { + a: modes.0, + b: modes.1, + c: modes.2, + } + } +} + +impl From<&ModedOpcode> for Modes { + fn from(mo: &ModedOpcode) -> Self { + mo.modes + } +} + +#[derive(Copy, Clone)] +enum Opcode { + Add, + Multiply, + Input, + Output, + JumpIfTrue, + JumpIfFalse, + LessThan, + Equals, + RelativeAdj, + Halt, +} + +impl From<&ModedOpcode> for Opcode { + fn from(mo: &ModedOpcode) -> Self { + mo.op + } +} + +impl From for Opcode { + fn from(i: isize) -> Self { + match i { + 1 => Opcode::Add, + 2 => Opcode::Multiply, + 3 => Opcode::Input, + 4 => Opcode::Output, + 5 => Opcode::JumpIfTrue, + 6 => Opcode::JumpIfFalse, + 7 => Opcode::LessThan, + 8 => Opcode::Equals, + 9 => Opcode::RelativeAdj, + 99 => Opcode::Halt, + _ => panic!("invalid opcode {}", i), + } + } +} + +struct ModedOpcode { + modes: Modes, + op: Opcode, +} + +impl From for ModedOpcode { + fn from(i: isize) -> Self { + let code = i % 100; + let modes = i / 100; + let a = (modes % 10) / 1; + let b = (modes % 100) / 10; + let c = (modes % 1000) / 100; + let modes = (Mode::from(a), Mode::from(b), Mode::from(c)); + ModedOpcode { + modes: Modes::from(modes), + op: Opcode::from(code), + } + } +} + +pub struct Computer { + pos: usize, + rel: isize, + program: Vec, + modes: Modes, + inputs: VecDeque, + outputs: VecDeque, + halted: bool, +} + +impl Clone for Computer { + fn clone(&self) -> Self { + Computer::from(self.program.clone()) + } +} + +impl From for Computer { + fn from(f: File) -> Self { + let f = BufReader::new(f); + let program: Vec = f + .split(b',') + .map(|o| { + let o = o.unwrap(); + let mut o = o.iter().peekable(); + let sign = if o.peek() == Some(&&b'-') { -1 } else { 1 }; + o.filter(|i| **i >= b'0' && **i <= b'9') + .fold(0, |s, i| ((i - b'0') as isize + 10 * s)) + * sign + }) + .collect(); + Computer::from(program) + } +} + +impl From> for Computer { + fn from(program: Vec) -> Self { + Computer { + pos: 0, + rel: 0, + program, + modes: Default::default(), + inputs: VecDeque::new(), + outputs: VecDeque::new(), + halted: false, + } + } +} + +impl Computer { + pub fn get_outputs(&self) -> &VecDeque { + &self.outputs + } + + pub fn pop_output(&mut self) -> Option { + self.outputs.pop_front() + } + + pub fn clone_with_modified_program(&self, u: Vec<(usize, isize)>) -> Computer { + let mut computer = self.clone(); + for (pos, val) in u { + computer.program[pos] = val; + } + computer + } + + pub fn clone_with_input(&self, input: Vec) -> Computer { + let mut computer = self.clone(); + computer.inputs = VecDeque::from(input); + computer + } + + pub fn run(&mut self) { + while !self.halted { + self.step(); + } + } + + pub fn run_until_output(&mut self) -> Option { + loop { + self.step(); + if self.outputs.len() > 0 { + return self.outputs.pop_front(); + } + if self.halted { + return None; + } + } + } + + pub fn run_must_output(&mut self) -> isize { + self.run_until_output() + .expect("computer halted without output") + } + + pub fn run_io(&mut self, input: isize) -> Option { + self.inputs.push_back(input); + self.run_until_output() + } + + fn step(&mut self) { + let instruction = self.program[self.pos]; + let instruction = ModedOpcode::from(instruction); + self.modes = Modes::from(&instruction); + let advance = match Opcode::from(&instruction) { + Opcode::Add => self.add(), + Opcode::Multiply => self.multiply(), + Opcode::Input => self.input(), + Opcode::Output => self.output(), + Opcode::JumpIfTrue => self.jump_if_true(), + Opcode::JumpIfFalse => self.jump_if_false(), + Opcode::LessThan => self.less_than(), + Opcode::Equals => self.equals(), + Opcode::RelativeAdj => self.relative_adj(), + Opcode::Halt => self.halt(), + }; + self.pos += advance; + } + + fn get_pos(&self, pos: usize) -> isize { + *self.program.get(pos).unwrap_or(&0) + } + + fn get(&self, v: &Var) -> isize { + let want = self.pos + *v as usize; + let want = match self.modes.mode_for(v) { + Mode::Position => self.get_pos(want as usize) as usize, + Mode::Immediate => want, + Mode::Relative => (self.rel + self.get_pos(want)) as usize, + }; + self.get_pos(want) + } + + fn get_a(&self) -> isize { + self.get(&Var::A) + } + + fn get_b(&self) -> isize { + self.get(&Var::B) + } + + fn set(&mut self, v: &Var, value: isize) { + let length = self.program.len(); + let want = self.pos + *v as usize; + let want = match self.modes.mode_for(v) { + Mode::Position => self.get_pos(want) as usize, + Mode::Immediate => want, + Mode::Relative => (self.rel + self.get_pos(want)) as usize, + }; + if length < want { + let missing = (want - length) + 10000; + self.program.extend_from_slice(&vec![0; missing]) + } + self.program[want] = value; + } + + fn set_a(&mut self, value: isize) { + self.set(&Var::A, value) + } + + fn set_c(&mut self, value: isize) { + self.set(&Var::C, value) + } + + fn add(&mut self) -> usize { + self.set_c(self.get_a() + self.get_b()); + 4 + } + + fn multiply(&mut self) -> usize { + self.set_c(self.get_a() * self.get_b()); + 4 + } + + fn input(&mut self) -> usize { + let value = self.inputs.pop_front().expect("no input provided"); + self.set_a(value); + 2 + } + + fn output(&mut self) -> usize { + self.outputs.push_back(self.get_a()); + 2 + } + + fn jump_if_true(&mut self) -> usize { + if self.get_a() > 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn jump_if_false(&mut self) -> usize { + if self.get_a() == 0 { + self.pos = self.get_b() as usize; + return 0; + } + 3 + } + + fn less_than(&mut self) -> usize { + if self.get_a() < self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn equals(&mut self) -> usize { + if self.get_a() == self.get_b() { + self.set_c(1) + } else { + self.set_c(0) + } + 4 + } + + fn relative_adj(&mut self) -> usize { + self.rel = self.rel + self.get_a(); + 2 + } + + fn halt(&mut self) -> usize { + self.halted = true; + 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn example1() { + let program = vec![ + 109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99, + ]; + let mut computer = Computer::from(program.clone()); + computer.run(); + assert_eq!(computer.get_outputs().iter().eq(program.iter()), true) + } + + #[test] + fn example2() { + assert_eq!( + Computer::from(vec![1102, 34915192, 34915192, 7, 4, 7, 99, 0]).run_must_output(), + 1219070632396864 + ) + } + + #[test] + fn example3() { + assert_eq!( + Computer::from(vec![104, 1125899906842624, 99]).run_must_output(), + 1125899906842624 + ) + } + + #[test] + fn day5exampe1() { + let computer = Computer::from(vec![3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8]); + assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe2() { + let computer = Computer::from(vec![3, 3, 1108, -1, 8, 3, 4, 3, 99]); + assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe3() { + let computer = Computer::from(vec![ + 3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9, + ]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + let computer = Computer::from(vec![3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1); + } + + #[test] + fn day5exampe4() { + let computer = Computer::from(vec![ + 3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0, + 0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4, + 20, 1105, 1, 46, 98, 99, + ]); + assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 999); + assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1000); + assert_eq!(computer.clone_with_input(vec![9]).run_must_output(), 1001); + } + + #[test] + fn day9part1() { + let computer = Computer::from(File::open("input9").unwrap()); + assert_eq!( + computer.clone_with_input(vec![1]).run_must_output(), + 3063082071 + ); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b6b007e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod intcode; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}