#![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); }