Compare commits
No commits in common. "c2b8638be2c90bc091ea7d096b5fd32611c55584" and "a5c515f64aacf42d886d28c8441cdde68204f329" have entirely different histories.
c2b8638be2
...
a5c515f64a
|
@ -88,7 +88,7 @@ impl aoc::Solver for Day {
|
||||||
|
|
||||||
// If valid, get the id and add it to the sum
|
// If valid, get the id and add it to the sum
|
||||||
if valid {
|
if valid {
|
||||||
let id = game.split_once(' ').unwrap().1;
|
let (_, id) = game.split_once(' ').unwrap();
|
||||||
let id: usize = id.parse().unwrap();
|
let id: usize = id.parse().unwrap();
|
||||||
Some(id)
|
Some(id)
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,7 +107,7 @@ impl aoc::Solver for Day {
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
// Split the game id from the actual game played
|
// Split the game id from the actual game played
|
||||||
let line = line.split_once(": ").unwrap().1;
|
let (_, line) = line.split_once(": ").unwrap();
|
||||||
|
|
||||||
// Get the required minimum amount for each color
|
// Get the required minimum amount for each color
|
||||||
let required = line.split(", ").fold((0, 0, 0), |acc, entry| {
|
let required = line.split(", ").fold((0, 0, 0), |acc, entry| {
|
||||||
|
|
|
@ -61,10 +61,9 @@ impl aoc::Solver for Day {
|
||||||
.lines()
|
.lines()
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
// Get rid of the first part
|
// Get rid of the first part
|
||||||
let line = line
|
let (_, line) = line
|
||||||
.split_once(": ")
|
.split_once(": ")
|
||||||
.expect("Input should be formatted properly")
|
.expect("Input should be formatted properly");
|
||||||
.1;
|
|
||||||
|
|
||||||
// Seperate the winning numbers and numbers we have
|
// Seperate the winning numbers and numbers we have
|
||||||
let (winning, numbers) = line
|
let (winning, numbers) = line
|
||||||
|
@ -97,10 +96,9 @@ impl aoc::Solver for Day {
|
||||||
|
|
||||||
input.lines().enumerate().for_each(|(i, line)| {
|
input.lines().enumerate().for_each(|(i, line)| {
|
||||||
// Get rid of the first part
|
// Get rid of the first part
|
||||||
let line = line
|
let (_, line) = line
|
||||||
.split_once(": ")
|
.split_once(": ")
|
||||||
.expect("Input should be formatted properly")
|
.expect("Input should be formatted properly");
|
||||||
.1;
|
|
||||||
|
|
||||||
// Seperate the winning numbers and numbers we have
|
// Seperate the winning numbers and numbers we have
|
||||||
let (winning, numbers) = line
|
let (winning, numbers) = line
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::max,
|
cmp::max,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
|
ops::{Add, BitAnd, BitOr},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -28,7 +29,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_test1() -> Result<()> {
|
fn part1_test2() -> Result<()> {
|
||||||
Day::test(Day::part2, "test-1", 46)
|
Day::test(Day::part2, "test-1", 46)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,17 +59,14 @@ 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,
|
||||||
|
@ -76,8 +74,6 @@ 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());
|
||||||
|
@ -92,77 +88,30 @@ impl Range {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exclude a different range from the current one.
|
pub fn exclude(&self, rhs: &Self) -> VecDeque<Self> {
|
||||||
/// 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![]
|
vec![].into()
|
||||||
} 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)]
|
vec![Range::new(start, length)].into()
|
||||||
} 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)]
|
vec![Range::new(start, length)].into()
|
||||||
} 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]
|
vec![a, b].into()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![*self]
|
vec![*self].into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 --
|
||||||
|
@ -176,87 +125,99 @@ 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 = parse_mapping_block(split.next().unwrap());
|
let map = split
|
||||||
|
.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(|seed| {
|
.map(|mut 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 mapping.0.contains(seed) {
|
if seed >= mapping.1 && seed < (mapping.1 + mapping.2) {
|
||||||
return seed + mapping.1;
|
seed = seed - mapping.1 + mapping.0;
|
||||||
|
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
|
||||||
// Get a list of all seeds
|
.next()
|
||||||
let mut seeds: VecDeque<_> = process_seeds(split.next().unwrap())
|
.unwrap()
|
||||||
// Take the entries pairwise and construct a range
|
.split_once(": ")
|
||||||
|
.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 = parse_mapping_block(split.next().unwrap());
|
let map = split
|
||||||
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the lowest seed value
|
seeds.iter().map(|range| range.start).min().unwrap()
|
||||||
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