Day 17 part 2
authorGrégory Burri <gregory.burri@matisa.ch>
Mon, 13 Jan 2020 12:20:38 +0000 (13:20 +0100)
committerGrégory Burri <gregory.burri@matisa.ch>
Mon, 13 Jan 2020 12:20:38 +0000 (13:20 +0100)
src/day04.rs
src/day17.rs
src/main.rs

index 42fb047..53a8c24 100644 (file)
@@ -37,14 +37,14 @@ pub fn nb_passwords_part2(min: i32, max: i32) -> i32 {
         &|digits: &Digits| {\r
             let mut last = digits[0];\r
             let mut n = 1;\r
-            for i in 1 .. digits.len() {\r
-                if digits[i] == last {\r
+            for d in &digits[1..] {\r
+                if *d == last {\r
                     n += 1;\r
                 } else {\r
                     if n == 2 { return true; }\r
                     n = 1;\r
                 }\r
-                last = digits[i];\r
+                last = *d;\r
             }\r
             n == 2\r
         }\r
@@ -57,7 +57,7 @@ fn nb_passwords(min: i32, max: i32, valid_password: &dyn Fn(&Digits) -> bool) ->
     let l = digits.len();\r
 \r
     fn set_range(from: usize, to: usize, value: u8, digits: &mut Digits) {\r
-        for i in from .. to { digits[i] = value; }\r
+        for d in &mut digits[from .. to] { *d = value; }\r
     };\r
 \r
     for i in (1 .. l).rev() {\r
index 8aae48a..75ed1a9 100644 (file)
@@ -1,6 +1,8 @@
 use super::intcode;\r
+use itertools::Itertools;\r
 use std::collections::HashSet;\r
 use std::convert::TryFrom;\r
+use std::ops::Range;\r
 \r
 #[derive(PartialEq, Eq, Copy, Clone, Debug)]\r
 enum Direction { Up, Left, Down, Right }\r
@@ -25,65 +27,55 @@ enum Movement { Left, Right }
 #[derive(PartialEq, Eq, Copy, Clone, Debug)]\r
 struct MovementCommand { mov: Movement, steps: u32 }\r
 \r
-pub struct RobotTrackingSystem {\r
-    output: Vec<i64>,\r
+struct RobotTrackingSystem {\r
     board: Vec<Vec<char>>,\r
     start_position: (i32, i32),\r
     start_dir: Direction,\r
-    crossings: Vec<(i32, i32)>,\r
-\r
     dir_commands: Vec<MovementCommand>,\r
-    commands_sequences: [Vec<(i32, i32)>; 3], // 3 sequences: A, B and C.\r
+    crossings: Vec<(i32, i32)>,\r
 }\r
 \r
 impl RobotTrackingSystem {\r
     fn new() -> Self {\r
         RobotTrackingSystem {\r
-            output: Vec::new(),\r
             board: Vec::<Vec<char>>::new(),\r
             start_position: (0, 0),\r
             start_dir: Direction::Up,\r
-            crossings: Vec::<(i32, i32)>::new(),\r
             dir_commands: Vec::<MovementCommand>::new(),\r
-            commands_sequences: [Vec::<(i32, i32)>::new(), Vec::<(i32, i32)>::new(), Vec::<(i32, i32)>::new()],\r
-        }\r
-    }\r
-\r
-    fn get(&self, x: i32, y: i32) -> Option<char> {\r
-        if x < self.board[0].len() as i32 && x >= 0 && y < self.board.len() as i32 && y >= 0 {\r
-            Some(self.board[y as usize][x as usize])\r
-        } else {\r
-            None\r
+            crossings: Vec::<(i32, i32)>::new(),\r
         }\r
     }\r
 \r
-    fn build_board_from_output(&mut self) {\r
-        // If the board has already been read.\r
-        if !self.board.is_empty() {\r
-            return;\r
-        }\r
-\r
+    fn from(output: &[i64]) -> Self {\r
+        let mut rts = RobotTrackingSystem::new();\r
         let mut current_line = Vec::<char>::new();\r
         let mut current_x = 0;\r
-        for c in self.output.iter() {\r
+        for c in output.iter() {\r
             if *c == 10 {\r
-                self.board.push(current_line);\r
+                rts.board.push(current_line);\r
                 current_line = Vec::<char>::new();\r
                 current_x = 0;\r
             } else {\r
                 let c = (*c as u8) as char;\r
                 if let Ok(dir) =  Direction::try_from(c) {\r
-                    self.start_position = (current_x, self.board.len() as i32);\r
-                    self.start_dir = dir\r
+                    rts.start_position = (current_x, rts.board.len() as i32);\r
+                    rts.start_dir = dir\r
                 }\r
 \r
                 current_line.push(c);\r
                 current_x += 1;\r
             }\r
         }\r
+        rts.run_through();\r
+        rts\r
+    }\r
 \r
-        self.output.clear();\r
-        self.run_through();\r
+    fn get(&self, x: i32, y: i32) -> Option<char> {\r
+        if x < self.board[0].len() as i32 && x >= 0 && y < self.board.len() as i32 && y >= 0 {\r
+            Some(self.board[y as usize][x as usize])\r
+        } else {\r
+            None\r
+        }\r
     }\r
 \r
     // Run the path and find the crossings and define the movements.\r
@@ -139,20 +131,127 @@ impl RobotTrackingSystem {
             break;\r
         }\r
     }\r
+}\r
 \r
-    fn find_sequences(&mut self) {\r
-        if !self.commands_sequences[0].is_empty() { return; }\r
+struct CommandSequences {\r
+    commands: Vec<(usize, Range<usize>)> // Each range is associated with a sequence number (first tuple value).\r
+}\r
 \r
+fn is_overlapping<T : PartialOrd>(r1: &Range<T>, r2: &Range<T>) -> bool {\r
+    r1.start < r2.start && r1.end > r2.start || r2.start < r1.start && r2.end > r1.start\r
+}\r
 \r
+impl CommandSequences {\r
+    fn new() -> Self {\r
+        CommandSequences {\r
+            commands: Vec::new()\r
+        }\r
     }\r
+\r
+    fn find_sequences(&mut self, movements: &[MovementCommand]) {\r
+        // For each sequence length we try to match them agains the movements.\r
+        let len_min = 3;\r
+        let len_max = 6;\r
+\r
+        for l1 in len_min ..= len_max {\r
+            for l2 in len_min ..= len_max {\r
+                for l3 in len_min ..= len_max {\r
+                    self.commands.clear();\r
+                    let mut position: usize = 0;\r
+                    for seq_num in 0 .. 3 {\r
+                        let l = match seq_num { 0 => l1, 1 => l2, _ => l3 };\r
+                        let range = position .. position + l;\r
+                        self.commands.push((seq_num, range.clone()));\r
+                        // Try to find the sequence elsewhere in 'movements'.\r
+                        let mut position2 = position + l;\r
+                        while position2 <= movements.len() - l {\r
+                            let range2 = position2 .. position2 + l;\r
+                            if !self.commands.iter().any(|(_, r)| is_overlapping(&r, &range2)) && movements.get(range.clone()) == movements.get(range2.clone()) {\r
+                                self.commands.push((seq_num, range2));\r
+                                position2 += l;\r
+                            } else {\r
+                                position2 += 1;\r
+                            }\r
+                        }\r
+\r
+                        // Update position to the next free position.\r
+                        while self.commands.iter().any(|(_, r)| r.contains(&position)) {\r
+                            position += 1;\r
+                        }\r
+                    }\r
+\r
+                    // Check if all movements are included into a sequence.\r
+                    if self.commands.iter().fold(0, |sum, (_, range)| sum + range.len()) == movements.len() {\r
+                        return;\r
+                    }\r
+                }\r
+\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
+struct Part1 { output: Vec<i64>, }\r
+\r
+impl Part1 { fn new() -> Self { Part1 { output: Vec::<i64>::new() } }}\r
+\r
+struct Part2 {\r
+    output: Vec<i64>,\r
+    rts: Option<RobotTrackingSystem>,\r
+    commands_sequences: CommandSequences,\r
+    input: Vec<i64>,\r
+    input_position: usize,\r
+    dust_collected: i64,\r
 }\r
 \r
-impl intcode::IO for RobotTrackingSystem {\r
+impl Part2 { fn new() -> Self { Part2 { output: Vec::<i64>::new(), rts: None, commands_sequences: CommandSequences::new(), input: Vec::new(), input_position: 0, dust_collected: 0 } } }\r
+\r
+impl intcode::IO for Part1 {\r
     // Read instructions\r
+    fn read(&mut self) -> i64 { 0 }\r
+\r
+    // Send to the output channel.\r
+    fn write(&mut self, value: i64) {\r
+        self.output.push(value);\r
+    }\r
+}\r
+\r
+impl intcode::IO for Part2 {\r
+    // Read instructions.\r
     fn read(&mut self) -> i64 {\r
-        self.build_board_from_output();\r
-        self.find_sequences();\r
-        42\r
+        if self.rts.is_none() {\r
+            self.rts = Some(RobotTrackingSystem::from(&self.output));\r
+            self.commands_sequences.find_sequences(&self.rts.as_ref().unwrap().dir_commands);\r
+\r
+            // 1: add the movements sequences: "A,B,C,A\n" // Max: 10 sequence calls.\r
+            for (i, (seq_num, _)) in self.commands_sequences.commands.iter().sorted_by(|(_, r1), (_, r2)| r1.start.cmp(&r2.start)).enumerate() {\r
+                if i > 0 { self.input.push(44); }\r
+                self.input.push(*seq_num as i64 + 65);\r
+            }\r
+            self.input.push(10);\r
+\r
+            // 2: Add the sequence A, B and C: "R,8,L,2,R,1\n", Max: ~6 movements.\r
+            for seq_num in 0 .. 3 {\r
+                let (_, sequence) = self.commands_sequences.commands.iter().find(|(s, _)| *s == seq_num).unwrap();\r
+                for (i, movement_command) in self.rts.as_ref().unwrap().dir_commands.get(sequence.clone()).unwrap().iter().enumerate() {\r
+\r
+                    if i > 0 { self.input.push(44); }\r
+                    if movement_command.mov == Movement::Left { self.input.push(76); } else { self.input.push(82); }\r
+                    self.input.push(44);\r
+                    for c in movement_command.steps.to_string().as_bytes() {\r
+                        self.input.push(*c as i64);\r
+                    }\r
+                }\r
+                self.input.push(10);\r
+            }\r
+\r
+            // 3: Add "y\n" (continuous video feed activated) or "n\n" (no video).\r
+            self.input.push(110);\r
+            self.input.push(10);\r
+        }\r
+\r
+        self.input_position += 1;\r
+        self.input[self.input_position - 1]\r
     }\r
 \r
     // Send to the output channel.\r
@@ -161,37 +260,19 @@ impl intcode::IO for RobotTrackingSystem {
     }\r
 \r
     fn finished(&mut self) {\r
-        self.build_board_from_output();\r
+        self.dust_collected = *self.output.last().unwrap();\r
     }\r
 }\r
 \r
 pub fn scaffold_intersections(code: &[i64]) -> i32 {\r
-    let mut rts = RobotTrackingSystem::new();\r
-    intcode::execute_op_code_with_custom_io(code, &mut rts);\r
+    let mut part1 = Part1::new();\r
+    intcode::execute_op_code_with_custom_io(code, &mut part1);\r
+    let rts = RobotTrackingSystem::from(&part1.output);\r
     rts.crossings.iter().fold(0, |sum, crossing| sum + crossing.0 * crossing.1)\r
 }\r
 \r
-pub fn part2(code: &[i64]) {\r
-    let mut rts = RobotTrackingSystem::new();\r
-    intcode::execute_op_code_with_custom_io(code, &mut rts);\r
-\r
-}\r
-\r
-#[cfg(test)]\r
-mod tests {\r
-    use super::*;\r
-    use itertools::Itertools;\r
-\r
-    #[test]\r
-    fn foo() {\r
-        let array = vec![1, 2, 3];\r
-        let s: i32 = array.iter().sum();\r
-        dbg!(s);\r
-        let mut combinations: Vec<Vec<i32>> = (3..=7).combinations_with_replacement(3).sorted_by(|v1, v2| {\r
-            let s1: i32 = v1.iter().sum();\r
-            let s2: i32 = v2.iter().sum();\r
-            s1.cmp(&s2)\r
-        }).collect();\r
-        println!("{:?}", combinations);\r
-    }\r
+pub fn collected_dust(code: &[i64]) -> i64 {\r
+    let mut part2 = Part2::new();\r
+    intcode::execute_op_code_with_custom_io(code, &mut part2);\r
+    part2.dust_collected\r
 }
\ No newline at end of file
index 71eec79..89e1295 100644 (file)
@@ -133,11 +133,10 @@ fn day16() -> String {
 
 fn day17() -> String {
     let mut code = common::read_list_of_numbers("data/day17.input", ",");
-    let n = day17::scaffold_intersections(&code);
+    let intersections = day17::scaffold_intersections(&code);
     code[0] = 2;
-    day17::part2(&code);
-    format!("part1: {}, part2: {}", n, "")
-
+    let dust = day17::collected_dust(&code);
+    format!("part1: {}, part2: {}", intersections, dust)
 }
 
 fn day18() -> String {