From: Grégory Burri Date: Wed, 11 Dec 2019 15:25:37 +0000 (+0100) Subject: * Day 11 X-Git-Url: https://git.euphorik.ch/?a=commitdiff_plain;h=d1d78479c650bbd11a8623ea1dd9dfe95b4fa83d;p=advent_of_code_2019.git * Day 11 * Merge intcode from day 05 and day09 to 'intcode' module --- diff --git a/src/common.rs b/src/common.rs index 30155d0..b3db167 100644 --- a/src/common.rs +++ b/src/common.rs @@ -10,4 +10,22 @@ where { fs::read_to_string(file).unwrap().split(sep).map(|line| line.parse::().unwrap()).collect() +} + +pub fn layer_to_printable_string(layer: &[u8], width: usize) -> String { + let mut result = String::new(); + let mut i = 0; + + loop { + for _ in 0 .. width { + if layer[i] == 0 { + result += " "; + } else { + result += "█"; + } + i += 1; + if i >= layer.len() { return result } + } + result += "\n"; + } } \ No newline at end of file diff --git a/src/day02.rs b/src/day02.rs index 81dfaaf..fd26331 100644 --- a/src/day02.rs +++ b/src/day02.rs @@ -11,7 +11,7 @@ fn execute_op_code(code: &mut [i32]) -> i32 { 1 => code[code[cursor + 3] as usize] = code[code[cursor + 1] as usize] + code[code[cursor + 2] as usize], 2 => code[code[cursor + 3] as usize] = code[code[cursor + 1] as usize] * code[code[cursor + 2] as usize], 99 => return code[0], - _ => panic!("Unkown code: {}", code[cursor]) + _ => panic!("Unknown code: {}", code[cursor]) } cursor += 4; } diff --git a/src/day05.rs b/src/day05.rs deleted file mode 100644 index 61f6203..0000000 --- a/src/day05.rs +++ /dev/null @@ -1,124 +0,0 @@ -// 'true' -> immediate mode, 'false' -> position mode. -fn read_op_and_modes(mut code: i32) -> (i32, [bool; 3]) { - let mut modes: [bool; 3] = [ false, false, false ]; - - if code >= 10_000 { - modes[2] = true; - code -= 10_000; - } - - if code >= 1_000 { - modes[1] = true; - code -= 1_000; - } - - if code >= 100 { - modes[0] = true; - code -= 100; - } - - (code, modes) -} - -pub fn execute_op_code(code: &mut [i32], input: &[i32]) -> Vec { - let mut cursor = 0; - let mut input_cursor = 0; - let mut output = Vec::::new(); - - fn read(position: usize, code: &[i32], mode: bool) -> i32 { - if mode { code[position] } else { code[code[position] as usize] } - }; - - fn jump_if(cond: bool, cursor: usize, code: &[i32], modes: [bool; 3]) -> usize { - let value = read(cursor + 1, &code, modes[0]); - if cond == (value != 0) { - read(cursor + 2, &code, modes[1]) as usize - } else { - cursor + 3 - } - }; - - loop { - let (op, modes) = read_op_and_modes(code[cursor]); - - match op { - // Sum. - 1 => { - code[code[cursor + 3] as usize] = read(cursor + 1, &code, modes[0]) + read(cursor + 2, &code, modes[1]); - cursor += 4; - }, - - // Multiply. - 2 => { - code[code[cursor + 3] as usize] = read(cursor + 1, &code, modes[0]) * read(cursor + 2, &code, modes[1]); - cursor += 4; - }, - - // Input. - 3 => { - code[code[cursor + 1] as usize] = input[input_cursor]; - input_cursor += 1; - cursor += 2; - } - - // Output. - 4 => { - output.push(read(cursor + 1, &code, modes[0])); - cursor += 2; - } - - // Jump-if-true. - 5 => cursor = jump_if(true, cursor, &code, modes), - - // Jump-if-false. - 6 => cursor = jump_if(false, cursor, &code, modes), - - // Less than. - 7 => { - code[code[cursor + 3] as usize] = - if read(cursor + 1, &code, modes[0]) < read(cursor + 2, &code, modes[1]) { 1 } else { 0 }; - cursor += 4; - }, - - // Equals. - 8 => { - code[code[cursor + 3] as usize] = - if read(cursor + 1, &code, modes[0]) == read(cursor + 2, &code, modes[1]) { 1 } else { 0 }; - cursor += 4; - }, - - 99 => break, - _ => panic!("Unkown code: {}", code[cursor]) - } - } - output -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn part1() { - let mut c = [1002, 4, 3, 4, 33]; - let _ = execute_op_code(&mut c, &Vec::new()); - assert_eq!(c[4], 99); - } - - #[test] - fn part2() { - let c = [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]; - - let mut c1 = c; - let r1 = execute_op_code(&mut c1, &[7]); - assert_eq!(r1[0], 999); - - let mut c2 = c; - let r2 = execute_op_code(&mut c2, &[8]); - assert_eq!(r2[0], 1000); - - let mut c3 = c; - let r3 = execute_op_code(&mut c3, &[9]); - assert_eq!(r3[0], 1001); - } -} \ No newline at end of file diff --git a/src/day06.rs b/src/day06.rs index fddb899..c0d4ae0 100644 --- a/src/day06.rs +++ b/src/day06.rs @@ -41,7 +41,7 @@ pub fn nb_orbital_transfers(orbits: &Orbits, loc1: &str, loc2: &str) -> usize { } #[cfg(test)] -mod test { +mod tests { use super::*; #[test] diff --git a/src/day07.rs b/src/day07.rs index e69de29..fea25ff 100644 --- a/src/day07.rs +++ b/src/day07.rs @@ -0,0 +1,11 @@ + + + +#[cfg(test)] +mod tests { + + #[test] + fn part1 () { + + } +} \ No newline at end of file diff --git a/src/day08.rs b/src/day08.rs index 95b0385..50fb119 100644 --- a/src/day08.rs +++ b/src/day08.rs @@ -47,24 +47,6 @@ pub fn merge_layers(layers: &[Vec]) -> Vec { result } -pub fn layer_to_printable_string(layer: &[u8], width: u32) -> String { - let mut result = String::new(); - let mut i = 0; - - loop { - for _ in 0 .. width { - if layer[i] == 0 { - result += " "; - } else { - result += "█"; - } - i += 1; - if i >= layer.len() { return result } - } - result += "\n"; - } -} - pub fn one_digits_times_two_digits(layer: &[u8]) -> u32 { count(layer, 1) * count(layer, 2) } diff --git a/src/day09.rs b/src/day09.rs deleted file mode 100644 index ea234b2..0000000 --- a/src/day09.rs +++ /dev/null @@ -1,152 +0,0 @@ -#[derive(Copy, Clone, Debug)] -enum Mode { - Position, - Immediate, - Relative -} - -// 'true' -> immediate mode, 'false' -> position mode. -fn read_op_and_modes(mut code: i64) -> (i64, [Mode; 3]) { - let mut modes: [Mode; 3] = [ Mode::Position, Mode::Position, Mode::Position ]; - - for i in (2..=4).rev() { - let power = 10i64.pow(i); - if code >= 2 * power { - modes[i as usize - 2] = Mode::Relative; - code -= 2 * power; - } else if code >= power { - modes[i as usize - 2] = Mode::Immediate; - code -= power; - } - } - - (code, modes) -} - -pub fn execute_op_code(code: &[i64], input: &[i64]) -> Vec { - let mut code = Vec::from(code); - let mut cursor = 0; - let mut input_cursor = 0; - let mut output = Vec::::new(); - let mut relative_base = 0; - - fn read(position: usize, code: &[i64], mode: Mode, relative_base: i64) -> i64 { - if position >= code.len() { return 0 } - - match mode { - Mode::Position => if code[position] as usize >= code.len() { 0 } else { code[code[position] as usize] }, - Mode::Immediate => code[position], - Mode::Relative => if (code[position] + relative_base) as usize >= code.len() { 0 } else { code[(code[position] + relative_base) as usize] } - } - }; - - fn write(position: usize, value: i64, code: &mut Vec, mode: Mode, relative_base: i64) { - let ref_position = - match mode { - Mode::Position => code[position] as usize, - Mode::Immediate => panic!("Can't write un immediate mode"), - Mode::Relative => (code[position] + relative_base) as usize - }; - - if ref_position >= code.len() { - code.resize(ref_position + 1, 0); - } - - code[ref_position] = value; - } - - fn jump_if(cond: bool, cursor: usize, code: &[i64], modes: [Mode; 3], relative_base: i64) -> usize { - let value = read(cursor + 1, &code, modes[0], relative_base); - if cond == (value != 0) { - read(cursor + 2, &code, modes[1], relative_base) as usize - } else { - cursor + 3 - } - }; - - loop { - let (op, modes) = read_op_and_modes(code[cursor]); - - match op { - // Sum. - 1 => { - write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) + read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base); - cursor += 4; - }, - - // Multiply. - 2 => { - write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) * read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base); - cursor += 4; - }, - - // Input. - 3 => { - write(cursor + 1, input[input_cursor], &mut code, modes[0], relative_base); - input_cursor += 1; - cursor += 2; - } - - // Output. - 4 => { - output.push(read(cursor + 1, &code, modes[0], relative_base)); - cursor += 2; - } - - // Jump-if-true. - 5 => cursor = jump_if(true, cursor, &code, modes, relative_base), - - // Jump-if-false. - 6 => cursor = jump_if(false, cursor, &code, modes, relative_base), - - // Less than. - 7 => { - write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) < read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base); - cursor += 4; - }, - - // Equals. - 8 => { - write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) == read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base); - cursor += 4; - }, - - // Change relative base. - 9 => { - relative_base += read(cursor + 1, &code, modes[0], relative_base); - cursor += 2; - } - - 99 => break, - - _ => panic!("Unkown code: {}", code[cursor]) - } - } - output -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn copy_of_itself() { - let c = [109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99]; - let r = execute_op_code(&c, &Vec::new()); - assert_eq!(r, c); - } - - #[test] - fn output_big_number() { - let c = [1102,34_915_192,34_915_192,7,4,7,99,0]; - let r = execute_op_code(&c, &Vec::new()); - assert_eq!(r[0], 1_219_070_632_396_864); - } - - #[test] - fn input_big_number() { - let c = [104,1_125_899_906_842_624,99]; - let r = execute_op_code(&c, &Vec::new()); - assert_eq!(r[0], 1_125_899_906_842_624); - } -} \ No newline at end of file diff --git a/src/day11.rs b/src/day11.rs new file mode 100644 index 0000000..e91d813 --- /dev/null +++ b/src/day11.rs @@ -0,0 +1,116 @@ +use std::collections::HashMap; +use super::intcode; + +enum NextCommand { + ColorToPaint, + Turn +} + +struct Robot { + next_command: NextCommand, + current_pos: (i32, i32), + current_dir: i32, // 0: up, 1: right, 2: down, 3: left. + panels: HashMap<(i32, i32), i64> +} + +impl Robot { + fn new() -> Self { + Robot { + next_command: NextCommand::ColorToPaint, + current_pos: (0, 0), + current_dir: 0, + panels: HashMap::new() + } + } +} + +impl intcode::IO for Robot { + fn read(&mut self) -> i64 { + *self.panels.get(&self.current_pos).unwrap_or(&0) + } + + fn write(&mut self, value: i64) { + self.next_command = + match self.next_command { + NextCommand::ColorToPaint => { self.panels.insert(self.current_pos, value); NextCommand::Turn }, + NextCommand::Turn => { + self.current_dir = (self.current_dir + if value == 0 { 3 } else { 1 }) % 4; + self.current_pos = + match self.current_dir { + 0 => (self.current_pos.0, self.current_pos.1 + 1), + 1 => (self.current_pos.0 + 1, self.current_pos.1), + 2 => (self.current_pos.0, self.current_pos.1 - 1), + 3 | _ => (self.current_pos.0 - 1, self.current_pos.1) + }; + NextCommand::ColorToPaint + } + } + } +} + +pub fn run_robot(code: &[i64], initial_value: i64) -> HashMap<(i32, i32), i64> { + let mut robot = Robot::new(); + if initial_value != 0 { + robot.panels.insert((0, 0), initial_value); + } + + intcode::execute_op_code_with_custom_io(code, &mut robot); + robot.panels +} + +pub fn panels_to_layer(panels: &HashMap<(i32, i32), i64>) -> (Vec, usize) { + let coordinates: Vec<&(i32, i32)> = panels.keys().collect(); + let min_x = coordinates.iter().min_by_key(|(x, _)| x).unwrap().0; + let max_x = coordinates.iter().max_by_key(|(x, _)| x).unwrap().0; + let min_y = coordinates.iter().min_by_key(|(_, y)| y).unwrap().1; + let max_y = coordinates.iter().max_by_key(|(_, y)| y).unwrap().1; + + let width = (max_x - min_x) as usize + 1; + let height = (max_y - min_y) as usize + 1; + + let mut layer = Vec::new(); + layer.resize(width * height, 0); + + for x in min_x ..= max_x { + for y in min_y ..= max_y { + let pos = (x - min_x) + ((height as i32 - y + min_y - 1) * width as i32); // Y axis is down. + layer[pos as usize] = *panels.get(&(x, y)).unwrap_or(&0) as u8; + } + } + + (layer, width) +} + +#[cfg(test)] +mod tests { + use super::*; + use super::intcode::IO; + + #[test] + fn part1() { + let mut robot = Robot::new(); + robot.write(1); // Paint white. + robot.write(0); // Turn left. + + robot.write(0); // Paint black. + robot.write(0); // Turn left. + + robot.write(1); // Paint white. + robot.write(0); // Turn left. + + robot.write(1); // Paint white. + robot.write(0); // Turn left. + + robot.write(0); // Paint black. + robot.write(1); // Turn right. + + robot.write(1); // Paint white. + robot.write(0); // Turn left. + + robot.write(1); // Paint white. + robot.write(0); // Turn left. + + assert_eq!(robot.panels.len(), 6); + } + +} \ No newline at end of file diff --git a/src/intcode.rs b/src/intcode.rs new file mode 100644 index 0000000..b1968ac --- /dev/null +++ b/src/intcode.rs @@ -0,0 +1,202 @@ +#[derive(Copy, Clone, Debug)] +enum Mode { + Position, + Immediate, + Relative +} + +pub trait IO { + fn read(&mut self) -> i64; + fn write(&mut self, value: i64); +} + +struct Buffer { + output: Vec, + input: Vec +} + +impl Buffer { + fn from(input: &[i64]) -> Self { + Buffer { + output: Vec::new(), + input: Vec::from(input) + } + } +} + +impl IO for Buffer { + fn read(&mut self) -> i64 { + self.input.remove(0) + } + + fn write(&mut self, value: i64) { + self.output.push(value) + } +} + +// 'true' -> immediate mode, 'false' -> position mode. +fn read_op_and_modes(mut code: i64) -> (i64, [Mode; 3]) { + let mut modes: [Mode; 3] = [ Mode::Position, Mode::Position, Mode::Position ]; + + for i in (2..=4).rev() { + let power = 10i64.pow(i); + if code >= 2 * power { + modes[i as usize - 2] = Mode::Relative; + code -= 2 * power; + } else if code >= power { + modes[i as usize - 2] = Mode::Immediate; + code -= power; + } + } + + (code, modes) +} + +pub fn execute_op_code(code: &[i64], input: &[i64]) -> Vec { + let mut buffer = Buffer::from(input); + execute_op_code_with_custom_io(code, &mut buffer); + buffer.output +} + +pub fn execute_op_code_with_custom_io(code: &[i64], io: &mut dyn IO) { + let mut code = Vec::from(code); + let mut cursor = 0; + let mut relative_base = 0; + + fn read(position: usize, code: &[i64], mode: Mode, relative_base: i64) -> i64 { + if position >= code.len() { return 0 } + + match mode { + Mode::Position => if code[position] as usize >= code.len() { 0 } else { code[code[position] as usize] }, + Mode::Immediate => code[position], + Mode::Relative => if (code[position] + relative_base) as usize >= code.len() { 0 } else { code[(code[position] + relative_base) as usize] } + } + }; + + fn write(position: usize, value: i64, code: &mut Vec, mode: Mode, relative_base: i64) { + let ref_position = + match mode { + Mode::Position => code[position] as usize, + Mode::Immediate => panic!("Can't write un immediate mode"), + Mode::Relative => (code[position] + relative_base) as usize + }; + + if ref_position >= code.len() { + code.resize(ref_position + 1, 0); + } + + code[ref_position] = value; + } + + fn jump_if(cond: bool, cursor: usize, code: &[i64], modes: [Mode; 3], relative_base: i64) -> usize { + let value = read(cursor + 1, &code, modes[0], relative_base); + if cond == (value != 0) { + read(cursor + 2, &code, modes[1], relative_base) as usize + } else { + cursor + 3 + } + }; + + loop { + let (op, modes) = read_op_and_modes(code[cursor]); + + match op { + // Sum. + 1 => { + write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) + read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base); + cursor += 4; + }, + + // Multiply. + 2 => { + write(cursor + 3, read(cursor + 1, &code, modes[0], relative_base) * read(cursor + 2, &code, modes[1], relative_base), &mut code, modes[2], relative_base); + cursor += 4; + }, + + // Input. + 3 => { + write(cursor + 1, io.read(), &mut code, modes[0], relative_base); + cursor += 2; + } + + // Output. + 4 => { + io.write(read(cursor + 1, &code, modes[0], relative_base)); + cursor += 2; + } + + // Jump-if-true. + 5 => cursor = jump_if(true, cursor, &code, modes, relative_base), + + // Jump-if-false. + 6 => cursor = jump_if(false, cursor, &code, modes, relative_base), + + // Less than. + 7 => { + write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) < read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base); + cursor += 4; + }, + + // Equals. + 8 => { + write(cursor + 3, if read(cursor + 1, &code, modes[0], relative_base) == read(cursor + 2, &code, modes[1], relative_base) { 1 } else { 0 }, &mut code, modes[2], relative_base); + cursor += 4; + }, + + // Change relative base. + 9 => { + relative_base += read(cursor + 1, &code, modes[0], relative_base); + cursor += 2; + } + + 99 => break, + + _ => panic!("Unknown code: {}", code[cursor]) + } + } +} + +#[cfg(test)] +mod tests_day05 { + use super::*; + + #[test] + fn part2() { + let c = [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]; + + let r1 = execute_op_code(&c, &[7]); + assert_eq!(r1[0], 999); + + let r2 = execute_op_code(&c, &[8]); + assert_eq!(r2[0], 1000); + + let r3 = execute_op_code(&c, &[9]); + assert_eq!(r3[0], 1001); + } +} + +#[cfg(test)] +mod tests_day09 { + use super::*; + + #[test] + fn copy_of_itself() { + let c = [109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99]; + let r = execute_op_code(&c, &Vec::new()); + assert_eq!(r, c); + } + + #[test] + fn output_big_number() { + let c = [1102,34_915_192,34_915_192,7,4,7,99,0]; + let r = execute_op_code(&c, &Vec::new()); + assert_eq!(r[0], 1_219_070_632_396_864); + } + + #[test] + fn input_big_number() { + let c = [104,1_125_899_906_842_624,99]; + let r = execute_op_code(&c, &Vec::new()); + assert_eq!(r[0], 1_125_899_906_842_624); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 42699c7..99ea6e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,15 +2,15 @@ use std::env; use std::fs; use std::time::Instant; +mod common; +mod intcode; mod day01; mod day02; mod day03; -mod day05; mod day06; mod day07; mod day08; -mod day09; -mod common; +mod day11; fn day01() -> String { let masses = common::read_list_of_numbers("data/day01.input", "\n"); @@ -38,7 +38,7 @@ fn day04() -> String { fn day05() -> String { let code = common::read_list_of_numbers("data/day05.input", ","); - format!("part1: {:?}, part2: {:?}", day05::execute_op_code(&mut Vec::from(&code[..]), &[1]), day05::execute_op_code(&mut Vec::from(&code[..]), &[5])) + format!("part1: {:?}, part2: {:?}", intcode::execute_op_code(&code, &[1]), intcode::execute_op_code(&code, &[5])) } fn day06() -> String { @@ -61,12 +61,25 @@ fn day08() -> String { let layer = day08::layer_with_fewer_0(&layers[..]); let merged = day08::merge_layers(&layers[..]); - format!("part1: {}, part2:\n{}", day08::one_digits_times_two_digits(layer), day08::layer_to_printable_string(&merged, 25)) + format!("part1: {}, part2:\n{}", day08::one_digits_times_two_digits(layer), common::layer_to_printable_string(&merged, 25)) } fn day09() -> String { let code = common::read_list_of_numbers::<&str, i64>("data/day09.input", ","); - format!("part1: {:?}, part2: {:?}", day09::execute_op_code(&code, &[1]), day09::execute_op_code(&code, &[2])) + + format!("part1: {:?}, part2: {:?}", intcode::execute_op_code(&code, &[1]), intcode::execute_op_code(&code, &[2])) +} + +fn day10() -> String { + format!("") +} + +fn day11() -> String { + let code = common::read_list_of_numbers::<&str, i64>("data/day11.input", ","); + let panels = day11::run_robot(&code, 1); + let (layer, width) = day11::panels_to_layer(&panels); + + format!("part1: {:?}, part2:\n{}", day11::run_robot(&code, 0).len(), common::layer_to_printable_string(&layer, width)) } fn format_micros(t: u128) -> String { @@ -96,7 +109,9 @@ fn main() { day06, day07, day08, - day09 + day09, + day10, + day11 ); let args: Vec = env::args().skip(1).collect();