diff --git a/2022/day18/rust/Cargo.toml b/2022/day18/rust/Cargo.toml new file mode 100644 index 0000000..e06179c --- /dev/null +++ b/2022/day18/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rust_2022_18" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aoc = { path = "../../../common/rust" } +petgraph = "0.6.2" +strum = "0.24.1" diff --git a/2022/day18/rust/src/main.rs b/2022/day18/rust/src/main.rs new file mode 100644 index 0000000..e0d72ce --- /dev/null +++ b/2022/day18/rust/src/main.rs @@ -0,0 +1,155 @@ +#![warn(clippy::pedantic)] +#![feature(generators)] +#![feature(iter_from_generator)] +#![feature(let_chains)] + +use petgraph::{algo::tarjan_scc, prelude::GraphMap, Undirected}; +use std::{io::stdin, iter}; +use strum::IntoEnumIterator; + +use aoc::vecn::{Direction3, VecN}; + +type Position = VecN<3, u64>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct Face { + position: Position, + side: Direction3, +} + +impl Face { + fn facing(self) -> Option { + let pos = self.position.try_map(i64::try_from).ok()? + self.side.into(); + + pos.try_map(u64::try_from).ok() + } + + fn inverse(self) -> Option { + Some(Face { + position: self.facing()?, + side: -self.side, + }) + } + + fn neighbours(self) -> impl Iterator { + iter::from_generator(move || { + let orthogonal_sides = + Direction3::iter().filter(move |&side| side != self.side && side != -self.side); + + for side in orthogonal_sides.clone() { + yield ( + Angle::Convex, + Face { + position: self.position, + side, + }, + ); + } + + if let Some(facing) = self.facing() { + for side in orthogonal_sides.clone() { + if let Some(inverse) = (Face { + position: facing, + side, + }) + .inverse() + { + yield (Angle::Concave, inverse); + } + } + } + + for side in orthogonal_sides { + if let Some(facing) = (Face { + position: self.position, + side, + }) + .facing() + { + yield ( + Angle::Flat, + Face { + position: facing, + side: self.side, + }, + ); + } + } + }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Angle { + Concave, + Convex, + Flat, +} + +fn main() { + let cubes = stdin().lines().map(|l| l.unwrap().parse().unwrap()); + let faces = + cubes.flat_map(|position| Direction3::iter().map(move |side| Face { position, side })); + + let mut graph = GraphMap::::new(); + for face in faces { + if let Some(inverse) = face.inverse() { + if graph.remove_node(inverse) { + continue; + } + } + + graph.add_node(face); + + for (angle, neighbour_face) in face.neighbours() { + if graph.contains_node(neighbour_face) { + graph.add_edge(face, neighbour_face, angle); + + if let Angle::Concave = angle { + graph.remove_edge( + face, + Face { + position: face.position, + side: -neighbour_face.side, + }, + ); + + graph.remove_edge( + neighbour_face, + Face { + position: neighbour_face.position, + side: -face.side, + }, + ); + } + } + } + } + + println!("{:?}", graph.node_count()); + + let sum_outside = tarjan_scc(&graph) + .into_iter() + .filter(|surface| { + graph + .all_edges() + .filter_map(|(start, end, angle)| { + if surface.contains(&start) || surface.contains(&end) { + Some(angle) + } else { + None + } + }) + .map(|angle| match angle { + Angle::Concave => -1, + Angle::Convex => 1, + Angle::Flat => 0, + }) + .sum::() + > 0 + }) + .map(|surface| surface.len()) + .max() + .unwrap(); + println!("{:?}", sum_outside); +} diff --git a/Cargo.lock b/Cargo.lock index 9adcd0c..f4aa06b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,6 +619,15 @@ dependencies = [ "aoc", ] +[[package]] +name = "rust_2022_18" +version = "0.1.0" +dependencies = [ + "aoc", + "petgraph", + "strum", +] + [[package]] name = "rustversion" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index ea046e5..b5da599 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,4 +25,5 @@ members = [ "2022/day13/rust", "2022/day14/rust", "2022/day15/rust", + "2022/day18/rust", ]