feat: Completed Dat 4
All checks were successful
Continuous integration / Check (push) Successful in 40s
Continuous integration / Test Suite (push) Successful in 41s
Continuous integration / Rustfmt (push) Successful in 27s
Continuous integration / Clippy (push) Successful in 40s
Continuous integration / build (push) Successful in 39s

This commit is contained in:
2025-12-07 21:41:40 +00:00
parent d03f97e641
commit 16b0f1960a
2 changed files with 111 additions and 6 deletions

View File

@@ -15,7 +15,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
Box::new(day01::Day01 {}),
Box::new(day02::Day02 {}),
Box::new(day03::Day03 {}),
// Box::new(day04::Day04 {}),
Box::new(day04::Day04 {}),
// Box::new(day05::Day05 {}),
// Box::new(day06::Day06 {}),
// Box::new(day07::Day07 {}),

View File

@@ -5,16 +5,54 @@ pub struct Day04 {}
impl Solution for Day04 {
fn part1(
&self,
_input: &mut Vec<String>,
input: &mut Vec<String>,
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
Ok(Box::new("Ready"))
// Convert to a 2D vector of chars
let grid = Grid::from_input(input);
let mut count = 0;
for r in 0..grid.h {
for c in 0..grid.w {
if grid.is_accessible(r, c) {
count += 1;
}
}
}
Ok(Box::new(count))
}
fn part2(
&self,
_input: &mut Vec<String>,
input: &mut Vec<String>,
) -> Result<Box<dyn std::fmt::Display>, Box<dyn std::error::Error>> {
Ok(Box::new("Ready"))
let mut grid = Grid::from_input(input);
let mut removed_total = 0;
loop {
let mut to_remove = Vec::new();
// Collect all accessible rolls in this pass.
for r in 0..grid.h {
for c in 0..grid.w {
if grid.is_accessible(r, c) {
to_remove.push((r, c));
}
}
}
// If none can be removed, we're done.
if to_remove.is_empty() {
break;
}
removed_total += to_remove.len();
// Remove them.
for (r, c) in to_remove {
grid.cells[r][c] = '.';
}
}
Ok(Box::new(removed_total))
}
fn get_day(&self) -> u8 {
@@ -24,6 +62,73 @@ impl Solution for Day04 {
impl Day04 {}
/// A 2D grid representing rolls of paper (`@`) and empty space (`.`).
///
/// The grid keeps the raw cell data plus cached height and width
/// to make neighbour checks simpler and avoid repeated recalculation.
#[derive(Clone)]
struct Grid {
cells: Vec<Vec<char>>,
h: usize,
w: usize,
}
impl Grid {
/// Constructs a `Grid` from raw puzzle input.
///
/// Each line becomes a row, and each character becomes a cell.
/// Assumes all lines are the same width (as per Advent of Code input).
fn from_input(s: &[String]) -> Self {
let cells: Vec<Vec<char>> = s.iter().map(|line| line.chars().collect()).collect();
let h = cells.len();
let w = cells[0].len();
Grid { cells, h, w }
}
/// Counts the number of adjacent `@` cells around the position `(r, c)`.
///
/// Neighbours are the 8 surrounding cells in the Moore neighbourhood:
/// diagonals plus orthogonals.
/// If `(r, c)` is on an edge, out-of-bounds neighbours are ignored.
fn count_adjacent(&self, r: usize, c: usize) -> usize {
let dirs = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1),
];
dirs.iter()
.filter(|(dr, dc)| {
let nr = r as isize + dr;
let nc = c as isize + dc;
nr >= 0
&& nr < self.h as isize
&& nc >= 0
&& nc < self.w as isize
&& self.cells[nr as usize][nc as usize] == '@'
})
.count()
}
/// Determines whether a roll at `(r, c)` is accessible to a forklift.
///
/// A roll is accessible if:
/// • the cell contains `@`, and
/// • fewer than 4 of the 8 surrounding cells contain `@`.
///
/// This logic is shared by Part 1 and Part 2.
fn is_accessible(&self, r: usize, c: usize) -> bool {
self.cells[r][c] == '@' && self.count_adjacent(r, c) < 4
}
}
/// Test from puzzle input
#[cfg(test)]
mod test {
@@ -61,6 +166,6 @@ mod test {
.unwrap()
.to_string();
assert_eq!(answer, "Ready");
assert_eq!(answer, "43");
}
}