Day 18 master
authorGreg Burri <greg.burri@gmail.com>
Tue, 3 Feb 2026 20:30:13 +0000 (21:30 +0100)
committerGreg Burri <greg.burri@gmail.com>
Tue, 3 Feb 2026 20:30:13 +0000 (21:30 +0100)
src/day18.rs [new file with mode: 0644]
src/days.rs
src/main.rs

diff --git a/src/day18.rs b/src/day18.rs
new file mode 100644 (file)
index 0000000..a8ed7f1
--- /dev/null
@@ -0,0 +1,140 @@
+use std::{
+    collections::{HashMap, HashSet, VecDeque},
+    io::BufRead,
+};
+
+type Pos = (i32, i32);
+
+pub struct CorruptedLocations {
+    positions: Vec<Pos>,
+    x_max: i32,
+    y_max: i32,
+}
+
+pub fn read_byte_positions(reader: &mut dyn BufRead) -> CorruptedLocations {
+    let mut positions = Vec::new();
+    for l in reader.lines() {
+        let pos: Vec<i32> = l
+            .unwrap()
+            .split(",")
+            .map(|s| s.parse::<i32>().unwrap())
+            .collect();
+        positions.push((pos[0], pos[1]));
+    }
+
+    let x_max = positions.iter().max_by(|a, b| a.0.cmp(&b.0)).unwrap().0;
+    let y_max = positions.iter().max_by(|a, b| a.1.cmp(&b.1)).unwrap().1;
+
+    CorruptedLocations {
+        positions,
+        x_max,
+        y_max,
+    }
+}
+
+fn neighbours(x: i32, y: i32) -> [Pos; 4] {
+    [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]
+}
+
+pub fn shortest_path(
+    corrupted_locations: &CorruptedLocations,
+    n_first_location: usize,
+) -> Vec<Pos> {
+    let locations: HashSet<&Pos> =
+        HashSet::from_iter(corrupted_locations.positions[0..n_first_location].iter());
+    let mut heads = VecDeque::from([(0, 0)]);
+
+    let mut visited: HashMap<Pos, Pos> = HashMap::from([((0, 0), (0, 0))]);
+
+    while let Some((x, y)) = heads.pop_front() {
+        for (xn, yn) in neighbours(x, y) {
+            if xn < 0
+                || xn > corrupted_locations.x_max
+                || yn < 0
+                || yn > corrupted_locations.y_max
+                || locations.contains(&(xn, yn))
+            {
+                continue;
+            }
+
+            if xn == corrupted_locations.x_max && yn == corrupted_locations.y_max {
+                let mut path = vec![(xn, yn)];
+                let (mut x, mut y) = (x, y);
+                while x != 0 || y != 0 {
+                    path.push((x, y));
+                    (x, y) = visited[&(x, y)];
+                }
+                return path;
+            }
+
+            visited.entry((xn, yn)).or_insert_with(|| {
+                heads.push_back((xn, yn));
+                (x, y)
+            });
+        }
+    }
+
+    Vec::new()
+}
+
+pub fn first_position_cutting_the_path(
+    corrupted_locations: &CorruptedLocations,
+    start_length: usize,
+) -> Pos {
+    let mut path = shortest_path(corrupted_locations, start_length);
+    for l in start_length + 1..=corrupted_locations.positions.len() {
+        if path.contains(&corrupted_locations.positions[l - 1]) {
+            path = shortest_path(corrupted_locations, l);
+            // Can't find a path -> return the previous corrupted byte position.
+            if path.is_empty() {
+                return corrupted_locations.positions[l - 1];
+            }
+        }
+    }
+
+    (0, 0)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    static BYTE_POSITIONS: &str = "5,4
+4,2
+4,5
+3,0
+2,1
+6,3
+2,4
+1,5
+0,6
+3,3
+2,6
+5,1
+1,2
+5,5
+2,5
+6,5
+1,4
+0,4
+6,4
+1,1
+6,1
+1,0
+0,5
+1,6
+2,0";
+
+    #[test]
+    fn part1() {
+        let positions = read_byte_positions(&mut BYTE_POSITIONS.as_bytes());
+        assert_eq!(shortest_path(&positions, 12).len(), 22);
+    }
+
+    #[test]
+    fn part2() {
+        let positions = read_byte_positions(&mut BYTE_POSITIONS.as_bytes());
+        let pos = first_position_cutting_the_path(&positions, 12);
+        assert_eq!(pos, (6, 1));
+    }
+}
index 3fc858a..4735e09 100644 (file)
@@ -156,3 +156,10 @@ pub fn day17(reader: &mut dyn BufRead) -> String {
     let reg_a = day17::fix_corrupted_reg_a(&mut state, &program);
     format!("part1: '{output}', part2: {reg_a}")
 }
+
+pub fn day18(reader: &mut dyn BufRead) -> String {
+    let corrupted_locations = day18::read_byte_positions(reader);
+    let path = day18::shortest_path(&corrupted_locations, 1024);
+    let (x, y) = day18::first_position_cutting_the_path(&corrupted_locations, 1024);
+    format!("part1: {}, part2: {},{}", path.len(), x, y)
+}
index 98da7f9..8135823 100644 (file)
@@ -17,7 +17,7 @@ mod day14;
 mod day15;
 mod day16;
 mod day17;
-// mod day18;
+mod day18;
 // mod day19;
 // mod day20;
 // mod day21;
@@ -48,7 +48,7 @@ fn main() {
         days::day15,
         days::day16,
         days::day17,
-        // days::day18,
+        days::day18,
         // days::day19,
         // days::day20,
         // days::day21,