From 496293b33b8aaf33cdf63ce0d5284a6f3a2b852e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Mon, 8 Dec 2025 14:38:02 +0100 Subject: [PATCH] Refactor Solver trait to streamline usage --- src/main.rs | 8 ++--- src/solvers.rs | 49 +++++++++++++++--------------- src/solvers/cafeteria.rs | 45 ++++++++------------------- src/solvers/printing_department.rs | 41 +++++++------------------ src/solvers/trash_compactor.rs | 43 ++++++++------------------ 5 files changed, 63 insertions(+), 123 deletions(-) diff --git a/src/main.rs b/src/main.rs index edfe8fe..2e46c32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,7 @@ mod solvers; fn main() { println!("### Advent of Code 2025 ###\n"); - solvers::run(Box::new( - solvers::printing_department::PrintingDepartment::new(), - )); - solvers::run(Box::new(solvers::cafeteria::Cafeteria::new())); - solvers::run(Box::new(solvers::trash_compactor::TrashCompactor::new())); + solvers::run(solvers::printing_department::PrintingDepartment{}); + solvers::run(solvers::cafeteria::Cafeteria{}); + solvers::run(solvers::trash_compactor::TrashCompactor {}); } diff --git a/src/solvers.rs b/src/solvers.rs index 003defd..176f11e 100644 --- a/src/solvers.rs +++ b/src/solvers.rs @@ -8,52 +8,53 @@ pub mod printing_department; pub mod trash_compactor; pub trait Solver { - fn get_puzzle_index(&self) -> u8; - fn get_puzzle_name(&self) -> &str; - fn get_input_filename(&self) -> &str; - fn get_part1(&self) -> u64; - fn get_part2(&self) -> u64; - fn process_data(&mut self, lines: io::Lines>); + const PUZZLE_INDEX: u8; + const PUZZLE_NAME: &'static str; + // TODO: Replace constant input filename by a conversion from the puzzle name in the runner. + const INPUT_FILENAME: &'static str; + + fn puzzle_index(&self) -> u8 { Self::PUZZLE_INDEX } + fn puzzle_name(&self) -> &'static str { Self::PUZZLE_NAME } + fn input_filename(&self) -> &'static str { Self::INPUT_FILENAME } + + fn process_data(&self, reader: R) -> (u64, u64); } -pub fn run(mut solver: Box) { +pub fn run(solver: S) { println!( "--- Day {}: {} ---", - solver.get_puzzle_index(), - solver.get_puzzle_name() + solver.puzzle_index(), + solver.puzzle_name() ); + // TODO: Convert to constant and add "example" paths. // 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(solver.get_input_filename(), &paths) { - Ok(lines) => { - solver.process_data(lines); - print_result(solver); + match read_data_file(solver.input_filename(), &paths) { + Ok(reader) => { + let result = solver.process_data(reader); + print_result(result); } Err(error) => eprintln!("{}", error), }; } -fn print_result(solver: Box) { - println!( - "Part 1: {:?}\nPart 2: {:?}\n", - solver.get_part1(), - solver.get_part2() - ); +fn print_result(result: (u64, u64)) { + println!("Part 1: {:?}\nPart 2: {:?}\n", result.0, result.1); } fn read_data_file( filename: T, search_paths: &Vec, -) -> Result>, String> +) -> Result, String> where T: AsRef, { for path in search_paths { - if let Ok(lines) = read_lines(path.as_ref().join(&filename)) { - return Ok(lines); + if let Ok(reader) = read_file(path.as_ref().join(&filename)) { + return Ok(reader); } } @@ -69,10 +70,10 @@ where } // This code is taken from https://doc.rust-lang.org/stable/rust-by-example/std_misc/file/read_lines.html -fn read_lines(filename: T) -> io::Result>> +fn read_file(filename: T) -> io::Result> where T: AsRef, { let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) + Ok(io::BufReader::new(file)) } diff --git a/src/solvers/cafeteria.rs b/src/solvers/cafeteria.rs index 6511a03..c1eecbd 100644 --- a/src/solvers/cafeteria.rs +++ b/src/solvers/cafeteria.rs @@ -1,35 +1,19 @@ use crate::common::interval::Interval; use crate::solvers::Solver; use std::collections::BTreeSet; -use std::fs::File; -use std::io; +use std::io::BufRead; -pub struct Cafeteria { - part1: u64, - part2: u64, -} +pub struct Cafeteria {} impl Solver for Cafeteria { - fn get_puzzle_index(&self) -> u8 { - 5 - } - fn get_puzzle_name(&self) -> &str { - "Cafeteria" - } - fn get_input_filename(&self) -> &str { - "cafeteria.txt" - } - fn get_part1(&self) -> u64 { - self.part1 - } - fn get_part2(&self) -> u64 { - self.part2 - } - fn process_data(&mut self, mut lines: io::Lines>) { + const PUZZLE_INDEX: u8 = 5; + const PUZZLE_NAME: &'static str = "Cafeteria"; + const INPUT_FILENAME: &'static str = "cafeteria.txt"; + + fn process_data(&self, mut reader: R) -> (u64, u64) { // Builds intervals collection. let mut intervals: BTreeSet = BTreeSet::new(); - for line in lines - .by_ref() + for line in reader.by_ref().lines() .map_while(|x| x.ok().filter(|s| !s.is_empty())) { let values: Vec = line.split('-').map_while(|s| s.parse().ok()).collect(); @@ -68,19 +52,14 @@ impl Solver for Cafeteria { } } + let mut part1 = 0; // Tests values against intervals. - for value in lines.map_while(|x| x.ok()?.parse::().ok()) { + for value in reader.lines().map_while(|x| x.ok()?.parse::().ok()) { if intervals.iter().any(|interval| interval.contains(value)) { - self.part1 += 1; + part1 += 1; } } - self.part2 = intervals.iter().map(|x| x.len()).sum(); - } -} - -impl Cafeteria { - pub fn new() -> Cafeteria { - Cafeteria { part1: 0, part2: 0 } + (part1, intervals.iter().map(|x| x.len()).sum()) } } diff --git a/src/solvers/printing_department.rs b/src/solvers/printing_department.rs index 8e4ecf7..ed001e9 100644 --- a/src/solvers/printing_department.rs +++ b/src/solvers/printing_department.rs @@ -1,35 +1,21 @@ use crate::solvers::Solver; use grid::*; -use std::fs::File; -use std::io; +use std::io::BufRead; -pub struct PrintingDepartment { - part1: u64, - part2: u64, -} +pub struct PrintingDepartment {} impl Solver for PrintingDepartment { - fn get_puzzle_index(&self) -> u8 { - 4 - } - fn get_puzzle_name(&self) -> &str { - "Printing Department" - } - fn get_input_filename(&self) -> &str { - "printing_department.txt" - } - fn get_part1(&self) -> u64 { - self.part1 - } - fn get_part2(&self) -> u64 { - self.part2 - } - fn process_data(&mut self, lines: io::Lines>) { + const PUZZLE_INDEX: u8 = 4; + const PUZZLE_NAME: &'static str = "Printing Department"; + const INPUT_FILENAME: &'static str = "printing_department.txt"; + + fn process_data(&self, reader: R) -> (u64, u64) { let mut grid = Grid::new(0, 0); - for line in lines.map_while(Result::ok) { + for line in reader.lines().map_while(Result::ok) { grid.push_row(line.bytes().map(|c| c == b'@').collect()); } + let mut part1 = 0; 'cells: for cell in grid.indexed_iter() { if *cell.1 { let mut count = 0; @@ -52,14 +38,9 @@ impl Solver for PrintingDepartment { } } } - self.part1 += 1; + part1 += 1; } } - } -} - -impl PrintingDepartment { - pub fn new() -> PrintingDepartment { - PrintingDepartment { part1: 0, part2: 0 } + (part1, 0) } } diff --git a/src/solvers/trash_compactor.rs b/src/solvers/trash_compactor.rs index 640f85a..ca38b11 100644 --- a/src/solvers/trash_compactor.rs +++ b/src/solvers/trash_compactor.rs @@ -1,51 +1,32 @@ use crate::solvers::Solver; use grid::*; -use std::fs::File; -use std::io; +use std::io::BufRead; -pub struct TrashCompactor { - part1: u64, - part2: u64, -} +pub struct TrashCompactor {} impl Solver for TrashCompactor { - fn get_puzzle_index(&self) -> u8 { - 6 - } - fn get_puzzle_name(&self) -> &str { - "Trash Compactor" - } - fn get_input_filename(&self) -> &str { - "trash_compactor.txt" - } - fn get_part1(&self) -> u64 { - self.part1 - } - fn get_part2(&self) -> u64 { - self.part2 - } - fn process_data(&mut self, lines: io::Lines>) { + const PUZZLE_INDEX: u8 = 6; + const PUZZLE_NAME: &'static str = "Trash Compactor"; + const INPUT_FILENAME: &'static str = "trash_compactor.txt"; + + fn process_data(&self, reader: R) -> (u64, u64) { + let mut part1 = 0; let mut grid = Grid::new(0, 0); - for line in lines.map_while(Result::ok) { + for line in reader.lines().map_while(Result::ok) { let v: Vec<&str> = line.split(' ').filter(|s| !s.is_empty()).collect(); match v[0].parse::() { Ok(_) => grid.push_row(v.iter().map(|n| n.parse::().unwrap()).collect()), Err(_) => { for (i, sign) in v.iter().enumerate() { if *sign == "+" { - self.part1 += grid.iter_col(i).sum::(); + part1 += grid.iter_col(i).sum::(); } else { - self.part1 += grid.iter_col(i).product::(); + part1 += grid.iter_col(i).product::(); } } } } } - } -} - -impl TrashCompactor { - pub fn new() -> TrashCompactor { - TrashCompactor { part1: 0, part2: 0 } + (part1, 0) } }