Cleanup and added comments
This commit is contained in:
parent
408473c50a
commit
c2b8638be2
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user