From 92a66b3d5f2e1bdf22dedfcc12c9d950dcb7159f Mon Sep 17 00:00:00 2001 From: Quentin Boyer Date: Thu, 4 Dec 2025 23:56:53 +0100 Subject: [PATCH] Day 4 --- src/bin/day4.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/bin/day4.rs diff --git a/src/bin/day4.rs b/src/bin/day4.rs new file mode 100644 index 0000000..05a27d4 --- /dev/null +++ b/src/bin/day4.rs @@ -0,0 +1,157 @@ +use std::time::Instant; + +use aoc_2025::{load, print_res}; +use bstr::{BString, ByteSlice}; +use color_eyre::eyre::ContextCompat; + +type Parsed = Grid; + +pub struct Grid { + values: Vec, + line_len: usize, + col_len: usize, +} + +impl Grid { + fn get(&self, line: isize, col: isize) -> Option { + if line < 0 || col < 0 || line as usize >= self.line_len || col as usize >= self.col_len { + None + } else { + Some(self[(line as usize, col as usize)]) + } + } + + fn neighbours(&self, line: usize, col: usize) -> usize { + let mut sum = 0; + for l in -1..=1 { + for c in -1..=1 { + if l == 0 && c == 0 { + continue; + } + + if self + .get(line as isize + l, col as isize + c) + .unwrap_or(false) + { + sum += 1 + } + } + } + sum + } +} + +impl std::ops::Index<(usize, usize)> for Grid { + type Output = bool; + + fn index(&self, (line, col): (usize, usize)) -> &Self::Output { + &self.values[line * self.line_len + col] + } +} + +impl std::ops::IndexMut<(usize, usize)> for Grid { + fn index_mut(&mut self, (line, col): (usize, usize)) -> &mut Self::Output { + &mut self.values[line * self.line_len + col] + } +} + +impl std::fmt::Display for Grid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for line in self.values.chunks_exact(self.line_len) { + line.iter().try_for_each(|v| { + write!( + f, + "{}", + match v { + true => '@', + false => '.', + } + ) + })?; + writeln!(f)?; + } + + Ok(()) + } +} + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + let line_len = input.find(b"\n").with_context(|| "No newline found")?; + let values: Vec<_> = input + .iter() + .filter(|&&v| v != b'\n') + .map(|&v| v == b'@') + .collect(); + + assert_eq!( + values.len() % line_len, + 0, + "Line length does not divide the grid size" + ); + Ok(Grid { + line_len, + col_len: values.len() / line_len, + values, + }) +} + +#[inline(never)] +pub fn part1(input: Parsed) { + let mut count = 0; + + for line in 0..input.line_len { + for col in 0..input.line_len { + if input[(line, col)] && input.neighbours(line, col) < 4 { + count += 1; + } + } + } + + print_res!("Accessible roll count: {count}"); +} + +#[inline(never)] +pub fn part2(mut input: Parsed) { + let mut count = 0; + + loop { + let mut found = false; + for line in 0..input.line_len { + for col in 0..input.line_len { + if input[(line, col)] && input.neighbours(line, col) < 4 { + input[(line, col)] = false; + found = true; + count += 1; + } + } + } + + if !found { + break; + } + } + + print_res!("Total removed: {count}"); +} + +pub fn main() -> color_eyre::Result<()> { + let context = load()?; + + let start = Instant::now(); + let parsed = parsing(&context.input)?; + let elapsed = humantime::format_duration(start.elapsed()); + + let start = Instant::now(); + if context.part == 1 { + part1(parsed); + } else { + part2(parsed); + } + let elapsed_part = humantime::format_duration(start.elapsed()); + + println!(" Parsing: {elapsed}"); + println!(" Solving: {elapsed_part}"); + + Ok(()) +}