Compare commits
2 Commits
d49157dd79
...
252bbf3cda
| Author | SHA1 | Date | |
|---|---|---|---|
|
252bbf3cda
|
|||
|
0ee1e316d7
|
BIN
.aoc_tiles/tiles/2025/05.png
Normal file
BIN
.aoc_tiles/tiles/2025/05.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
@@ -1,6 +1,6 @@
|
||||
<!-- AOC TILES BEGIN -->
|
||||
<h1 align="center">
|
||||
2025 - 8 ⭐ - Rust
|
||||
2025 - 10 ⭐ - Rust
|
||||
</h1>
|
||||
<a href="src/day1.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/01.png" width="161px">
|
||||
@@ -14,4 +14,7 @@
|
||||
<a href="src/day4.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/04.png" width="161px">
|
||||
</a>
|
||||
<a href="src/day5.rs">
|
||||
<img src=".aoc_tiles/tiles/2025/05.png" width="161px">
|
||||
</a>
|
||||
<!-- AOC TILES END -->
|
||||
|
||||
171
src/day5.rs
Normal file
171
src/day5.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt::Debug;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
struct Database {
|
||||
fresh_ingredients: Vec<RangeInclusive<u64>>,
|
||||
available_ingredients: Vec<u64>,
|
||||
}
|
||||
|
||||
struct Database2 {
|
||||
fresh_ingredients: Vec<Span>,
|
||||
}
|
||||
|
||||
#[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<Span>,
|
||||
}
|
||||
|
||||
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.is_empty() {
|
||||
parsing_ranges = false;
|
||||
continue;
|
||||
}
|
||||
if parsing_ranges {
|
||||
let (start, end) = line.split_once('-').unwrap();
|
||||
fresh_ingredients.push(RangeInclusive::new(
|
||||
start.parse().unwrap(),
|
||||
end.parse().unwrap(),
|
||||
));
|
||||
} else {
|
||||
available_ingredients.push(line.parse().unwrap())
|
||||
}
|
||||
}
|
||||
Database {
|
||||
fresh_ingredients,
|
||||
available_ingredients,
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
.available_ingredients
|
||||
.iter()
|
||||
.filter(|i| input.fresh_ingredients.iter().any(|r| r.contains(i)))
|
||||
.count() as u64
|
||||
}
|
||||
|
||||
#[aoc(day5, part2)]
|
||||
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::<u64>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const EXAMPLE: &str = "3-5
|
||||
10-14
|
||||
16-20
|
||||
12-18
|
||||
|
||||
1
|
||||
5
|
||||
8
|
||||
11
|
||||
17
|
||||
32";
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse(EXAMPLE)), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse2(EXAMPLE)), 14);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ mod day1;
|
||||
mod day2;
|
||||
mod day3;
|
||||
mod day4;
|
||||
mod day5;
|
||||
|
||||
use aoc_runner_derive::aoc_lib;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user