diff --git a/Cargo.lock b/Cargo.lock index 3299a8d..71c57a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,12 @@ version = 4 [[package]] name = "advent_of_code_2025" version = "0.1.0" +dependencies = [ + "grid", +] + +[[package]] +name = "grid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" diff --git a/Cargo.toml b/Cargo.toml index cb3e139..3c13b18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +grid = "1.0.0" diff --git a/README.md b/README.md index ef7899d..70e0e68 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ This is a single command line application for the puzzles written in [Rust](http This project does not contain the puzzle or example inputs as per the [copyright notice of Advent of Code](https://adventofcode.com/about). In order to run the compiled application, the puzzle inputs have to be downloaded from the [Advent of Code 2025](https://adventofcode.com/2025/) puzzle pages, and placed as text files into the `AdventOfCode2025\data` directory, e.g. `AdventOfCode2025\data\secret_entrance.txt`, or `AdventOfCode2025\data\example\secret_entrance.txt` for the unit tests. The application will output an error message with details if it cannot find an input file. +## Solutions + +### Day 4: Printing Department + +:mag_right: Puzzle: , :white_check_mark: Solver: [`PrintingDepartment`](src/printing_department.rs) + ## Tests The package contains unit tests for each solver to help troubleshoot issues and prevent regressions. These tests cover the solutions for provided examples and full data inputs. The solutions used within the tests are user-specific. diff --git a/src/main.rs b/src/main.rs index e7a11a9..323c72d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,9 @@ +mod printing_department; + +use printing_department::PrintingDepartment; + fn main() { - println!("Hello, world!"); + println!("### Advent of Code 2025 ###"); + let mut solver = PrintingDepartment::new(); + solver.run(); } diff --git a/src/printing_department.rs b/src/printing_department.rs new file mode 100644 index 0000000..09f35d2 --- /dev/null +++ b/src/printing_department.rs @@ -0,0 +1,101 @@ +use grid::*; +use std::fs::File; +use std::io; +use std::io::BufRead; +use std::path::Path; + +pub struct PrintingDepartment { + part1: u64, + part2: u64, +} + +impl PrintingDepartment { + pub fn new() -> PrintingDepartment { + PrintingDepartment { part1: 0, part2: 0 } + } + + pub fn run(&mut self) { + println!("\n--- Day 4: Printing Department ---"); + + // The "../../data" and "../../../data" paths are useful for running the binary from + // "target/release/" directory with "data/" directory in package root or parent directory. + let paths = vec!["./data", "../data", "../../data", "../../../data"]; + + match read_data_file("printing_department.txt", &paths) { + Ok(lines) => { + self.process_data(lines); + self.print_result(); + } + Err(error) => eprintln!("{}", error), + }; + } + + fn process_data(&mut self, lines: io::Lines>) { + let mut grid = Grid::new(0, 0); + for line in lines.map_while(Result::ok) { + grid.push_row(line.bytes().map(|c| c == b'@').collect()); + } + + '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; + } + } + } + } + } + } + self.part1 += 1; + } + } + } + + fn print_result(&self) { + println!("Part 1: {:?}\nPart 2: {:?}", self.part1, self.part2); + } +} + +fn read_data_file( + filename: T, + search_paths: &Vec, +) -> Result>, String> +where + T: AsRef, +{ + for path in search_paths { + if let Ok(lines) = read_lines(path.as_ref().join(&filename)) { + return Ok(lines); + } + } + + let message = search_paths + .iter() + .map(|path| path.as_ref().join(&filename).display().to_string()) + .collect::>() + .join("\n"); + Err(format!( + "Cannot find puzzle input file, searched these paths:\n{}", + message + )) +} + +fn read_lines(filename: T) -> io::Result>> +where + T: AsRef, +{ + let file = File::open(filename)?; + Ok(io::BufReader::new(file).lines()) +}