Add solutions for "Day 5: Cafeteria", part 1 and 2
This commit is contained in:
@@ -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())
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
|
||||
Reference in New Issue
Block a user