diff --git a/src/common.rs b/src/common.rs index 70589ba..2b00da5 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1 +1 @@ -pub mod interval; \ No newline at end of file +pub mod interval; diff --git a/src/main.rs b/src/main.rs index 6661c30..5aed89f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ -mod solvers; mod common; +mod solvers; fn main() { println!("### Advent of Code 2025 ###\n"); - solvers::printing_department::PrintingDepartment::new().run(); - solvers::cafeteria::Cafeteria::new().run(); + solvers::run(Box::new( + solvers::printing_department::PrintingDepartment::new(), + )); + solvers::run(Box::new(solvers::cafeteria::Cafeteria::new())); } diff --git a/src/solvers.rs b/src/solvers.rs index 3cd2977..a6489a1 100644 --- a/src/solvers.rs +++ b/src/solvers.rs @@ -1,2 +1,77 @@ +use std::fs::File; +use std::io; +use std::io::BufRead; +use std::path::Path; + pub mod cafeteria; pub mod printing_department; + +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>); +} + +pub fn run(mut solver: Box) { + println!( + "--- Day {}: {} ---", + solver.get_puzzle_index(), + solver.get_puzzle_name() + ); + + // 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); + } + Err(error) => eprintln!("{}", error), + }; +} + +fn print_result(solver: Box) { + println!( + "Part 1: {:?}\nPart 2: {:?}\n", + solver.get_part1(), + solver.get_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 open puzzle input file, searched these paths:\n{}", + message + )) +} + +// 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>> +where + T: AsRef, +{ + let file = File::open(filename)?; + Ok(io::BufReader::new(file).lines()) +} diff --git a/src/solvers/cafeteria.rs b/src/solvers/cafeteria.rs index c928e61..6511a03 100644 --- a/src/solvers/cafeteria.rs +++ b/src/solvers/cafeteria.rs @@ -1,36 +1,30 @@ use crate::common::interval::Interval; +use crate::solvers::Solver; use std::collections::BTreeSet; use std::fs::File; use std::io; -use std::io::BufRead; -use std::path::Path; pub struct Cafeteria { part1: u64, part2: u64, } -impl Cafeteria { - pub fn new() -> Cafeteria { - Cafeteria { part1: 0, part2: 0 } +impl Solver for Cafeteria { + fn get_puzzle_index(&self) -> u8 { + 5 } - - pub fn run(&mut self) { - println!("--- Day 5: Cafeteria ---"); - - // 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("cafeteria.txt", &paths) { - Ok(lines) => { - self.process_data(lines); - self.print_result(); - } - Err(error) => eprintln!("{}", error), - }; + 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>) { // Builds intervals collection. let mut intervals: BTreeSet = BTreeSet::new(); @@ -83,41 +77,10 @@ impl Cafeteria { self.part2 = intervals.iter().map(|x| x.len()).sum(); } +} - fn print_result(&self) { - println!("Part 1: {:?}\nPart 2: {:?}\n", self.part1, self.part2); +impl Cafeteria { + pub fn new() -> Cafeteria { + Cafeteria { part1: 0, part2: 0 } } } - -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 open puzzle input file, searched these paths:\n{}", - message - )) -} - -// 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>> -where - T: AsRef, -{ - let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) -} diff --git a/src/solvers/printing_department.rs b/src/solvers/printing_department.rs index dac1b7c..8e4ecf7 100644 --- a/src/solvers/printing_department.rs +++ b/src/solvers/printing_department.rs @@ -1,35 +1,29 @@ +use crate::solvers::Solver; 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 } +impl Solver for PrintingDepartment { + fn get_puzzle_index(&self) -> u8 { + 4 } - - pub fn run(&mut self) { - println!("--- 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 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>) { let mut grid = Grid::new(0, 0); for line in lines.map_while(Result::ok) { @@ -62,41 +56,10 @@ impl PrintingDepartment { } } } +} - fn print_result(&self) { - println!("Part 1: {:?}\nPart 2: {:?}\n", self.part1, self.part2); +impl PrintingDepartment { + pub fn new() -> PrintingDepartment { + PrintingDepartment { part1: 0, part2: 0 } } } - -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 open puzzle input file, searched these paths:\n{}", - message - )) -} - -// 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>> -where - T: AsRef, -{ - let file = File::open(filename)?; - Ok(io::BufReader::new(file).lines()) -}