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;