Day 12
authorGreg Burri <greg.burri@gmail.com>
Mon, 12 Dec 2022 09:30:23 +0000 (10:30 +0100)
committerGreg Burri <greg.burri@gmail.com>
Mon, 12 Dec 2022 09:30:23 +0000 (10:30 +0100)
src/day12.rs [new file with mode: 0644]
src/days.rs [new file with mode: 0644]
src/main.rs

diff --git a/src/day12.rs b/src/day12.rs
new file mode 100644 (file)
index 0000000..1e03ed3
--- /dev/null
@@ -0,0 +1,96 @@
+#[derive(Debug)]
+pub struct Heightmap {
+    elevations: Vec<Vec<i32>>,
+    start: (usize, usize),
+    end: (usize, usize),
+}
+
+fn char_to_num(c: char) -> i32 {
+    c as i32 - 'a' as i32
+}
+
+pub fn parse(input: &str) -> Heightmap {
+    let mut hm = Heightmap { elevations: Vec::new(), start: (0, 0), end: (0, 0) };
+    for (i, l) in input.lines().enumerate() {
+        let mut row = Vec::new();
+        for (j, c) in l.trim().chars().enumerate() {
+            if c == 'S' {
+                hm.start = (i, j);
+                row.push(char_to_num('a'));
+            } else if c == 'E' {
+                hm.end = (i, j);
+                row.push(char_to_num('z'));
+            } else {
+                row.push(char_to_num(c));
+            }
+        }
+        hm.elevations.push(row);
+    }
+    hm
+}
+
+#[derive(PartialEq)]
+pub enum Path {
+    StartToEnd,
+    EndTo0Elevation,
+}
+
+pub fn nb_steps(hm: &Heightmap, path: Path) -> i32 {
+    let (n, m) = (hm.elevations.len(), hm.elevations[0].len());
+    let mut visited: Vec<Vec<i32>> = Vec::new();
+    for _ in 0..n { visited.push(vec![-1; m]); }
+    let mut positions = vec![hm.end];
+    let mut next_positions = Vec::new();
+    let mut step = -1;
+
+    let neighbors = |i, j| {
+        let mut positions = Vec::new();
+        if i > 0 { positions.push((i - 1, j)); }
+        if i < n - 1 { positions.push((i + 1, j)); }
+        if j > 0 { positions.push((i, j - 1)); }
+        if j < m - 1 { positions.push((i, j + 1)); }
+        positions
+    };
+
+    loop {
+        step = step + 1;
+        for (i, j) in positions.drain(..) {
+            if path == Path::StartToEnd && (i, j) == hm.start || path == Path::EndTo0Elevation && hm.elevations[i][j] == 0 {
+                return step;
+            }
+
+            visited[i][j] = step;
+            for (ni, nj) in neighbors(i, j) {
+                if visited[ni][nj] == -1 && hm.elevations[i][j] - hm.elevations[ni][nj] <= 1 {
+                    visited[ni][nj] = step;
+                    next_positions.push((ni, nj));
+                }
+            }
+        }
+        std::mem::swap(&mut next_positions, &mut positions);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    static HEIGHTMAP: &str =
+        "Sabqponm
+         abcryxxl
+         accszExk
+         acctuvwj
+         abdefghi";
+
+    #[test]
+    fn part1() {
+        let heightmap = parse(HEIGHTMAP);
+        assert_eq!(nb_steps(&heightmap, Path::StartToEnd), 31);
+    }
+
+    #[test]
+    fn part2() {
+        let heightmap = parse(HEIGHTMAP);
+        assert_eq!(nb_steps(&heightmap, Path::EndTo0Elevation), 29);
+    }
+}
diff --git a/src/days.rs b/src/days.rs
new file mode 100644 (file)
index 0000000..ce0d798
--- /dev/null
@@ -0,0 +1,85 @@
+use std::{fs, io::{BufReader, Seek, SeekFrom}};
+
+use crate::*;
+
+pub fn day01() -> String {
+    let f = fs::File::open("data/day01.input").unwrap();
+    let calories = day01::read_calories(BufReader::new(f));
+    format!("part1: {}, part2: {}", day01::get_most_calories(&calories), day01::get_sum_most_three_calories(&calories))
+}
+
+pub fn day02() -> String {
+    let mut f = fs::File::open("data/day02.input").unwrap();
+    let shapes = day02::read_shapes(BufReader::new(f.try_clone().unwrap()));
+    let _ = f.seek(SeekFrom::Start(0));
+    let shapes_2 = day02::read_shapes_2(BufReader::new(f));
+    format!("part1: {}, part2: {}", day02::get_score(&shapes), day02::get_score(&shapes_2))
+}
+
+pub fn day03() -> String {
+    let rucksacks = day03::parse(&fs::read_to_string("data/day03.input").unwrap());
+    format!("part1: {}, part2: {}", day03::priority_sum(&rucksacks), day03::badge_sum(&rucksacks))
+}
+
+pub fn day04() -> String {
+    let pairs = day04::parse(&fs::read_to_string("data/day04.input").unwrap());
+    format!("part1: {}, part2: {}", day04::number_fully_contain(&pairs), day04::number_overlaps(&pairs))
+}
+
+pub fn day05() -> String {
+    let (mut stacks, moves) = day05::parse(&fs::read_to_string("data/day05.input").unwrap());
+    let mut stacks2 = stacks.clone();
+    day05::apply_moves_by_crate_mover_9000(&mut stacks, &moves);
+    day05::apply_moves_by_crate_mover_9001(&mut stacks2, &moves);
+    format!("part1: {}, part2: {}", day05::get_top_as_string(&stacks), day05::get_top_as_string(&stacks2))
+}
+
+pub fn day06() -> String {
+    let signals = fs::read_to_string("data/day06.input").unwrap();
+    format!("part1: {}, part2: {}", day06::first_marker_pos(&signals, 4), day06::first_marker_pos(&signals, 14))
+}
+
+pub fn day07() -> String {
+    let root = day07::parse(&fs::read_to_string("data/day07.input").unwrap());
+
+    let (root_size, sum_part1, ) = {
+        let mut sizes: Vec<i64> = Vec::new();
+        (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::<i64>())
+    };
+
+    let min_part2 = {
+        let to_free = root_size - (70_000_000 - 30_000_000);
+        let mut sizes: Vec<i64> = Vec::new();
+        root.dir_sizes(|size| size >= to_free, &mut sizes);
+        *sizes.iter().min().unwrap()
+    };
+
+    format!("part1: {}, part2: {}", sum_part1, min_part2)
+}
+
+pub fn day08() -> String {
+    let forest = day08::parse(&fs::read_to_string("data/day08.input").unwrap());
+    format!("part1: {}, part2: {}", day08::number_of_visible_trees(&forest), day08::best_scenic_score(&forest))
+}
+
+pub fn day09() -> String {
+    let movements = day09::parse(&fs::read_to_string("data/day09.input").unwrap());
+    format!("part1: {}, part2: {}", day09::nb_positions_visited_by_tail::<2>(&movements), day09::nb_positions_visited_by_tail::<10>(&movements))
+}
+
+pub fn day10() -> String {
+    let instructions = day10::parse(&fs::read_to_string("data/day10.input").unwrap());
+    let mut screen = day10::Screen::new();
+    let sum_signal_strength = screen.draw_screen(&instructions);
+    format!("part1: {}, part2: \n{}", sum_signal_strength, screen.to_ascii())
+}
+
+pub fn day11() -> String {
+    let monkeys = day11::parse(&fs::read_to_string("data/day11.input").unwrap());
+    format!("part1: {}, part2: {}", day11::run(&mut monkeys.clone(), 20, 3), day11::run(&mut monkeys.clone(), 10000, 1))
+}
+
+pub fn day12() -> String {
+    let heightmap = day12::parse(&fs::read_to_string("data/day12.input").unwrap());
+    format!("part1: {}, part2: {}", day12::nb_steps(&heightmap, day12::Path::StartToEnd), day12::nb_steps(&heightmap, day12::Path::EndTo0Elevation))
+}
index 0e63823..d9e8472 100644 (file)
@@ -1,6 +1,7 @@
-use std::{env, fs, time::Instant, io::{BufReader, Seek, SeekFrom}};
+use std::{env, time::Instant};
 
 mod common;
+mod days;
 mod day01;
 mod day02;
 mod day03;
@@ -12,114 +13,24 @@ mod day08;
 mod day09;
 mod day10;
 mod day11;
-
-fn day01() -> String {
-    let f = fs::File::open("data/day01.input").unwrap();
-    let calories = day01::read_calories(BufReader::new(f));
-    format!("part1: {}, part2: {}", day01::get_most_calories(&calories), day01::get_sum_most_three_calories(&calories))
-}
-
-fn day02() -> String {
-    let mut f = fs::File::open("data/day02.input").unwrap();
-    let shapes = day02::read_shapes(BufReader::new(f.try_clone().unwrap()));
-    let _ = f.seek(SeekFrom::Start(0));
-    let shapes_2 = day02::read_shapes_2(BufReader::new(f));
-    format!("part1: {}, part2: {}", day02::get_score(&shapes), day02::get_score(&shapes_2))
-}
-
-fn day03() -> String {
-    let rucksacks = day03::parse(&fs::read_to_string("data/day03.input").unwrap());
-    format!("part1: {}, part2: {}", day03::priority_sum(&rucksacks), day03::badge_sum(&rucksacks))
-}
-
-fn day04() -> String {
-    let pairs = day04::parse(&fs::read_to_string("data/day04.input").unwrap());
-    format!("part1: {}, part2: {}", day04::number_fully_contain(&pairs), day04::number_overlaps(&pairs))
-}
-
-fn day05() -> String {
-    let (mut stacks, moves) = day05::parse(&fs::read_to_string("data/day05.input").unwrap());
-    let mut stacks2 = stacks.clone();
-    day05::apply_moves_by_crate_mover_9000(&mut stacks, &moves);
-    day05::apply_moves_by_crate_mover_9001(&mut stacks2, &moves);
-    format!("part1: {}, part2: {}", day05::get_top_as_string(&stacks), day05::get_top_as_string(&stacks2))
-}
-
-fn day06() -> String {
-    let signals = fs::read_to_string("data/day06.input").unwrap();
-    format!("part1: {}, part2: {}", day06::first_marker_pos(&signals, 4), day06::first_marker_pos(&signals, 14))
-}
-
-fn day07() -> String {
-    let root = day07::parse(&fs::read_to_string("data/day07.input").unwrap());
-
-    let (root_size, sum_part1, ) = {
-        let mut sizes: Vec<i64> = Vec::new();
-        (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::<i64>())
-    };
-
-    let min_part2 = {
-        let to_free = root_size - (70_000_000 - 30_000_000);
-        let mut sizes: Vec<i64> = Vec::new();
-        root.dir_sizes(|size| size >= to_free, &mut sizes);
-        *sizes.iter().min().unwrap()
-    };
-
-    format!("part1: {}, part2: {}", sum_part1, min_part2)
-}
-
-fn day08() -> String {
-    let forest = day08::parse(&fs::read_to_string("data/day08.input").unwrap());
-    format!("part1: {}, part2: {}", day08::number_of_visible_trees(&forest), day08::best_scenic_score(&forest))
-}
-
-fn day09() -> String {
-    let movements = day09::parse(&fs::read_to_string("data/day09.input").unwrap());
-    format!("part1: {}, part2: {}", day09::nb_positions_visited_by_tail::<2>(&movements), day09::nb_positions_visited_by_tail::<10>(&movements))
-}
-
-fn day10() -> String {
-    let instructions = day10::parse(&fs::read_to_string("data/day10.input").unwrap());
-    let mut screen = day10::Screen::new();
-    let sum_signal_strength = screen.draw_screen(&instructions);
-    format!("part1: {}, part2: \n{}", sum_signal_strength, screen.to_ascii())
-}
-
-fn day11() -> String {
-    let monkeys = day11::parse(&fs::read_to_string("data/day11.input").unwrap());
-    format!("part1: {}, part2: {}", day11::run(&mut monkeys.clone(), 20, 3), day11::run(&mut monkeys.clone(), 10000, 1))
-}
-
-fn format_micros(t: u128) -> String {
-    if t < 10_000 {
-        format!("{} μs", t)
-    } else if t < 10_000_000u128 {
-        format!("{} ms", t / 1_000u128)
-    } else {
-        format!("{} s", t / 1_000_000u128)
-    }
-}
-
-fn do_day(days: &[fn() -> String], day: usize) {
-    let now = Instant::now();
-    println!("Result of day {:02}: {} (time: {})", day, days[day - 1](), format_micros(now.elapsed().as_micros()));
-}
+mod day12;
 
 fn main() {
     println!("https://adventofcode.com/2022");
 
     let days: Vec<fn() -> String> = vec!(
-        day01,
-        day02,
-        day03,
-        day04,
-        day05,
-        day06,
-        day07,
-        day08,
-        day09,
-        day10,
-        day11,
+        days::day01,
+        days::day02,
+        days::day03,
+        days::day04,
+        days::day05,
+        days::day06,
+        days::day07,
+        days::day08,
+        days::day09,
+        days::day10,
+        days::day11,
+        days::day12,
     );
 
     let args: Vec<String> = env::args().skip(1).collect();
@@ -144,3 +55,18 @@ fn main() {
         }
     }
 }
+
+fn do_day(days: &[fn() -> String], day: usize) {
+    let now = Instant::now();
+    println!("Result of day {:02}: {} (time: {})", day, days[day - 1](), format_micros(now.elapsed().as_micros()));
+}
+
+fn format_micros(t: u128) -> String {
+    if t < 10_000 {
+        format!("{} μs", t)
+    } else if t < 10_000_000u128 {
+        format!("{} ms", t / 1_000u128)
+    } else {
+        format!("{} s", t / 1_000_000u128)
+    }
+}