Solve day 5
This commit is contained in:
@@ -36,6 +36,7 @@ Timings are given as: [lower-bound **best-estimate** upper-bound]
|
|||||||
| 02 | [2.0386 ms **2.0483 ms** 2.0584 ms] | [2.0823 ms **2.0918 ms** 2.1015 ms] |
|
| 02 | [2.0386 ms **2.0483 ms** 2.0584 ms] | [2.0823 ms **2.0918 ms** 2.1015 ms] |
|
||||||
| 03 | [45.711 µs **45.937 µs** 46.177 µs] | [267.18 µs **267.95 µs** 268.75 µs] |
|
| 03 | [45.711 µs **45.937 µs** 46.177 µs] | [267.18 µs **267.95 µs** 268.75 µs] |
|
||||||
| 04 | [143.40 µs **144.00 µs** 144.73 µs] | [1.6165 ms **1.6258 ms** 1.6355 ms] |
|
| 04 | [143.40 µs **144.00 µs** 144.73 µs] | [1.6165 ms **1.6258 ms** 1.6355 ms] |
|
||||||
|
| 05 | [187.25 µs **188.93 µs** 190.74 µs] | [63.809 µs **64.204 µs** 64.606 µs] |
|
||||||
|
|
||||||
## Profiling
|
## Profiling
|
||||||
|
|
||||||
|
|||||||
11
src/day05/input/test1.txt
Normal file
11
src/day05/input/test1.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
3-5
|
||||||
|
10-14
|
||||||
|
16-20
|
||||||
|
12-18
|
||||||
|
|
||||||
|
1
|
||||||
|
5
|
||||||
|
8
|
||||||
|
11
|
||||||
|
17
|
||||||
|
32
|
||||||
125
src/day05/mod.rs
Normal file
125
src/day05/mod.rs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
use color_eyre::{
|
||||||
|
Result,
|
||||||
|
eyre::{Error, eyre},
|
||||||
|
};
|
||||||
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
pub const INPUT: &str = include_str!("input/input.txt");
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct FreshRange(pub std::ops::RangeInclusive<i64>);
|
||||||
|
|
||||||
|
impl FromStr for FreshRange {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut parts = s.split('-');
|
||||||
|
let start = parts
|
||||||
|
.next()
|
||||||
|
.ok_or(eyre!("Invalid fresh range: no start"))?
|
||||||
|
.parse::<i64>()?;
|
||||||
|
let end = parts
|
||||||
|
.next()
|
||||||
|
.ok_or(eyre!("Invalid fresh range: no end"))?
|
||||||
|
.parse::<i64>()?;
|
||||||
|
if parts.next().is_some() {
|
||||||
|
return Err(eyre!("Invalid fresh range: too many parts"));
|
||||||
|
}
|
||||||
|
Ok(FreshRange(start..=end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FreshRange {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}-{}", self.0.start(), self.0.end())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(input))]
|
||||||
|
pub fn part1(input: &str) -> Result<usize> {
|
||||||
|
let mut processing_ranges = true;
|
||||||
|
let mut fresh_ranges = Vec::new();
|
||||||
|
let mut fresh_ingredients = 0;
|
||||||
|
for line in input.trim().lines() {
|
||||||
|
if line.is_empty() {
|
||||||
|
processing_ranges = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if processing_ranges {
|
||||||
|
let range = line.parse::<FreshRange>()?;
|
||||||
|
debug!(range = %range);
|
||||||
|
fresh_ranges.push(range);
|
||||||
|
} else {
|
||||||
|
let ingredient = line.parse::<i64>()?;
|
||||||
|
debug!(ingredient);
|
||||||
|
for range in &mut fresh_ranges {
|
||||||
|
if range.0.contains(&ingredient) {
|
||||||
|
fresh_ingredients += 1;
|
||||||
|
debug!(fresh_ingredients, "fresh!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fresh_ingredients)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(input))]
|
||||||
|
pub fn part2(input: &str) -> Result<usize> {
|
||||||
|
let mut fresh_ranges: Vec<Option<FreshRange>> = Vec::new();
|
||||||
|
for line in input.trim().lines() {
|
||||||
|
if line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let range = line.parse::<FreshRange>()?;
|
||||||
|
let mut overlap_range = range.clone();
|
||||||
|
debug!(range = %range);
|
||||||
|
for range_slot in fresh_ranges.iter_mut() {
|
||||||
|
if let Some(existing_range) = range_slot {
|
||||||
|
if overlap_range.0.end() < existing_range.0.start()
|
||||||
|
|| overlap_range.0.start() > existing_range.0.end()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let start = *overlap_range.0.start().min(existing_range.0.start());
|
||||||
|
let end = *overlap_range.0.end().max(existing_range.0.end());
|
||||||
|
if start <= end {
|
||||||
|
overlap_range = FreshRange(start..=end);
|
||||||
|
debug!(overlap_range = %overlap_range, existing_range = %existing_range, "merging existing range");
|
||||||
|
*range_slot = None; // this existing range is now completely merged with the current range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fresh_ranges.push(Some(overlap_range));
|
||||||
|
}
|
||||||
|
Ok(fresh_ranges
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.map(|r| (r.0.end() - r.0.start() + 1) as usize)
|
||||||
|
.sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use test_log::test;
|
||||||
|
|
||||||
|
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part1() {
|
||||||
|
assert_eq!(part1(TEST_INPUT1).unwrap(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2() {
|
||||||
|
assert_eq!(part2(TEST_INPUT1).unwrap(), 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2_triple_overlap() {
|
||||||
|
assert_eq!(part2("3-4\n2-5\n1-6").unwrap(), 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ macro_rules! all_days {
|
|||||||
2 => day02,
|
2 => day02,
|
||||||
3 => day03,
|
3 => day03,
|
||||||
4 => day04,
|
4 => day04,
|
||||||
|
5 => day05,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ pub mod day01;
|
|||||||
pub mod day02;
|
pub mod day02;
|
||||||
pub mod day03;
|
pub mod day03;
|
||||||
pub mod day04;
|
pub mod day04;
|
||||||
|
pub mod day05;
|
||||||
pub mod days;
|
pub mod days;
|
||||||
|
|||||||
Reference in New Issue
Block a user