1
0

Add solutions for "Day 5: Cafeteria", part 1 and 2

This commit is contained in:
Stefan Müller
2025-12-06 00:52:43 +01:00
parent 312731ce06
commit 0730ce7f01
5 changed files with 155 additions and 8 deletions
+4
View File
@@ -14,6 +14,10 @@ This project does not contain the puzzle or example inputs as per the [copyright
:mag_right: Puzzle: <https://adventofcode.com/2025/day/4>, :white_check_mark: Solver: [`PrintingDepartment`](src/printing_department.rs)
### Day 5: Cafeteria
:mag_right: Puzzle: <https://adventofcode.com/2025/day/5>, :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.
+123
View File
@@ -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<io::BufReader<File>>) {
// Builds intervals collection.
let mut intervals: BTreeSet<Interval> = BTreeSet::new();
for line in lines
.by_ref()
.map_while(|x| x.ok().filter(|s| !s.is_empty()))
{
let values: Vec<u64> = 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::<u64>().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<T>(
filename: T,
search_paths: &Vec<T>,
) -> Result<io::Lines<io::BufReader<File>>, String>
where
T: AsRef<Path>,
{
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::<Vec<_>>()
.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<T>(filename: T) -> io::Result<io::Lines<io::BufReader<File>>>
where
T: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
+19
View File
@@ -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
}
}
+5 -5
View File
@@ -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();
}
+4 -3
View File
@@ -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::<Vec<_>>()
.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<T>(filename: T) -> io::Result<io::Lines<io::BufReader<File>>>
where
T: AsRef<Path>,