From 7a55f0be923b253296b87e1ea36a33459df1bb3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 5 Feb 2026 09:58:16 +0100 Subject: [PATCH] Add solution for "Day 4: Printing Department", part 2 --- README.md | 4 + src/common/geometry.rs | 23 ++++++ src/solvers/printing_department.rs | 113 ++++++++++++++++++++--------- tests/examples.rs | 2 +- tests/full_data.rs | 2 +- 5 files changed, 106 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9ff3966..7c50e64 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ With a continuously updated tally of the current best joltage initialized at zer :mag_right: Puzzle: , :white_check_mark: Solver: [`PrintingDepartment`](src/solvers/printing_department.rs) +For part 1, we create a two-dimensional grid to track location and neighbor counts for each paper roll. Parsing the input data row by row, for each location we only have to check the four neighbors that were not yet encountered, i.e. right in the same row, and the three neighbors in the next row, to handle all pairs of locations. This means that we know whether a location is accessible or not immediately before moving on to the next. + +For part 2, the accessible locations are tracked, and removed individually from the grid. After each removal, the neighbor counts of the neighbors are updated. Any of the neighbors that now fall under the threshold are counted as well and added to the removal list. + ### Day 5: Cafeteria :mag_right: Puzzle: , :white_check_mark: Solver: [`Cafeteria`](src/solvers/cafeteria.rs) diff --git a/src/common/geometry.rs b/src/common/geometry.rs index ff3bd22..57fd8d2 100644 --- a/src/common/geometry.rs +++ b/src/common/geometry.rs @@ -1,9 +1,24 @@ +use std::ops; use std::str::FromStr; #[derive(Hash, PartialEq, Eq, Debug, Copy, Clone)] pub struct Point2(pub i64, pub i64); impl Point2 { + pub const EIGHT_POINT_DIRECTIONS: &'static [Point2] = &[ + Point2(-1, -1), + Point2(-1, 0), + Point2(-1, 1), + Point2(0, 1), + Point2(1, 1), + Point2(1, 0), + Point2(1, -1), + Point2(0, -1), + ]; + + pub const FORWARD_DOWN_DIRECTIONS: &'static [Point2] = + &[Point2(-1, 1), Point2(0, 1), Point2(1, 1), Point2(1, 0)]; + const POINT_SPLIT_CHAR: char = ','; pub fn rect_area(&self, other: &Self) -> u64 { @@ -24,6 +39,14 @@ impl FromStr for Point2 { } } +impl ops::Add<&Self> for Point2 { + type Output = Self; + + fn add(self, rhs: &Self) -> Self { + Self(self.0 + rhs.0, self.1 + rhs.1) + } +} + #[derive(Hash, PartialEq, Eq, Debug)] pub struct Point3(pub i64, pub i64, pub i64); diff --git a/src/solvers/printing_department.rs b/src/solvers/printing_department.rs index f9df120..8480850 100644 --- a/src/solvers/printing_department.rs +++ b/src/solvers/printing_department.rs @@ -1,3 +1,4 @@ +use crate::common::geometry::Point2; use crate::solvers::Solver; use grid::Grid; use std::io::BufRead; @@ -6,6 +7,78 @@ pub struct PrintingDepartment {} impl PrintingDepartment { const PAPER_ROLL_CHAR: char = '@'; + const MAX_NEIGHBORS_ACCESSIBLE: u8 = 3; + + fn init_paper_roll_grid(&self, reader: R) -> Grid> { + let mut grid = Grid::new(0, 0); + for line in reader.lines().map_while(Result::ok) { + grid.push_row( + line.bytes() + .map(|c| (c == Self::PAPER_ROLL_CHAR as u8).then_some(0_u8)) + .collect(), + ); + } + grid + } + + fn count_neighbors(&self, grid: &mut Grid>) -> Vec { + let mut removables = Vec::new(); + let mut location = Point2(0, 0); + // Loops over the grid. + for j in 0..grid.rows() { + location.1 = i64::try_from(j).unwrap(); + for i in 0..grid.cols() { + location.0 = i64::try_from(i).unwrap(); + let mut count = 0; + if grid.get(j, i).unwrap().is_some() { + // Loops only over the four neighbors that have not yet been encountered. This + // still ensures that we handle each pair of neighbors. + for direction in Point2::FORWARD_DOWN_DIRECTIONS { + if let Some(Some(other)) = + grid.get_mut(location.1 + direction.1, location.0 + direction.0) + { + *other += 1; + count += 1; + } + } + // We know that the cell is Some because of the indices of the loops, and we + // also know that the value in the cell is Some because of the enclosing "if". + let cell_count = grid.get_mut(j, i).unwrap().as_mut().unwrap(); + *cell_count += count; + if *cell_count <= Self::MAX_NEIGHBORS_ACCESSIBLE { + removables.push(location); + } + } + } + } + removables + } + + fn count_removable_paper_rolls( + &self, + mut grid: Grid>, + mut removables: Vec, + ) -> u64 { + // Considers everything in "removables" as already counted. + let mut result = removables.len() as u64; + while let Some(current) = removables.pop() { + // Updates the neighbors. + for direction in Point2::EIGHT_POINT_DIRECTIONS { + let neighbor = current + direction; + if let Some(Some(other)) = grid.get_mut(neighbor.1, neighbor.0) { + *other -= 1; + if *other == Self::MAX_NEIGHBORS_ACCESSIBLE { + result += 1; + removables.push(neighbor); + } + } + } + // Updates the current location. + let cell = grid.get_mut(current.1, current.0).unwrap(); + *cell = None; + } + result + } } impl Solver for PrintingDepartment { @@ -13,41 +86,9 @@ impl Solver for PrintingDepartment { const PUZZLE_NAME: &'static str = "Printing Department"; fn process_data(&self, reader: R) -> (u64, u64) { - let mut grid = Grid::new(0, 0); - for line in reader.lines().map_while(Result::ok) { - grid.push_row( - line.bytes() - .map(|c| c == Self::PAPER_ROLL_CHAR as u8) - .collect(), - ); - } - - let mut part1 = 0; - 'cells: for cell in grid.indexed_iter() { - if *cell.1 { - let mut count = 0; - // TODO: This could be a loop over eight directions instead. - for i in -1i32..=1 { - for j in -1i32..=1 { - if i != 0 || j != 0 { - if let Some(&blocked) = grid.get( - i32::try_from(cell.0.0).unwrap() + i, - i32::try_from(cell.0.1).unwrap() + j, - ) { - if blocked { - if count <= 2 { - count += 1; - } else { - continue 'cells; - } - } - } - } - } - } - part1 += 1; - } - } - (part1, 0) + let mut grid = self.init_paper_roll_grid(reader); + let removables = self.count_neighbors(&mut grid); + let part1 = removables.len() as u64; + (part1, self.count_removable_paper_rolls(grid, removables)) } } diff --git a/tests/examples.rs b/tests/examples.rs index 6636784..4b3febd 100644 --- a/tests/examples.rs +++ b/tests/examples.rs @@ -35,7 +35,7 @@ fn lobby() { #[test] fn printing_department() { assert_eq!( - Ok((13, 0)), + Ok((13, 43)), solvers::run_solver( solvers::printing_department::PrintingDepartment {}, &EXAMPLE_PATHS diff --git a/tests/full_data.rs b/tests/full_data.rs index 08b3d3f..fc23384 100644 --- a/tests/full_data.rs +++ b/tests/full_data.rs @@ -30,7 +30,7 @@ fn lobby() { #[test] fn printing_department() { assert_eq!( - Ok((1433, 0)), + Ok((1433, 8616)), solvers::run_solver( solvers::printing_department::PrintingDepartment {}, &solvers::DATA_PATHS