diff --git a/.aoc_tiles/tiles/2025/05.png b/.aoc_tiles/tiles/2025/05.png
index fd91c3e..6723ab7 100644
Binary files a/.aoc_tiles/tiles/2025/05.png and b/.aoc_tiles/tiles/2025/05.png differ
diff --git a/README.md b/README.md
index 205bb05..676e8aa 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
- 2025 - 9 ⭐ - Rust
+ 2025 - 10 ⭐ - Rust
diff --git a/src/day5.rs b/src/day5.rs
index 0ffc29a..a2f6131 100644
--- a/src/day5.rs
+++ b/src/day5.rs
@@ -1,3 +1,5 @@
+use std::cmp::{max, min};
+use std::fmt::Debug;
use std::ops::RangeInclusive;
use aoc_runner_derive::{aoc, aoc_generator};
@@ -7,18 +9,94 @@ struct Database {
available_ingredients: Vec,
}
-#[aoc_generator(day5)]
+struct Database2 {
+ fresh_ingredients: Vec,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct Span {
+ start: u64,
+ end: u64,
+}
+
+impl From<(u64, u64)> for Span {
+ fn from(value: (u64, u64)) -> Self {
+ Self {
+ start: value.0,
+ end: value.1,
+ }
+ }
+}
+
+impl Span {
+ fn len(&self) -> u64 {
+ self.end - self.start + 1
+ }
+}
+
+struct RangeSet {
+ ranges: Vec,
+}
+
+impl RangeSet {
+ fn new() -> Self {
+ Self { ranges: Vec::new() }
+ }
+ fn simplify(&mut self) {
+ // sort the ranges by start
+ if self.ranges.len() < 2 {
+ return;
+ }
+ let mut modified = true;
+ while modified {
+ let mut new_ranges = Vec::new();
+ modified = false;
+ self.ranges.sort_by_key(|s| s.start);
+ for chunk in self.ranges.chunks(2) {
+ if chunk.len() == 1 {
+ new_ranges.push(chunk[0].clone());
+ continue;
+ }
+ let (l, r) = (&chunk[0], &chunk[1]);
+ if r.start <= l.end + 1 {
+ modified = true;
+ new_ranges.push((min(r.start, l.start), max(l.end, r.end)).into())
+ } else {
+ new_ranges.push(l.clone());
+ new_ranges.push(r.clone());
+ }
+ }
+ if !self.ranges.len().is_multiple_of(2) && new_ranges.len() >= 2 {
+ let (l, r) = (new_ranges.pop().unwrap(), new_ranges.pop().unwrap());
+ if r.start <= l.end + 1 && r.end >= l.start - 1 {
+ modified = true;
+ new_ranges.push((min(r.start, l.start), max(l.end, r.end)).into())
+ } else {
+ new_ranges.push(l.clone());
+ new_ranges.push(r.clone());
+ }
+ }
+ self.ranges = new_ranges;
+ }
+ }
+ fn add(&mut self, s: Span) {
+ self.ranges.push(s);
+ self.simplify()
+ }
+}
+
+#[aoc_generator(day5, part1)]
fn parse(input: &str) -> Database {
let mut fresh_ingredients = Vec::new();
let mut available_ingredients = Vec::new();
let mut parsing_ranges = true;
for line in input.lines() {
- if line == "" {
+ if line.is_empty() {
parsing_ranges = false;
continue;
}
if parsing_ranges {
- let (start, end) = line.split_once(|c| c == '-').unwrap();
+ let (start, end) = line.split_once('-').unwrap();
fresh_ingredients.push(RangeInclusive::new(
start.parse().unwrap(),
end.parse().unwrap(),
@@ -33,6 +111,19 @@ fn parse(input: &str) -> Database {
}
}
+#[aoc_generator(day5, part2)]
+fn parse2(input: &str) -> Database2 {
+ let mut fresh_ingredients = Vec::new();
+ for line in input.lines() {
+ if line.is_empty() {
+ return Database2 { fresh_ingredients };
+ }
+ let (start, end) = line.split_once('-').unwrap();
+ fresh_ingredients.push((start.parse().unwrap(), end.parse().unwrap()).into());
+ }
+ Database2 { fresh_ingredients }
+}
+
#[aoc(day5, part1)]
fn part1(input: &Database) -> u64 {
input
@@ -43,8 +134,13 @@ fn part1(input: &Database) -> u64 {
}
#[aoc(day5, part2)]
-fn part2(input: &Database) -> u64 {
- 0
+fn part2(input: &Database2) -> u64 {
+ let mut all_ingredients = RangeSet::new();
+ for r in &input.fresh_ingredients {
+ all_ingredients.add(r.clone())
+ }
+
+ all_ingredients.ranges.iter().map(|r| r.len()).sum::()
}
#[cfg(test)]
@@ -70,6 +166,6 @@ mod tests {
#[test]
fn part2_example() {
- assert_eq!(part2(&parse(EXAMPLE)), 0);
+ assert_eq!(part2(&parse2(EXAMPLE)), 14);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 3961a65..1bb311f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@ mod day1;
mod day2;
mod day3;
mod day4;
+mod day5;
use aoc_runner_derive::aoc_lib;