From c48a7913f56eeb17cc230438ee7cc6c5da35b5b9 Mon Sep 17 00:00:00 2001 From: Patrick Auernig Date: Wed, 14 Dec 2022 19:01:37 +0100 Subject: [PATCH] Add solution for 2022 day 12 --- 2022/Cargo.lock | 46 ++++++++++++++ 2022/Cargo.toml | 1 + 2022/README.md | 2 +- 2022/day-12/Cargo.toml | 14 ++++ 2022/day-12/Justfile | 5 ++ 2022/day-12/inputs/puzzle.txt | 41 ++++++++++++ 2022/day-12/inputs/test.txt | 5 ++ 2022/day-12/src/bin/part_one.rs | 41 ++++++++++++ 2022/day-12/src/bin/part_two.rs | 54 ++++++++++++++++ 2022/day-12/src/lib.rs | 109 ++++++++++++++++++++++++++++++++ README.md | 2 +- 11 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 2022/day-12/Cargo.toml create mode 100644 2022/day-12/Justfile create mode 100644 2022/day-12/inputs/puzzle.txt create mode 100644 2022/day-12/inputs/test.txt create mode 100644 2022/day-12/src/bin/part_one.rs create mode 100644 2022/day-12/src/bin/part_two.rs create mode 100644 2022/day-12/src/lib.rs diff --git a/2022/Cargo.lock b/2022/Cargo.lock index c13379a..c3ef457 100644 --- a/2022/Cargo.lock +++ b/2022/Cargo.lock @@ -55,12 +55,48 @@ dependencies = [ "itertools", ] +[[package]] +name = "aoc-2022-12" +version = "0.1.0" +dependencies = [ + "itertools", + "petgraph", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "either" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "itertools" version = "0.10.5" @@ -69,3 +105,13 @@ checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] + +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] diff --git a/2022/Cargo.toml b/2022/Cargo.toml index e8d12cf..28bd8c9 100644 --- a/2022/Cargo.toml +++ b/2022/Cargo.toml @@ -11,6 +11,7 @@ members = [ "./day-09", "./day-10", "./day-11", + "./day-12", ] diff --git a/2022/README.md b/2022/README.md index ce3757e..5b02890 100644 --- a/2022/README.md +++ b/2022/README.md @@ -15,7 +15,7 @@ | [09] | ✓ | ✓ | [Rust] | | [10] | ✓ | ✓ | [Rust] | | [11] | ✓ | | [Rust] | -| [12] | | | | +| [12] | ✓ | ✓ | [Rust] | | [13] | | | | | [14] | | | | | [15] | | | | diff --git a/2022/day-12/Cargo.toml b/2022/day-12/Cargo.toml new file mode 100644 index 0000000..390de1e --- /dev/null +++ b/2022/day-12/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "aoc-2022-12" +version = "0.1.0" +edition = "2021" + + +[lib] +test = false +doctest = false + + +[dependencies] +petgraph = "0.6.2" +itertools = "0.10" diff --git a/2022/day-12/Justfile b/2022/day-12/Justfile new file mode 100644 index 0000000..7d08527 --- /dev/null +++ b/2022/day-12/Justfile @@ -0,0 +1,5 @@ +@part PART INPUT_FILE="inputs/puzzle.txt": + cargo --quiet run --bin part_{{PART}} -- {{INPUT_FILE}} + +clean: + cargo clean diff --git a/2022/day-12/inputs/puzzle.txt b/2022/day-12/inputs/puzzle.txt new file mode 100644 index 0000000..19c1d62 --- /dev/null +++ b/2022/day-12/inputs/puzzle.txt @@ -0,0 +1,41 @@ +abcccccccccaaaaaaaaaaccccccccccccaaaaaaaaccaaccccccccccccccccccccccccccccccccccccccccccccaaaaaa +abccccccccccaaaaaaaaaccccccccccccaaaaaaaaaaaacccccccccccaacccacccccccccccccccccccccccccccaaaaaa +abcccccccccccaaaaaaacccccccccccccaaaaaaaaaaaaaacccccccccaaacaacccccccccaaaccccccccccccccccaaaaa +abccccccccccaaaaaaccccccccccccccaaaaaaaaaaaaaaaccccccccccaaaaaccccccccccaaacccccccccccccccccaaa +abccccccccccaaaaaaaccccccccccccaaaaaaaaaaaaaacccccccccccaaaaaacccccccccaaaacccccccccccccccccaac +abaaccaaccccaaccaaaccccccccaaaaaaaaaaaaaaacaaccccccccccaaaaaaaacccccccccaaalcccccccccccccccaaac +abaaaaaacccccccccaaccccccccaaaaaacccaaaacccaaccccccccccaaaaaaaaccccccccalllllllcccccccccccccccc +abaaaaaacccccccaaacccccccccaaaaccccccaaaccccaaaaacccccccccaacccccccaaaakllllllllcccccccaacccccc +abaaaaaacccccccaaaacccccccccaacccccccaaaccccaaaaacccccccccaacccccccaakkklllpllllccccacaaacccccc +abaaaaaaaccccccaaaaccccaaccccccccccccccccccaaaaaaccccccccccccccccccckkkkpppppplllcccaaaaaaacccc +abaaaaaaacaaaccaaaaccaaaaaaccccccccccccccccaaaaaacccccccaaaccccckkkkkkkpppppppplllcddaaaaaacccc +abcaaaacccaacccccccccaaaaaacccccaaaccccccccaaaaaacccccccaaaaccjkkkkkkkpppppuppplmmdddddaaaccccc +abccaaaaaaaaaccccccccaaaaaaccccaaaaaacccccccaaacccccccccaaaajjjkkkkkrpppuuuuupppmmmdddddacccccc +abccccaaaaaaaacccccccaaaaacccccaaaaaacccccccccccccccccccaaacjjjjrrrrrrppuuuuupqqmmmmmddddaccccc +abccccaaaaaaaaacccccccaaaacccccaaaaaaccccccccccccccccccccccjjjrrrrrrrrpuuuxuvvqqqmmmmmddddccccc +abccccaaaaaaaaacccccccccccccccccaaaaaccccaacccaccccccccaaccjjjrrrruuuuuuuxxyvvqqqqqmmmmmdddcccc +abccccaaaaaaaacccccccccaaaccccccaacaaccccaaacaacccaaacaaaccjjjrrrtuuuuuuuxxyvvvqqqqqmmmmdddcccc +abccaaaaaaaacccccccccccaaaaaccccccccccccccaaaaacccaaaaaaaccjjjrrttttxxxxxxyyvvvvvqqqqmmmmdeeccc +abccaaaccaaaccccccccaacaaaaacccccccccccccaaaaaacccaaaaaacccjjjrrtttxxxxxxxyyvvvvvvvqqqmmmeeeccc +abaaaaaaaaaacccaaaccaaaaaaaaaaaccaaaccccaaaaaaaacccaaaaaaaajjjqqrttxxxxxxxyyyyyyvvvqqqnnneeeccc +SbaaaaaaaaccccaaaaccaaaaaaaaaaaaaaaaacccaaaaaaaaccaaaaaaaaacjjjqqtttxxxxEzzyyyyvvvvqqqnnneeeccc +abcaaaaaacccccaaaaccccaaaaaaaccaaaaaaccccccaaccccaaaaaaaaaaciiiqqqtttxxxyyyyyyvvvvrrrnnneeecccc +abcaaaaaacccccaaaacccaaaaaaaaccaaaaaaccccccaaccccaaacaaacccciiiqqqqttxxyyyyyywvvvrrrnnneeeecccc +abcaaaaaaccccccccccccaaaaaaaaacaaaaacccccccccccccccccaaaccccciiiqqtttxxyyyyyywwrrrrnnnneeeccccc +abcaaacaacccccaacccccaaaaaaaaacaaaaacccccccccccccccccaaaccccciiiqqttxxxywwyyywwrrrnnnneeecccccc +abccccccccaaacaaccccccccccacccccccccccccccccccccccccccccccccciiqqqttxxwwwwwwywwrrrnnneeeccccccc +abccaacccccaaaaaccccccccccccccccccccccccccccccccccccccccaacaaiiqqqttwwwwsswwwwwrrrnnfffeccccccc +abaaaaccccccaaaaaacccccccccccccccccccccccccccccaaaccccccaaaaaiiqqqttssssssswwwwrrronfffaccccccc +abaaaaaacccaaaaaaacccccccccccccccccccccccccccaaaaaacccccaaaaaiiqqqssssssssssswrrrooofffaaaacccc +abaaaaaaccaaaaaacccccccccccccccccccccccccccccaaaaaacccccaaaaaiiqqqppssspppssssrrrooofffaaaacccc +abaaaaaaccaacaaacccccccccccccccccccccccccccccaaaaaacccccaaaaaiihpppppppppppossrrooofffaaaaacccc +abaaaaccccccccaacccccccccccccccccccccccccccccaaaaaccccccccaaahhhhppppppppppoooooooofffaaaaccccc +abaaaaccccccccccaacccccccccccccccccaaacccccccaaaaacccccccccccchhhhhhhhhhggpoooooooffffaaaaccccc +abccaacccccccacaaaccccccccccccccccaaaaacccccccccccccccccccccccchhhhhhhhhggggoooooffffaacaaacccc +abccccccccccaaaaacaaccccccccccccccaaaaaccccccccccccccccccccccccchhhhhhhhggggggggggffcaacccccccc +abccccccccccaaaaaaaaccccccccccccccaaaacccaacccccccccccaccccccccccccccaaaaaggggggggfcccccccccccc +abccccccccccccaaaaaccccaacccccccccaaaacaaaaccccccccaaaaccccccccccccccaaaacaaagggggcccccccccaccc +abcccccccccccaaaaacccccaacccccccccaaaaaaaaaccccccccaaaaaaccccccccccccaaaccaaaacccccccccccccaaac +abcccccccccccaacaaccaaaaaaaacccaaaaaaaaaaaccccccccccaaaaccccccccccccccaccccaaacccccccccccccaaaa +abccccccccccccccaaccaaaaaaaaccaaaaaaaaaaaccccccccccaaaaacccccccccccccccccccccacccccccccccccaaaa +abccccccccccccccccccccaaaaacccaaaaaaaaaaaacccccccccaacaacccccccccccccccccccccccccccccccccaaaaaa \ No newline at end of file diff --git a/2022/day-12/inputs/test.txt b/2022/day-12/inputs/test.txt new file mode 100644 index 0000000..433e0d2 --- /dev/null +++ b/2022/day-12/inputs/test.txt @@ -0,0 +1,5 @@ +Sabqponm +abcryxxl +accszExk +acctuvwj +abdefghi \ No newline at end of file diff --git a/2022/day-12/src/bin/part_one.rs b/2022/day-12/src/bin/part_one.rs new file mode 100644 index 0000000..6767cea --- /dev/null +++ b/2022/day-12/src/bin/part_one.rs @@ -0,0 +1,41 @@ +use std::{env, io}; + +use aoc_2022_12::{parse_input, Graph}; +use petgraph::algo::dijkstra; + +fn main() -> io::Result<()> { + let infile_path = env::args().nth(1).expect("input file"); + + let result = solve(&infile_path)?; + + println!("{result}"); + + Ok(()) +} + +fn solve(path: &str) -> io::Result { + let (start, end, points) = parse_input(path)?; + + let graph = Graph::from_edges(points); + + let result = dijkstra(&graph, start, Some(end), |_| 1); + + Ok(result[&end]) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let result = solve("inputs/test.txt").unwrap(); + assert_eq!(31, result) + } + + #[test] + fn puzzle() { + let result = solve("inputs/puzzle.txt").unwrap(); + assert_eq!(420, result); + } +} diff --git a/2022/day-12/src/bin/part_two.rs b/2022/day-12/src/bin/part_two.rs new file mode 100644 index 0000000..1cd2be0 --- /dev/null +++ b/2022/day-12/src/bin/part_two.rs @@ -0,0 +1,54 @@ +use std::{env, io}; + +use aoc_2022_12::{parse_input, Graph}; +use itertools::Itertools; +use petgraph::algo::dijkstra; + +fn main() -> io::Result<()> { + let infile_path = env::args().nth(1).expect("input file"); + + let result = solve(&infile_path)?; + + println!("{result}"); + + Ok(()) +} + +fn solve(path: &str) -> io::Result { + let (_, end, points) = parse_input(path)?; + + let potential_starting_points = points + .iter() + .filter_map(|(a, _)| if a.2 == 1 { Some(*a) } else { None }) + .unique() + .collect::>(); + + let graph = Graph::from_edges(points); + let result = potential_starting_points + .into_iter() + .filter_map(|start| { + let result = dijkstra(&graph, start, Some(end), |_| 1); + result.get(&end).copied() + }) + .min() + .unwrap(); + + Ok(result) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sample() { + let result = solve("inputs/test.txt").unwrap(); + assert_eq!(29, result) + } + + #[test] + fn puzzle() { + let result = solve("inputs/puzzle.txt").unwrap(); + assert_eq!(414, result); + } +} diff --git a/2022/day-12/src/lib.rs b/2022/day-12/src/lib.rs new file mode 100644 index 0000000..f05d8e8 --- /dev/null +++ b/2022/day-12/src/lib.rs @@ -0,0 +1,109 @@ +use std::{ + collections::HashMap, + fs::File, + io::{self, BufRead, BufReader}, + iter, + path::Path, +}; + +use petgraph::prelude::DiGraphMap; + +pub type Graph = DiGraphMap; +type PointWithHeight = (i32, i32, i32); + +pub struct HeightMap { + items: HashMap<(i32, i32), i32>, + width: i32, + height: i32, +} + +impl HeightMap { + pub fn new() -> Self { + Self { + items: HashMap::new(), + width: 0, + height: 0, + } + } +} + +pub fn parse_input

( + path: P, +) -> io::Result<( + PointWithHeight, + PointWithHeight, + Vec<(PointWithHeight, PointWithHeight)>, +)> +where + P: AsRef, +{ + let file = File::open(path)?; + let mut height_map = HeightMap::new(); + let mut start_point = None; + let mut end_point = None; + + for (line_idx, line) in BufReader::new(file).lines().enumerate() { + let line = line?; + + for (cell_idx, cell) in line.chars().enumerate() { + height_map.width = (cell_idx + 1) as i32; + + let point = (line_idx as i32, cell_idx as i32); + + let height = if cell == 'S' { + start_point = Some((point.0, point.1, 1)); + 1 + } else if cell == 'E' { + end_point = Some((point.0, point.1, 26)); + 26 + } else { + (cell as u8 - 96).into() + }; + + height_map.items.insert(point, height); + } + + height_map.height += 1 + } + + let edges = build_edges(&height_map); + let start_point = start_point.expect("no start point"); + let end_point = end_point.expect("no end point"); + + Ok((start_point, end_point, edges)) +} + +fn build_edges(map: &HeightMap) -> Vec<(PointWithHeight, PointWithHeight)> { + map.items + .keys() + .flat_map(|point @ (x, y)| { + let neighbors = + [(-1, 0), (0, 1), (1, 0), (0, -1)] + .iter() + .filter_map(move |(nx, ny)| { + let dx = x + nx; + let dy = y + ny; + + let inside_bounds = + (0..map.height).contains(&dx) && (0..map.width).contains(&dy); + + if inside_bounds { + Some((dx, dy)) + } else { + None + } + }); + + iter::repeat(point).zip(neighbors).filter_map(|(a, b)| { + let height_self = map.items[&(a.0, a.1)]; + let height_neighbor = map.items[&(b.0, b.1)]; + + if height_self + 1 >= height_neighbor { + Some(((a.0, a.1, height_self), (b.0, b.1, height_neighbor))) + } else { + None + } + }) + }) + .collect() +} diff --git a/README.md b/README.md index 04f0986..1520508 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,4 @@ - [2019](2019/README.md) (0% completed) - [2020](2020/README.md) (20% completed) - [2021](2021/README.md) (68% completed) -- [2022](2022/README.md) (42% completed) +- [2022](2022/README.md) (46% completed)