From 0ff3b02db96429054acb7aca7339669e615b9338 Mon Sep 17 00:00:00 2001 From: Jan-Bulthuis Date: Thu, 4 Dec 2025 09:20:37 +0100 Subject: [PATCH] feat: Day 4 --- aoc_2025/src/day4.rs | 155 +++++++++++++++++++++++++++++++++++++++++++ aoc_2025/src/lib.rs | 2 + 2 files changed, 157 insertions(+) create mode 100644 aoc_2025/src/day4.rs diff --git a/aoc_2025/src/day4.rs b/aoc_2025/src/day4.rs new file mode 100644 index 0000000..07a08b8 --- /dev/null +++ b/aoc_2025/src/day4.rs @@ -0,0 +1,155 @@ +use winnow::{ + Parser, Result, + ascii::{multispace0, newline}, + combinator::{alt, preceded, repeat, terminated}, +}; + +use crate::{AoC, SimpleDay, WinnowDay}; + +fn day() -> impl WinnowDay { + AoC::new(parse as _, part1 as _, part2 as _) +} + +type Parsed = Vec>; + +#[derive(Debug, PartialEq, Eq)] +enum Cell { + Empty, + Paper, +} + +impl Cell { + fn is_empty(&self) -> bool { + self == &Cell::Empty + } + + fn is_paper(&self) -> bool { + self == &Cell::Paper + } +} + +fn parse(input: &mut &str) -> Result { + repeat(1.., preceded(multispace0, parse_line)).parse_next(input) +} + +fn parse_line(input: &mut &str) -> Result> { + repeat(1.., parse_cell).parse_next(input) +} + +fn parse_cell(input: &mut &str) -> Result { + alt((".".map(|_| Cell::Empty), "@".map(|_| Cell::Paper))).parse_next(input) +} + +fn part1(input: Parsed) -> usize { + let mut input = input; + remove_paper(&mut input) +} + +fn remove_paper(input: &mut [Vec]) -> usize { + let mut score = 0; + let mut access = Vec::new(); + let h = input.len(); + let w = input[0].len(); + let directions = [ + (-1, -1), + (0, -1), + (1, -1), + (1, 0), + (1, 1), + (0, 1), + (-1, 1), + (-1, 0), + ]; + for y in 0..h { + for x in 0..w { + match input[y][x] { + Cell::Empty => {} + Cell::Paper => { + let count = directions + .iter() + .filter(|dir| { + let nx = x as isize + dir.0; + let ny = y as isize + dir.1; + nx >= 0 + && nx < w as isize + && ny >= 0 + && ny < h as isize + && input[ny as usize][nx as usize].is_paper() + }) + .count(); + if count < 4 { + score += 1; + access.push((x, y)); + } + } + } + } + } + access.iter().for_each(|(x, y)| { + input[*y][*x] = Cell::Empty; + }); + score +} + +fn part2(input: Parsed) -> usize { + let mut input = input; + let mut score = 0; + loop { + let removed = remove_paper(&mut input); + score += removed; + if removed == 0 { + return score; + } + } +} + +#[cfg(test)] +mod tests { + use crate::Day; + + use super::*; + + #[test] + fn part1_test() { + day().assert1( + "..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@.", + "13", + ); + } + + #[test] + fn part1_solve() { + day().assert1(include_str!("../input/day4.txt"), "1356"); + } + + #[test] + fn part2_test() { + day().assert2( + "..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@.", + "43", + ); + } + + #[test] + fn part2_solve() { + day().assert2(include_str!("../input/day4.txt"), "8713"); + } +} diff --git a/aoc_2025/src/lib.rs b/aoc_2025/src/lib.rs index 1629ae8..6f63d38 100644 --- a/aoc_2025/src/lib.rs +++ b/aoc_2025/src/lib.rs @@ -1,4 +1,5 @@ #![allow(dead_code)] +#![allow(unused)] use std::fmt::{Debug, Display}; @@ -7,6 +8,7 @@ use winnow::Parser; mod day1; mod day2; mod day3; +mod day4; trait Day { fn assert1(&self, input: &str, output: &str);