From: Greg Burri Date: Mon, 12 Dec 2022 09:30:23 +0000 (+0100) Subject: Day 12 X-Git-Url: http://git.euphorik.ch/index.cgi?a=commitdiff_plain;h=3f40e0659777ba355ad4da20ce5aaa6532541f6d;p=advent_of_code_2022.git Day 12 --- diff --git a/data/day12.input b/data/day12.input new file mode 100644 index 0000000..415cc54 --- /dev/null +++ b/data/day12.input @@ -0,0 +1,41 @@ +abaccccccccccccccaaaccccaaaaaaaaaaaaaccccccaacccccccccccccccccccccccccccccaaaaaa +abaaccaacccccccccaaaaaccccaaaaaaaaaaaaaccccaaacccccccccccccccccccccccccccccaaaaa +abaaccaaacccccccaaaaaacccaaaaaaaaaaaaaacaaaaaaaaccccccccaacccccccccccccccccccaaa +abcaaaaaaaacccccaaaaaacccaaaaaaaaaaaaaacaaaaaaaacccccccaaaacccccccccccccccccaaaa +abcaaaaaaaaccccccaaaaaccaaaaaaaaccaaaaaccaaaaaaccccccccaaaaccaaaccccccccccccaaac +abccaaaaaacccccccaaaaccaaaaaaaaaacaaaacccaaaaaacccccccccakkaaaaaacccccccccccaacc +abccaaaaaacccccccccccccaaaaaaaaaaccccccccaaaaaaccccccckkkkkkkaaacccccccccccccccc +abccaaaaaaccccccccccccccccaaaaaaaaaccccccaacaaacccccckkkkkkkkkaccccccaccaaaccccc +abccaacaaacccccaaccccccccaaacacaaaacaaccccccccccccccakkkoppkkkkicccccaaaaaaccccc +abccccccccccccaaaccccccccaacccccaaaaaaccccccccccccccjkkooppppkiicccccccaaaaccccc +abccccccccccaaaaaaaaccccccccccaaaaaaaccccccccccccccjjjooopppppiiiicccccccaaacccc +abaaacccccccaaaaaaaacccccccaacaaaaaaccccccccccccccjjjjooouuppppiiiiiicccccaacccc +abaaaccccccccaaaaaaccccccccaaaccaaaaacccccccccccjjjjjooouuuupppiiiiiiiiccccacccc +abaaaaaacccccaaaaaacccccaaaaaaaaaacaaaccccccccjjjjjjooouuuuuupppppiiiiiicccccccc +abaaaaaacccccaaaaaacccccaaaaaaaaaacccccccccccjjjjjooooouuxxuupppppqqqijjjccccccc +abaaaacccccaaaaccaaccccccaaaaaaccccccccccccciijjnooooouuuxxxuuupqqqqqqjjjdddcccc +abaaaaaccaaaaaaccacccccccaaaaaaccccccccccaaiiiinnootttuuxxxxuuvvvvvqqqjjjdddcccc +abaaaaaccaaaaaacaaaccaaccaaaaaaccccccccccaaiiinnnntttttuxxxxxvvvvvvqqqjjjdddcccc +abaaccacccaaaaacaaaaaaaccaaccaaccccccccccaaiiinnnttttxxxxxxxyyyyyvvqqqjjjdddcccc +abcccccccaaaaacccaaaaaaccccccaaaaacccccccaaiiinnntttxxxxxxxyyyyyvvvqqqjjjddccccc +SbcccccccaaaaacaaaaaaaaccccccaaaaaccccccccciiinnntttxxxEzzzzyyyyvvqqqjjjdddccccc +abcccccccccccccaaaaaaaaaccccaaaaaaccccccccciiinnnntttxxxxyyyyyvvvvqqjjjdddcccccc +abcccccccccccccaaaaaaaaaacccaaaaaacccccccccciiinnnttttxxxyyyyyvvvqqqjjjdddcccccc +abccccccccccccccccaaaaaaacccaaaaaaccccccccccciiinnnntttwyyywyyyvvrrrkkjdddcccccc +abcccccccccccccccaaaaaaaaccccaaaccccccccccccciiihnnnttwwwywwyyywvrrrkkkeeccccccc +abcccccccccccccccaaaaaaaaccccccccccccccccccccchhhmmmsswwwwwwwwwwwvrrkkkeeccccccc +abcccccccaacccccccacaaacccccccccccccccccccaacchhhhmmsswwwwwswwwwwrrrkkkeeccccccc +abcccccccaaaccacccccaaacccccccccccccccaaccaaccchhhmmssswwwssrrwwwrrrkkkeeccccccc +abcccccccaaaaaaacccccccccccaaaccccccccaaaaaaccchhhmmssssssssrrrrrrrrkkkeeaaacccc +abcccccaaaaaaaaccccccccccccaaaaccccccccaaaaaaachhhmmmssssssllrrrrrrkkkeeeaaacccc +abccccaaaaaaaaaccccccccccccaaaacccccccccaaaaacchhhmmmmsssllllllllkkkkkeeeaaacccc +abccccaaaaaaaaaccccccccccccaaacccccccccaaaaacccchhhmmmmmlllllllllkkkkeeeeaaccccc +abcccccccaaaaaaccccccccccaacccccccaaccaaacaacccchhhmmmmmlllgfflllkkffeeeaaaacccc +abccccccaaaaaaaccccccccccaaaaaaaaaaaaaccccaacccchhhggmmmggggffffffffffeaaaaacccc +abccaacccaacccaaaaccaccccaaaaaaaaaaaaacccccccccccgggggggggggffffffffffaacccccccc +abaaaaccaaaccccaaaaaaccccaaaaaacaaaaaaccccccccccccgggggggggaaaaccffccccccccccccc +abaaaacccccccccaaaaaaccaaaaaaaaaaaaaacccccccccccccccgggaaaaaaaacccccccccccccccca +abaaaaacccccccaaaaaaaccaaaaaaaaaaaaaacccccccccccccccccaaacccaaaaccccccccccccccaa +abaaaaacaaaaccaaaaaaaacaaaaaaaaaaaccccccccccccccccccccaaaccccaaaccccccccccaaacaa +abaaaaacaaaaccaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccccccccccaaaaaa +abaaacccaaaaccccaaaccccaaaaaaaaaaacccccccccccccccccccccccccccccccccccccccccaaaaa \ No newline at end of file diff --git a/src/day12.rs b/src/day12.rs new file mode 100644 index 0000000..1e03ed3 --- /dev/null +++ b/src/day12.rs @@ -0,0 +1,96 @@ +#[derive(Debug)] +pub struct Heightmap { + elevations: Vec>, + 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::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 index 0000000..ce0d798 --- /dev/null +++ b/src/days.rs @@ -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 = Vec::new(); + (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::()) + }; + + let min_part2 = { + let to_free = root_size - (70_000_000 - 30_000_000); + let mut sizes: Vec = 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)) +} diff --git a/src/main.rs b/src/main.rs index 0e63823..d9e8472 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 = Vec::new(); - (root.dir_sizes(|size| size <= 100_000, &mut sizes), sizes.iter().sum::()) - }; - - let min_part2 = { - let to_free = root_size - (70_000_000 - 30_000_000); - let mut sizes: Vec = 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 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 = 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) + } +}