Cleanup and added comments

This commit is contained in:
Dreaded_X 2023-12-05 22:54:35 +01:00
parent 408473c50a
commit c2b8638be2
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4

View File

@ -3,7 +3,6 @@
use std::{ use std::{
cmp::max, cmp::max,
collections::VecDeque, collections::VecDeque,
ops::{Add, BitAnd, BitOr},
}; };
use anyhow::Result; use anyhow::Result;
@ -29,7 +28,7 @@ mod tests {
} }
#[test] #[test]
fn part1_test2() -> Result<()> { fn part2_test1() -> Result<()> {
Day::test(Day::part2, "test-1", 46) Day::test(Day::part2, "test-1", 46)
} }
@ -59,14 +58,17 @@ struct Range {
} }
impl Range { impl Range {
/// Create a new range
pub fn new(start: isize, length: isize) -> Self { pub fn new(start: isize, length: isize) -> Self {
Self { start, length } Self { start, length }
} }
/// Get the position of the first element after the range
pub fn end(&self) -> isize { pub fn end(&self) -> isize {
self.start + self.length self.start + self.length
} }
/// Apply an offset of the range
pub fn offset(&self, offset: isize) -> Self { pub fn offset(&self, offset: isize) -> Self {
Self { Self {
start: self.start + offset, start: self.start + offset,
@ -74,6 +76,8 @@ impl Range {
} }
} }
/// Get the overlap between this range and another range.
/// Returns [None] if there is no overlap
pub fn overlap(&self, rhs: &Self) -> Option<Self> { pub fn overlap(&self, rhs: &Self) -> Option<Self> {
let start = max(self.start, rhs.start); let start = max(self.start, rhs.start);
let end = std::cmp::min(self.end(), rhs.end()); let end = std::cmp::min(self.end(), rhs.end());
@ -88,30 +92,77 @@ impl Range {
} }
} }
pub fn exclude(&self, rhs: &Self) -> VecDeque<Self> { /// Exclude a different range from the current one.
/// This returns a vector containing all ranges that form after excluding the different range
/// from this range.
/// The vector can be of size 0 if this range is included entirely in the different one.
/// Size 2 if the different range is included entirely in this one.
/// And size 1 if there is a partial overlap between the two ranges.
pub fn exclude(&self, rhs: &Self) -> Vec<Self> {
if let Some(overlap) = self.overlap(rhs) { if let Some(overlap) = self.overlap(rhs) {
if rhs.start <= self.start && rhs.end() >= self.end() { if rhs.start <= self.start && rhs.end() >= self.end() {
vec![].into() vec![]
} else if rhs.start <= self.start { } else if rhs.start <= self.start {
let start = rhs.end(); let start = rhs.end();
let length = self.end() - start; let length = self.end() - start;
vec![Range::new(start, length)].into() vec![Range::new(start, length)]
} else if rhs.end() >= self.end() { } else if rhs.end() >= self.end() {
let start = self.start; let start = self.start;
let length = rhs.start - start; let length = rhs.start - start;
vec![Range::new(start, length)].into() vec![Range::new(start, length)]
} else { } else {
let a = Range::new(self.start, overlap.start - self.start); let a = Range::new(self.start, overlap.start - self.start);
let b = Range::new(overlap.end(), self.end() - overlap.end()); let b = Range::new(overlap.end(), self.end() - overlap.end());
vec![a, b].into() vec![a, b]
} }
} else { } else {
vec![*self].into() vec![*self]
} }
} }
/// Check if a value is contained in this range
pub fn contains(&self, entry: isize) -> bool {
entry >= self.start && entry < self.end()
}
}
/// Parse a mapping block into a vector containing the each mapping range and the value that needs
/// to be added to perform the remap
fn parse_mapping_block(block: &str) -> Vec<(Range, isize)> {
// Each line of the mapping block is formatted as
// dst src len
block
// Take each line of the block
.lines()
// Skip the first line giving the name of the map
.skip(1)
.map(|line| {
let mut split = line.splitn(3, ' ').map(|num| num.parse::<isize>().unwrap());
let destination = split.next().unwrap();
let source = split.next().unwrap();
let length = split.next().unwrap();
// To move an entry from the source to the destination subtract the source and add the
// destination, adding the remap value will do this
let remap = destination - source;
(Range::new(source, length), remap)
})
.collect()
}
/// Process the input containing the seeds into a interator of all seed numbers
fn process_seeds(seeds: &str) -> impl Iterator<Item = isize> + '_ {
seeds
// Split the string at the whitespace
.split_whitespace()
// The first part contains "seed: " which we can ignore
.skip(1)
// Parse the numbers
.map(|num| num.parse().unwrap())
} }
// -- Solution -- // -- Solution --
@ -125,99 +176,87 @@ impl aoc::Solver for Day {
} }
fn part1(input: &str) -> Self::Output1 { fn part1(input: &str) -> Self::Output1 {
// Split all the input blocks
let mut split = input.split("\n\n"); let mut split = input.split("\n\n");
let mut seeds: Vec<_> = split
.next()
.unwrap()
.split_once(": ")
.unwrap()
.1
.split_whitespace()
.map(|seed| seed.parse::<isize>().unwrap())
.collect();
// Get a list of all seeds
let mut seeds: Vec<_> = process_seeds(split.next().unwrap()).collect();
// There are 7 mapping blocks, so loop 7 times
for _ in 0..7 { for _ in 0..7 {
let map = split let map = parse_mapping_block(split.next().unwrap());
.next()
.unwrap()
.lines()
.skip(1)
.map(|line| {
let mut split = line.splitn(3, ' ');
(
split.next().unwrap().parse::<isize>().unwrap(),
split.next().unwrap().parse::<isize>().unwrap(),
split.next().unwrap().parse::<isize>().unwrap(),
)
})
.collect::<Vec<_>>();
// Update the list of seeds
seeds = seeds seeds = seeds
.into_iter() .into_iter()
.map(|mut seed| { .map(|seed| {
// For each mapping check if the seed is in the mapping, if it is remap it
for mapping in map.iter() { for mapping in map.iter() {
if seed >= mapping.1 && seed < (mapping.1 + mapping.2) { if mapping.0.contains(seed) {
seed = seed - mapping.1 + mapping.0; return seed + mapping.1;
break;
} }
} }
// Otherwise keep the current value
seed seed
}) })
.collect(); .collect();
} }
// Get the minimum value
*seeds.iter().min().unwrap() *seeds.iter().min().unwrap()
} }
fn part2(input: &str) -> Self::Output2 { fn part2(input: &str) -> Self::Output2 {
// Split all the input blocks
let mut split = input.split("\n\n"); let mut split = input.split("\n\n");
let mut seeds: VecDeque<_> = split
.next() // Get a list of all seeds
.unwrap() let mut seeds: VecDeque<_> = process_seeds(split.next().unwrap())
.split_once(": ") // Take the entries pairwise and construct a range
.unwrap()
.1
.split_whitespace()
.map(|seed| seed.parse::<isize>().unwrap())
.map_windows(|range: &[isize; 2]| Range::new(range[0], range[1])) .map_windows(|range: &[isize; 2]| Range::new(range[0], range[1]))
// Make sure we move over 2 every time instead of only 1
.step_by(2) .step_by(2)
.collect(); .collect();
// There are 7 mapping blocks, so loop 7 times
for _ in 0..7 { for _ in 0..7 {
let map = split let map = parse_mapping_block(split.next().unwrap());
.next()
.unwrap()
.lines()
.skip(1)
.map(|line| {
let mut split = line.splitn(3, ' ');
let remap = split.next().unwrap().parse::<isize>().unwrap();
let start = split.next().unwrap().parse::<isize>().unwrap();
let length = split.next().unwrap().parse::<isize>().unwrap();
(Range::new(start, length), remap - start)
})
.collect::<Vec<_>>();
// Output list
let mut result = VecDeque::new(); let mut result = VecDeque::new();
// Keep processing the input list until it is empty
'outer: while let Some(seed) = seeds.pop_front() { 'outer: while let Some(seed) = seeds.pop_front() {
for mapping in map.iter() { for mapping in map.iter() {
// For each mapping check if there is overlap
if let Some(overlap) = seed.overlap(&mapping.0) { if let Some(overlap) = seed.overlap(&mapping.0) {
// Remap the overlapping part and add it to the output list
result.push_back(overlap.offset(mapping.1)); result.push_back(overlap.offset(mapping.1));
seeds.append(&mut seed.exclude(&mapping.0));
// Add the remainder back to the input list
seeds.append(&mut seed.exclude(&mapping.0).into());
// Continue with the next input
continue 'outer; continue 'outer;
} }
} }
// There was no overlap with any of the mappings, the current range can be added to the output list
result.push_back(seed); result.push_back(seed);
} }
// The input for the next iterator is the output of this iteration
seeds = result; seeds = result;
} }
seeds.iter().map(|range| range.start).min().unwrap() // Find the lowest seed value
seeds
.iter()
// The start of each range is the lowest value of that range
.map(|range| range.start)
// Get the minimum value
.min()
.unwrap()
} }
} }