From d8780f97a770eb14d263d3a6849a0992995ae7b4 Mon Sep 17 00:00:00 2001 From: SebastianStork Date: Thu, 4 Dec 2025 14:43:03 +0100 Subject: [PATCH] Solve 2025 day 4 in rust --- 2025/rust/day-04/Cargo.lock | 7 ++ 2025/rust/day-04/Cargo.toml | 6 ++ 2025/rust/day-04/src/main.rs | 166 +++++++++++++++++++++++++++++++++++ flake.lock | 6 +- 4 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 2025/rust/day-04/Cargo.lock create mode 100644 2025/rust/day-04/Cargo.toml create mode 100644 2025/rust/day-04/src/main.rs diff --git a/2025/rust/day-04/Cargo.lock b/2025/rust/day-04/Cargo.lock new file mode 100644 index 0000000..bc5d859 --- /dev/null +++ b/2025/rust/day-04/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "day-04" +version = "0.1.0" diff --git a/2025/rust/day-04/Cargo.toml b/2025/rust/day-04/Cargo.toml new file mode 100644 index 0000000..ca7eca6 --- /dev/null +++ b/2025/rust/day-04/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day-04" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/2025/rust/day-04/src/main.rs b/2025/rust/day-04/src/main.rs new file mode 100644 index 0000000..9b5a3a8 --- /dev/null +++ b/2025/rust/day-04/src/main.rs @@ -0,0 +1,166 @@ +use std::{cell::RefCell, fs, rc::Rc}; + +struct Roll { + neighbours: Vec>>, + deleted: bool, +} + +impl Roll { + fn new() -> Rc> { + Rc::new(RefCell::new(Roll { + neighbours: Vec::new(), + deleted: false, + })) + } + + fn add_neighbour(this: &Rc>, neighbour: &Rc>) { + this.borrow_mut().neighbours.push(Rc::clone(neighbour)); + } + + fn is_accessible(&self) -> bool { + self.neighbours + .iter() + .filter(|neighbour| !neighbour.borrow().deleted) + .count() + < 4 + } + + fn delete(this: &Rc>) { + if this.borrow().deleted { + return; + } + + this.borrow_mut().deleted = true; + + this.borrow() + .neighbours + .iter() + .filter(|neighbour| !neighbour.borrow().deleted) + .filter(|neighbour| neighbour.borrow().is_accessible()) + .for_each(Roll::delete); + } +} + +fn main() { + let grid = parse_input(&fs::read_to_string("../../inputs/04.txt").unwrap()); + let rolls = build_graph(grid); + + println!( + "Number of accessible rolls: {}", + number_of_accessible_rolls(&rolls) + ); + println!( + "Number of removeable rolls: {}", + number_of_removeable_rolls(&rolls) + ); +} + +fn parse_input(contents: &str) -> Vec>>>> { + contents + .trim() + .lines() + .map(|line: &str| { + line.bytes() + .map(|b| if b == b'@' { Some(Roll::new()) } else { None }) + .collect() + }) + .collect() +} + +fn build_graph(grid: Vec>>>>) -> Vec>> { + for (row_index, row) in grid.iter().enumerate() { + for (col_index, cell) in row.iter().enumerate() { + if let Some(roll) = cell { + for neighbour in get_neighbours(&grid, row_index, col_index) { + Roll::add_neighbour(roll, neighbour); + } + } + } + } + + grid.into_iter().flatten().flatten().collect() +} + +fn get_neighbours( + grid: &[Vec>>>], + row: usize, + col: usize, +) -> Vec<&Rc>> { + let offsets = [ + (-1, -1), + (-1, 0), + (-1, 1), + (0, -1), + (0, 1), + (1, -1), + (1, 0), + (1, 1), + ]; + + offsets + .iter() + .filter_map(|(di, dj)| { + let (ni, nj) = (row as i32 + di, col as i32 + dj); + grid.get(ni as usize)?.get(nj as usize)?.as_ref() + }) + .collect() +} + +fn number_of_accessible_rolls(rolls: &[Rc>]) -> usize { + rolls + .iter() + .filter(|roll| roll.borrow().is_accessible()) + .count() +} + +fn number_of_removeable_rolls(rolls: &[Rc>]) -> usize { + loop { + let deleted_any = rolls + .iter() + .filter(|roll| !roll.borrow().deleted) + .filter(|roll| roll.borrow().is_accessible()) + .inspect(|roll| Roll::delete(roll)) + .count() + > 0; + + if !deleted_any { + break; + } + } + + rolls.iter().filter(|roll| roll.borrow().deleted).count() +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_INPUT: &str = " +..@@.@@@@. +@@@.@.@.@@ +@@@@@.@.@@ +@.@@@@..@. +@@.@@@@.@@ +.@@@@@@@.@ +.@.@.@.@@@ +@.@@@.@@@@ +.@@@@@@@@. +@.@.@@@.@. +"; + + #[test] + fn test_part1() { + assert_eq!( + number_of_accessible_rolls(&build_graph(parse_input(TEST_INPUT))), + 13 + ); + } + + #[test] + fn test_part2() { + assert_eq!( + number_of_removeable_rolls(&build_graph(parse_input(TEST_INPUT))), + 43 + ); + } +} diff --git a/flake.lock b/flake.lock index 90110f9..ec6ecaf 100644 --- a/flake.lock +++ b/flake.lock @@ -3,10 +3,10 @@ "inputs": { "flake": false, "locked": { - "lastModified": 1764767558, - "narHash": "sha256-AqEynDX4eu61liwLdGPzk0b6J9sE+dKWSpxfO+mZpBc=", + "lastModified": 1764840758, + "narHash": "sha256-GTN8SBnhvv6t2oDS1H85xcYAhafy8mpUQWSrCErhtgw=", "ref": "refs/heads/main", - "rev": "a1a4c48fcc3eddac81d35ce2030c887ac38f4002", + "rev": "415a59297c64d0448d22975ca5ad3d8ef064c9f0", "shallow": true, "type": "git", "url": "ssh://git@github.com/SebastianStork/advent-of-code-inputs.git"