1
0

Add solution for "Day 4: Printing Department", part 2

This commit is contained in:
Stefan Müller
2026-02-05 09:58:16 +01:00
parent b746cad9ae
commit 7a55f0be92
5 changed files with 106 additions and 38 deletions
+4
View File
@@ -34,6 +34,10 @@ With a continuously updated tally of the current best joltage initialized at zer
:mag_right: Puzzle: <https://adventofcode.com/2025/day/4>, :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: <https://adventofcode.com/2025/day/5>, :white_check_mark: Solver: [`Cafeteria`](src/solvers/cafeteria.rs)
+23
View File
@@ -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);
+77 -36
View File
@@ -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<R: BufRead>(&self, reader: R) -> Grid<Option<u8>> {
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<Option<u8>>) -> Vec<Point2> {
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<Option<u8>>,
mut removables: Vec<Point2>,
) -> 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<R: BufRead>(&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))
}
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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