From 0730ce7f016c78fa2f4058ff74386d1b8dc55e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Sat, 6 Dec 2025 00:52:43 +0100 Subject: [PATCH] Add solutions for "Day 5: Cafeteria", part 1 and 2 --- README.md | 4 ++ src/cafeteria.rs | 123 +++++++++++++++++++++++++++++++++++++ src/interval.rs | 19 ++++++ src/main.rs | 10 +-- src/printing_department.rs | 7 ++- 5 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 src/cafeteria.rs create mode 100644 src/interval.rs diff --git a/README.md b/README.md index 70e0e68..0f5b93c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ This project does not contain the puzzle or example inputs as per the [copyright :mag_right: Puzzle: , :white_check_mark: Solver: [`PrintingDepartment`](src/printing_department.rs) +### Day 5: Cafeteria + +:mag_right: Puzzle: , :white_check_mark: Solver: [`Cafeteria`](src/cafeteria.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/cafeteria.rs b/src/cafeteria.rs new file mode 100644 index 0000000..be21421 --- /dev/null +++ b/src/cafeteria.rs @@ -0,0 +1,123 @@ +use crate::interval::Interval; +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 } + } + + 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 process_data(&mut self, mut lines: io::Lines>) { + // Builds intervals collection. + let mut intervals: BTreeSet = BTreeSet::new(); + for line in lines + .by_ref() + .map_while(|x| x.ok().filter(|s| !s.is_empty())) + { + let values: Vec = line.split('-').map_while(|s| s.parse().ok()).collect(); + let mut interval = Interval::new(values[0], values[1]); + + let mut to_delete = Vec::new(); + for next in intervals.range(&interval..) { + if interval.contains(next.start) || interval.end + 1 == next.start { + // New interval overlaps with the "next" interval already in the collection. + if interval.end < next.end { + interval.end = next.end; + } + to_delete.push(next.clone()); + } else { + break; + } + } + + let mut do_insert = true; + if let Some(prev) = intervals.range(..=&interval).next_back() { + if interval.contains(prev.end) || prev.end + 1 == interval.start { + // Enlarges new interval to replace the "previous" interval already in the collection. + interval.start = prev.start; + to_delete.push(prev.clone()); + } else if interval.end <= prev.end { + do_insert = false; + } + } + + for del in to_delete { + intervals.remove(&del); + } + + if do_insert { + intervals.insert(interval); + } + } + + // Tests values against intervals. + for value in lines.map_while(|x| x.ok()?.parse::().ok()) { + if intervals.iter().any(|interval| interval.contains(value)) { + self.part1 += 1; + } + } + + self.part2 = intervals.iter().map(|x| x.len()).sum(); + } + + fn print_result(&self) { + println!("Part 1: {:?}\nPart 2: {:?}\n", 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 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/interval.rs b/src/interval.rs new file mode 100644 index 0000000..270acf0 --- /dev/null +++ b/src/interval.rs @@ -0,0 +1,19 @@ +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct Interval { + pub start: u64, + pub end: u64, +} + +impl Interval { + pub fn new(start: u64, end: u64) -> Interval { + Interval { start, end } + } + + pub fn contains(&self, value: u64) -> bool { + self.start <= value && value <= self.end + } + + pub fn len(&self) -> u64 { + self.end - self.start + 1 + } +} diff --git a/src/main.rs b/src/main.rs index 323c72d..c712e2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ +mod cafeteria; +mod interval; mod printing_department; -use printing_department::PrintingDepartment; - fn main() { - println!("### Advent of Code 2025 ###"); - let mut solver = PrintingDepartment::new(); - solver.run(); + println!("### Advent of Code 2025 ###\n"); + printing_department::PrintingDepartment::new().run(); + cafeteria::Cafeteria::new().run(); } diff --git a/src/printing_department.rs b/src/printing_department.rs index 09f35d2..dac1b7c 100644 --- a/src/printing_department.rs +++ b/src/printing_department.rs @@ -15,7 +15,7 @@ impl PrintingDepartment { } pub fn run(&mut self) { - println!("\n--- Day 4: Printing Department ---"); + 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. @@ -64,7 +64,7 @@ impl PrintingDepartment { } fn print_result(&self) { - println!("Part 1: {:?}\nPart 2: {:?}", self.part1, self.part2); + println!("Part 1: {:?}\nPart 2: {:?}\n", self.part1, self.part2); } } @@ -87,11 +87,12 @@ where .collect::>() .join("\n"); Err(format!( - "Cannot find puzzle input file, searched these paths:\n{}", + "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,