diff --git a/Cargo.lock b/Cargo.lock index 158c74f..b62e131 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,7 @@ dependencies = [ "indoc", "nd_vec", "num-traits", + "pathfinding", "petgraph", "rayon", ] @@ -341,6 +342,18 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "deprecate-until" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec2e2a2d0c79afd023be50309ecf010f20bf4a2761f15e264f4ac9516aff58b" +dependencies = [ + "proc-macro2", + "quote", + "semver", + "syn 2.0.39", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -575,6 +588,15 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -732,6 +754,21 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "pathfinding" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a373a9925b8361b790cf53bc286dbc87736335427537ff438f53445eb41a526d" +dependencies = [ + "deprecate-until", + "fixedbitset", + "indexmap", + "integer-sqrt", + "num-traits", + "rustc-hash", + "thiserror", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -960,6 +997,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1076,9 +1119,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" @@ -1204,6 +1247,26 @@ dependencies = [ "utf-8", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/aoc_2022/Cargo.toml b/aoc_2022/Cargo.toml index 4e36d3f..dadddbf 100644 --- a/aoc_2022/Cargo.toml +++ b/aoc_2022/Cargo.toml @@ -10,5 +10,6 @@ hashbrown = "0.13.1" indoc = "2.0.4" nd_vec = "0.3.0" num-traits = "0.2.15" +pathfinding = "4.3.3" petgraph = "0.6.2" rayon = "1.6.1" diff --git a/aoc_2022/src/day_24.rs b/aoc_2022/src/day_24.rs index 6998203..b5e48b9 100644 --- a/aoc_2022/src/day_24.rs +++ b/aoc_2022/src/day_24.rs @@ -1,5 +1,7 @@ use common::{Answer, Solution}; +use hashbrown::HashSet; use nd_vec::{vector, Vec2}; +use pathfinding::directed::bfs::bfs; type Pos = Vec2; @@ -12,7 +14,23 @@ impl Solution for Day24 { fn part_a(&self, input: &str) -> Answer { let basin = Basin::parse(input); - Answer::Unimplemented + let end = basin.end(); + let states = basin.all_states(); + + let path = bfs( + &(vector!(1, 0), 0), + move |(pos, idx)| { + states[(idx + 1) % states.len()] + .available(*pos) + .iter() + .map(move |x| (*x, *idx)) + .collect::>() + }, + |(pos, _)| *pos == end, + ) + .unwrap(); + + path.len().into() } fn part_b(&self, _input: &str) -> Answer { @@ -28,17 +46,17 @@ enum Direction { } // [up, down, left, right] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] struct Blizzard(u8); -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum Tile { Empty, Wall, Blizzard(Blizzard), } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Basin { tiles: Vec>, size: Pos, @@ -58,6 +76,34 @@ impl Basin { } } + fn available(&self, pos: Pos) -> Vec { + let mut out = Vec::new(); + let [x, y] = [pos.x(), pos.y()]; + let [sx, sy] = [self.size.x(), self.size.y()]; + + if self.tiles[y][x] == Tile::Empty { + out.push(vector!(x, y)); + } + + if x >= 1 && self.tiles[y][x - 1] == Tile::Empty { + out.push(vector!(x - 1, y)); + } + + if x + 1 < sx && self.tiles[y][x + 1] == Tile::Empty { + out.push(vector!(x + 1, y)); + } + + if y + 1 < sy && self.tiles[y + 1][x] == Tile::Empty { + out.push(vector!(x, y + 1)); + } + + if y >= 1 && self.tiles[y - 1][x] == Tile::Empty { + out.push(vector!(x, y - 1)); + } + + out + } + fn tick(&self) -> Self { let mut tiles = vec![vec![Tile::Empty; self.size.x()]; self.size.y()]; @@ -73,7 +119,7 @@ impl Basin { let [nx, ny] = [new_pos.x(), new_pos.y()]; if self.tiles[ny][nx] == Tile::Wall { - let [sx, sy] = [self.size.y() - 2, self.size.x() - 2]; + let [sx, sy] = [self.size.x() - 2, self.size.y() - 2]; new_pos = match dir { Direction::Up => vector!(nx, sy), Direction::Down => vector!(nx, 1), @@ -100,6 +146,18 @@ impl Basin { Self { tiles, ..*self } } + fn all_states(mut self) -> Vec { + let mut seen = HashSet::new(); + let mut out = Vec::new(); + + while seen.insert(self.clone()) { + out.push(self.clone()); + self = self.tick(); + } + + out + } + fn end(&self) -> Pos { vector!(self.size.y() - 1, self.size.x() - 2) }