Restarted day 5

Implemented parsing for task 1 and 2
Implemented solution for task 1
This commit is contained in:
BoolPurist 2024-08-17 21:20:26 +02:00
parent 79b55bd23a
commit 4c093353cf
17 changed files with 1092 additions and 537 deletions

0
3 Normal file
View file

451
output.txt Normal file
View file

@ -0,0 +1,451 @@
Compiling advent_of_code_2023 v0.1.0 (/home/nice_graphic/Code/AdventOfCode/advent_of_code_2023_in_rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.74s
Running `target/debug/advent_of_code_2023 -d 5 -t 2 -r real_puzzel_input/day5_real.txt`
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 1742065688,
end: 1750102221,
},
SeedRange {
start: 2567370277,
end: 2599723683,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 0,
end: 8416624,
},
SeedRange {
start: 266989853,
end: 274550609,
},
SeedRange {
start: 300866774,
end: 317512630,
},
SeedRange {
start: 358218184,
end: 372723591,
},
SeedRange {
start: 380891048,
end: 442413628,
},
SeedRange {
start: 515244023,
end: 557308930,
},
SeedRange {
start: 683066098,
end: 684855124,
},
SeedRange {
start: 807721029,
end: 832371103,
},
SeedRange {
start: 1194459751,
end: 1203805997,
},
SeedRange {
start: 1468004524,
end: 1485692688,
},
SeedRange {
start: 1543990204,
end: 1544270679,
},
SeedRange {
start: 2018290756,
end: 2019801469,
},
SeedRange {
start: 2019801470,
end: 2042706893,
},
SeedRange {
start: 2115385660,
end: 2129003835,
},
SeedRange {
start: 2129003836,
end: 2211102547,
},
SeedRange {
start: 2392979056,
end: 2408003322,
},
SeedRange {
start: 2607311497,
end: 2674349508,
},
SeedRange {
start: 2746561268,
end: 2747062564,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 58753428,
end: 65411093,
},
SeedRange {
start: 100997493,
end: 137201535,
},
SeedRange {
start: 157986927,
end: 199863499,
},
SeedRange {
start: 204313376,
end: 218545188,
},
SeedRange {
start: 332621358,
end: 372723591,
},
SeedRange {
start: 426571448,
end: 467405784,
},
SeedRange {
start: 495575122,
end: 511974002,
},
SeedRange {
start: 743910440,
end: 759938333,
},
SeedRange {
start: 759938334,
end: 760087643,
},
SeedRange {
start: 793045073,
end: 807721028,
},
SeedRange {
start: 867655257,
end: 896123052,
},
SeedRange {
start: 896123053,
end: 973947275,
},
SeedRange {
start: 973947276,
end: 993981548,
},
SeedRange {
start: 993981549,
end: 1002343672,
},
SeedRange {
start: 1055927934,
end: 1095728907,
},
SeedRange {
start: 1095728908,
end: 1119175102,
},
SeedRange {
start: 1154854138,
end: 1180635906,
},
SeedRange {
start: 1238423661,
end: 1248511724,
},
SeedRange {
start: 1512990961,
end: 1523584140,
},
SeedRange {
start: 1534040686,
end: 1546302601,
},
SeedRange {
start: 1803258673,
end: 1809841397,
},
SeedRange {
start: 1809841398,
end: 1841935458,
},
SeedRange {
start: 1856531115,
end: 1913738155,
},
SeedRange {
start: 2018290756,
end: 2028995369,
},
SeedRange {
start: 2674349509,
end: 2681453545,
},
SeedRange {
start: 2738910873,
end: 2744209881,
},
SeedRange {
start: 2747062565,
end: 2783941215,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 475181169,
end: 490368072,
},
SeedRange {
start: 836186785,
end: 843842518,
},
SeedRange {
start: 2734840928,
end: 2738910872,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 244009039,
end: 254933582,
},
SeedRange {
start: 305477485,
end: 329774690,
},
SeedRange {
start: 442413629,
end: 458180769,
},
SeedRange {
start: 1260160771,
end: 1272797031,
},
SeedRange {
start: 1272797032,
end: 1286745099,
},
SeedRange {
start: 1769136092,
end: 1817580770,
},
SeedRange {
start: 1941952815,
end: 1959258660,
},
SeedRange {
start: 2941559481,
end: 2963703887,
},
SeedRange {
start: 3338671715,
end: 3392340230,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 332621358,
end: 351714897,
},
SeedRange {
start: 372723592,
end: 390393835,
},
SeedRange {
start: 426571448,
end: 467405784,
},
SeedRange {
start: 1203805998,
end: 1248511724,
},
SeedRange {
start: 1512990961,
end: 1523584140,
},
SeedRange {
start: 1628354042,
end: 1634087664,
},
SeedRange {
start: 2089724509,
end: 2193360445,
},
SeedRange {
start: 2333003205,
end: 2348927163,
},
SeedRange {
start: 2919622420,
end: 2941559480,
},
SeedRange {
start: 3241659967,
end: 3257744758,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 137201536,
end: 157986926,
},
SeedRange {
start: 218545189,
end: 220477638,
},
SeedRange {
start: 600713965,
end: 665397099,
},
SeedRange {
start: 832371104,
end: 836186784,
},
SeedRange {
start: 843842519,
end: 867655256,
},
SeedRange {
start: 1119175103,
end: 1138382174,
},
SeedRange {
start: 1491903136,
end: 1499334672,
},
SeedRange {
start: 1523584141,
end: 1543990203,
},
SeedRange {
start: 2281428417,
end: 2289569154,
},
SeedRange {
start: 2492935748,
end: 2514211768,
},
SeedRange {
start: 2562030648,
end: 2567370276,
},
SeedRange {
start: 2744209882,
end: 2758197105,
},
SeedRange {
start: 2961556571,
end: 2970025309,
},
SeedRange {
start: 2970025310,
end: 2975037528,
},
SeedRange {
start: 3055485859,
end: 3060533366,
},
SeedRange {
start: 3336845004,
end: 3337449016,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 372723592,
end: 390393835,
},
SeedRange {
start: 1203805998,
end: 1230926691,
},
SeedRange {
start: 1230926692,
end: 1234898502,
},
SeedRange {
start: 1450460003,
end: 1468004523,
},
SeedRange {
start: 1546302602,
end: 1600730425,
},
SeedRange {
start: 1628354042,
end: 1634087664,
},
SeedRange {
start: 1644303246,
end: 1684446508,
},
SeedRange {
start: 1702371352,
end: 1750102221,
},
SeedRange {
start: 1899676495,
end: 1926978899,
},
SeedRange {
start: 1950759356,
end: 1994459585,
},
SeedRange {
start: 2089724509,
end: 2193360445,
},
SeedRange {
start: 2172619534,
end: 2188333609,
},
SeedRange {
start: 2333003205,
end: 2348927163,
},
SeedRange {
start: 2567370277,
end: 2607311496,
},
SeedRange {
start: 2885502549,
end: 2941559480,
},
SeedRange {
start: 3241659967,
end: 3257744758,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 1672236638,
end: 1707764533,
},
SeedRange {
start: 2064249380,
end: 2073429343,
},
]
[src/solutions/day5/task2.rs:42:5] &current_branches = [
SeedRange {
start: 688038520,
end: 693497941,
},
SeedRange {
start: 1485692689,
end: 1491903135,
},
SeedRange {
start: 1750102222,
end: 1755861445,
},
SeedRange {
start: 1775334238,
end: 1803258672,
},
]

70
src/iterations.rs Normal file
View file

@ -0,0 +1,70 @@
pub trait AdventIterator: Iterator {
fn into_chunks<const N: usize>(self) -> impl Iterator<Item = Chunck<N, Self::Item>>
where
Self: Sized,
{
in_chunks::<N, Self::Item>(self)
}
}
impl<I> AdventIterator for I where I: Iterator {}
#[derive(Debug, PartialEq, Eq)]
pub enum Chunck<const N: usize, T> {
Next([T; N]),
Rest(Vec<T>),
}
pub fn in_chunks<const N: usize, T>(
mut iterator: impl Iterator<Item = T>,
) -> impl Iterator<Item = Chunck<N, T>> {
let mut buffer = Vec::new();
let mut done = false;
std::iter::from_fn(move || {
if done {
return None;
}
for _ in 0..N {
match iterator.next() {
Some(to_push) => buffer.push(to_push),
None => {
done = true;
return if buffer.is_empty() {
None
} else {
Some(Chunck::Rest(std::mem::take(&mut buffer)))
};
}
}
}
let array: [T; N] = std::mem::take(&mut buffer)
.try_into()
.unwrap_or_else(|_| panic!("Buffer must have the same size as the N ({})", N));
Some(Chunck::Next(array))
})
}
#[cfg(test)]
mod testing {
use crate::iterations::{in_chunks, Chunck};
#[test]
fn should_split_chunks() {
fn assert_case<const N: usize>(input: Vec<u32>, expected: Vec<Chunck<N, u32>>) {
let actual: Vec<Chunck<N, u32>> = in_chunks::<N, u32>(input.into_iter()).collect();
assert_eq!(expected, actual);
}
assert_case::<2>(vec![2], vec![Chunck::Rest(vec![2])]);
assert_case::<2>(vec![], vec![]);
assert_case::<2>(vec![2, 2], vec![Chunck::Next([2, 2])]);
assert_case::<2>(
vec![1, 2, 3],
vec![Chunck::Next([1, 2]), Chunck::Rest(vec![3])],
);
assert_case::<1>(
vec![1, 2, 3],
vec![Chunck::Next([1]), Chunck::Next([2]), Chunck::Next([3])],
);
}
}

View file

@ -1,4 +1,6 @@
pub mod cli; pub mod cli;
pub mod constants; pub mod constants;
pub mod iterations;
pub mod parsing_utils; pub mod parsing_utils;
pub mod sequences;
pub mod solutions; pub mod solutions;

26
src/sequences.rs Normal file
View file

@ -0,0 +1,26 @@
pub fn is_sorted<T>(shoube_be_sorted: &[T]) -> bool
where
T: PartialOrd + Ord + Clone + PartialEq + Eq,
{
let mut sorted: Vec<T> = shoube_be_sorted.into_iter().cloned().collect();
sorted.sort();
sorted.as_slice().eq(shoube_be_sorted)
}
#[cfg(test)]
mod testing {
#[test]
fn should_detect_if_is_sorted() {
fn assert_case(input: &[u32], expected: bool) {
let actual = super::is_sorted(input);
assert_eq!(expected, actual, "Input {:#?}", input);
}
assert_case(&[1, 2], true);
assert_case(&[2, 1, 2], false);
assert_case(&[2, 1], false);
assert_case(&[1], true);
assert_case(&[], true);
}
}

View file

@ -6,7 +6,7 @@ pub fn create_solutions() -> Vec<Vec<fn(&str) -> String>> {
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![day5::solve_task_1, not_implemented_yet], vec![day5::solve_task_1, day5::solve_task_2],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],
vec![not_implemented_yet, not_implemented_yet], vec![not_implemented_yet, not_implemented_yet],

View file

@ -1,68 +1,94 @@
use mapping_layer::MappingLayer; use item_mapping::ItemMapping;
use seed_range::SeedRange;
type UnsignedNumber = u64; mod item_mapping;
mod mapping_layer;
mod parsing; mod parsing;
mod range_mapping; mod seed_range;
type UnsignedNumber = u128;
pub fn solve_task_1(input: &str) -> String { pub fn solve_task_1(input: &str) -> String {
let (seeds, layers) = parsing::parse(input); let (seeds, mapping_layers) = parsing::parse(input);
let location = get_lowest_locations_from(&seeds, &layers); let min = min_location_from_given_single_seeds(&seeds, &mapping_layers);
location.to_string() min.to_string()
} }
fn get_lowest_locations_from(seeds: &[UnsignedNumber], layers: &[MappingLayer]) -> UnsignedNumber { fn min_location_from_given_single_seeds(
seeds seeds: &[UnsignedNumber],
.into_iter() layers: &[Vec<ItemMapping>],
.map(|next_seed| location_of_one_point(*next_seed, layers)) ) -> UnsignedNumber {
.min() let mut min = UnsignedNumber::MAX;
.unwrap() for next_seed in seeds {
} let mut current_seed = *next_seed;
fn location_of_one_point(seed: UnsignedNumber, layers: &[MappingLayer]) -> UnsignedNumber {
let mut current_position = seed;
for next_layer in layers { for next_layer in layers {
let ranges = next_layer.ranges(); for next_mapping in next_layer {
let range_to_use = match ranges match next_mapping.map_point(current_seed) {
.binary_search_by_key(&current_position, |extract_source| extract_source.source()) Some(found_mapping) => {
{ current_seed = found_mapping;
Ok(exact) => ranges break;
.get(exact) }
.expect("Exact index can not be out of bounds"), None => (),
Err(not_exact) => {
let not_exact = not_exact.clamp(0, ranges.len() - 1);
let source = ranges.get(not_exact).unwrap();
let source_pos = source.source();
if current_position > source_pos {
source
} else {
ranges.get(not_exact.saturating_sub(1)).unwrap()
} }
} }
}
min = current_seed.min(min);
}
min
}
pub fn solve_task_2(input: &str) -> String {
let (starting_from, mappings) = {
let (single_seeds, mappings) = parsing::parse(input);
let seed_ranges = combine_single_to_ranges_for_seeds(&single_seeds);
(seed_ranges, mappings)
}; };
current_position = range_to_use.map_position(current_position); "".to_string()
}
fn combine_single_to_ranges_for_seeds(single_seeds: &[UnsignedNumber]) -> Vec<SeedRange> {
assert!(single_seeds.len() % 2 == 0);
single_seeds
.chunks(2)
.into_iter()
.map(|array| match array {
[start, end] => {
let start = *start;
let range = end - 1;
let end = start + range;
SeedRange::new(start, end)
} }
current_position _ => unreachable!(),
})
.collect()
} }
#[cfg(test)] #[cfg(test)]
mod testing { mod testing {
use super::*; use crate::solutions::day5::{
combine_single_to_ranges_for_seeds, parsing, seed_range::SeedRange, solve_task_2,
};
use super::solve_task_1;
const EXAMPLE_INPUT: &str = include_str!("day5/day_example_input.txt");
// Seed 79, soil 81, fertilizer 81, water 81, light 74, temperature 78, humidity 78, location 82.
// Seed 14, soil 14, fertilizer 53, water 49, light 42, temperature 42, humidity 43, location 43.
// Seed 55, soil 57, fertilizer 57, water 53, light 46, temperature 82, humidity 82, location 86.
// Seed 13, soil 13, fertilizer 52, water 41, light 34, temperature 34, humidity 35, location 35.
#[test] #[test]
fn name() { fn should_solve_task_1() {
fn assert_case(seeds: UnsignedNumber, layer: &[MappingLayer], expected: UnsignedNumber) { let actual = solve_task_1(EXAMPLE_INPUT);
let actual = location_of_one_point(seeds, &layer); let expected = "35";
assert_eq!(expected, actual, "Given seeds: {}", seeds); assert_eq!(expected, actual)
} }
let input = parsing::parse(include_str!("day5/day5_example_input.txt"));
let layers = &input.1; #[test]
assert_case(79, layers, 82); fn should_combine_single_to_ranges_for_seeds() {
assert_case(14, layers, 43); let (single_seeds, _) = parsing::parse(EXAMPLE_INPUT);
assert_case(55, layers, 86); let actual = combine_single_to_ranges_for_seeds(&single_seeds);
assert_case(13, layers, 35); let expected = vec![SeedRange::new(79, 92), SeedRange::new(55, 67)];
assert_eq!(expected, actual)
}
#[test]
fn should_solve_task2() {
let actual = solve_task_2(EXAMPLE_INPUT);
let expected = "46";
assert_eq!(expected, actual)
} }
} }

View file

@ -0,0 +1,118 @@
use std::str::FromStr;
use thiserror::Error;
use super::{seed_range::SeedRange, UnsignedNumber};
#[derive(Debug, PartialEq, Eq)]
pub struct ItemMapping {
source_range: SeedRange,
target: UnsignedNumber,
range: UnsignedNumber,
}
impl ItemMapping {
pub fn map_point(&self, to_map: UnsignedNumber) -> Option<UnsignedNumber> {
let source_range = self.source_range;
let start = source_range.start();
let end = source_range.end();
if start <= to_map && end >= to_map {
let difference = to_map - start;
let mapped = self.target + difference;
Some(mapped)
} else {
None
}
}
}
#[derive(Debug, Error, PartialEq, Eq)]
#[error("Item mapping must be exactly 3 numbers")]
pub struct InvalidStrItemMapping;
impl FromStr for ItemMapping {
type Err = InvalidStrItemMapping;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut splitted = s.split_whitespace().take(3).map(|to_parse| {
to_parse
.parse::<UnsignedNumber>()
.map_err(|_| InvalidStrItemMapping)
});
match (splitted.next(), splitted.next(), splitted.next()) {
(Some(Ok(target)), Some(Ok(source)), Some(Ok(range))) => {
let inclusive_range = range.saturating_sub(1);
let end = source + inclusive_range;
let source_range = SeedRange::new(source, end);
let parsed_self = Self {
source_range,
target,
range,
};
Ok(parsed_self)
}
(_, _, _) => Err(InvalidStrItemMapping),
}
}
}
#[cfg(test)]
mod testing {
use crate::solutions::day5::{
item_mapping::{InvalidStrItemMapping, ItemMapping},
seed_range::SeedRange,
UnsignedNumber,
};
#[test]
fn from_str_item_mapping() {
fn assert_case(input: &str, expected: Result<ItemMapping, InvalidStrItemMapping>) {
let actual = input.parse();
assert_eq!(expected, actual, "Input: {}", input);
}
assert_case(
"20 10 22",
Ok(ItemMapping {
source_range: SeedRange::new(10, 31),
target: 20,
range: 22,
}),
);
assert_case(
"50 98 2",
Ok(ItemMapping {
source_range: SeedRange::new(98, 99),
target: 50,
range: 2,
}),
);
assert_case(
"52 50 48",
Ok(ItemMapping {
source_range: SeedRange::new(50, 97),
target: 52,
range: 48,
}),
);
assert_case("a 10 22", Err(InvalidStrItemMapping));
assert_case("10 10", Err(InvalidStrItemMapping));
}
#[test]
fn should_map_point() {
fn assert_case(point: UnsignedNumber, input: &str, expected: Option<UnsignedNumber>) {
let mapping: ItemMapping = input.parse().unwrap();
let actual = mapping.map_point(point);
assert_eq!(
expected, actual,
"Point: {:?}\nMaping: {:?}",
point, mapping
);
}
assert_case(2, "4 2 2", Some(4));
assert_case(0, "4 2 2", None);
assert_case(4, "4 2 2", None);
assert_case(79, "52 50 48", Some(81));
}
}

View file

@ -1,69 +0,0 @@
use derive_more::derive;
use super::range_mapping::RangeMapping;
#[derive(Debug)]
pub struct MappingLayer {
label: String,
ranges: Vec<RangeMapping>,
}
impl MappingLayer {
pub fn new(label: impl Into<String>, mut ranges: Vec<RangeMapping>) -> Self {
ranges.sort_by_key(|element| element.source());
let ranges = Self::fill_in_the_gaps(ranges);
Self {
label: label.into(),
ranges,
}
}
/// # Expected
/// It is assumed that ranges is sorted by the field `source`
pub fn fill_in_the_gaps(ranges: Vec<RangeMapping>) -> Vec<RangeMapping> {
let mut current_source_start = 0;
let mut without_gaps: Vec<RangeMapping> = Vec::new();
for next_range in ranges {
let next_source = next_range.source();
assert!(current_source_start <= next_source);
if current_source_start == next_source {
current_source_start = next_range.inclusive_end_source().saturating_add(1);
without_gaps.push(next_range);
} else {
let new_range = next_source;
without_gaps.push(RangeMapping::just_with_source(
current_source_start,
new_range,
));
current_source_start = next_range.inclusive_end_source() + 1;
without_gaps.push(next_range);
}
}
without_gaps.push(RangeMapping::source_to_max(current_source_start));
without_gaps
}
pub fn ranges(&self) -> &[RangeMapping] {
&self.ranges
}
}
#[cfg(test)]
mod testing {
use crate::solutions::day5::{mapping_layer::MappingLayer, range_mapping::RangeMapping};
#[test]
fn should_fill_in_the_gaps() {
let input = vec![
RangeMapping::new(45, 81, 19),
RangeMapping::new(64, 68, 13),
RangeMapping::new(77, 45, 23),
RangeMapping::new(110, 55, 20),
];
let actual = MappingLayer::fill_in_the_gaps(input);
insta::assert_debug_snapshot!(actual);
}
}

View file

@ -1,46 +1,38 @@
use crate::{parsing_utils, solutions::day5::range_mapping::RangeMapping}; use crate::parsing_utils;
use super::{mapping_layer::MappingLayer, UnsignedNumber}; use super::{item_mapping::ItemMapping, UnsignedNumber};
pub fn parse(input: &str) -> (Vec<UnsignedNumber>, Vec<MappingLayer>) { pub fn parse(input: &str) -> (Vec<UnsignedNumber>, Vec<Vec<ItemMapping>>) {
fn parse_one_layer(lines: Vec<&str>) -> MappingLayer {
const ALREADY_GOT_MAPPING_NAME: usize = 1;
let mapping_name = *lines.first().unwrap();
let mut mappings = Vec::new();
for next_line in lines.into_iter().skip(ALREADY_GOT_MAPPING_NAME) {
let next_mapping: RangeMapping = next_line.parse().unwrap();
mappings.push(next_mapping);
}
MappingLayer::new(mapping_name.to_string(), mappings)
}
let mut blocks = parsing_utils::blocks_of_lines_seperated_by_empty_lines(input); let mut blocks = parsing_utils::blocks_of_lines_seperated_by_empty_lines(input);
let seeds = {
let first_block = blocks.next().unwrap(); let first_block = blocks.next().unwrap();
let first_line = first_block.first().unwrap();
assert!(first_block.len() == 1); first_line
let seeds = first_block .strip_prefix("seeds:")
.first()
.unwrap() .unwrap()
.trim_start_matches("seeds: ") .split_whitespace()
.split(" ") .map(|not_parsed| not_parsed.parse::<UnsignedNumber>().unwrap())
.map(|to_number| to_number.parse().unwrap()) .collect()
.collect(); };
let mappings = blocks.into_iter().map(parse_one_layer).collect();
let mappings: Vec<Vec<ItemMapping>> = blocks
.map(|lines| {
lines
.into_iter()
.skip(1)
.map(|to_parse| to_parse.parse::<ItemMapping>().unwrap())
.collect()
})
.collect();
(seeds, mappings) (seeds, mappings)
} }
#[cfg(test)] #[cfg(test)]
mod testing { mod testing {
use crate::solutions::day5::UnsignedNumber;
#[test] #[test]
fn parse_input_day5() { fn should_parse() {
let input = include_str!("day5_example_input.txt"); let input = include_str!("day_example_input.txt");
let actual = super::parse(input); let actual = super::parse(input);
let expected_seeds: Vec<UnsignedNumber> = vec![79, 14, 55, 13]; insta::assert_debug_snapshot!(actual);
assert_eq!(expected_seeds, actual.0);
insta::assert_debug_snapshot!(actual.1);
} }
} }

View file

@ -1,156 +0,0 @@
use std::{str::FromStr, u32};
use thiserror::Error;
use super::UnsignedNumber;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct RangeMapping {
source: UnsignedNumber,
target: UnsignedNumber,
range: UnsignedNumber,
}
#[derive(Debug, Error)]
pub enum ParseErrorRangeMapping {
#[error("Needs to have at least 3 numbers")]
LessThan3Numbers,
#[error("Every item must be a valid unsigned number")]
InvalidFormatForNumbers,
}
impl FromStr for RangeMapping {
type Err = ParseErrorRangeMapping;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut numbers_as_3 = s.split(" ").take(3).map(|unparsed| {
unparsed
.parse::<UnsignedNumber>()
.map_err(|_| ParseErrorRangeMapping::InvalidFormatForNumbers)
});
let (target, source, range) = (
numbers_as_3
.next()
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
numbers_as_3
.next()
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
numbers_as_3
.next()
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
);
Ok(Self {
target,
source,
range,
})
}
}
impl RangeMapping {
pub fn new(source: UnsignedNumber, target: UnsignedNumber, range: UnsignedNumber) -> Self {
Self {
source,
target,
range,
}
}
pub fn just_with_source(source: UnsignedNumber, range: UnsignedNumber) -> Self {
Self {
source,
target: source,
range,
}
}
pub fn source_to_max(source: UnsignedNumber) -> Self {
const ENSURES_SOURCE_END_IS_MAX: UnsignedNumber = 1;
let range = if source == 0 {
UnsignedNumber::MAX
} else {
UnsignedNumber::MAX - (source - ENSURES_SOURCE_END_IS_MAX)
};
Self::just_with_source(source, range)
}
pub fn source(&self) -> UnsignedNumber {
self.source
}
pub fn inclusive_end_source(&self) -> UnsignedNumber {
if self.range() == UnsignedNumber::MAX {
UnsignedNumber::MAX
} else {
self.source() + (self.range() - 1)
}
}
pub fn map_position(&self, position: UnsignedNumber) -> UnsignedNumber {
let inclusive_end_source = self.inclusive_end_source();
if self.source() > position || position > inclusive_end_source {
panic!(
"Given position ({}) is outside of this range between ({}) and ({})",
position,
self.source(),
inclusive_end_source
);
}
let difference = position - self.source();
self.target() + difference
}
pub fn target(&self) -> UnsignedNumber {
self.target
}
pub fn range(&self) -> UnsignedNumber {
self.range
}
}
#[cfg(test)]
mod testing {
use std::u32;
use super::*;
#[test]
fn parse_from_line() {
const INPUT: &str = "50 98 2";
let expected = RangeMapping {
source: 98,
target: 50,
range: 2,
};
let actual: RangeMapping = INPUT.parse().unwrap();
assert_eq!(expected, actual);
}
#[test]
fn calc_inclusive_end_source() {
fn assert_case(given: RangeMapping, expected: UnsignedNumber) {
let actual = given.inclusive_end_source();
assert_eq!(expected, actual, "Given: {:#?}", given);
}
assert_case(RangeMapping::new(98, 50, 2), 99);
assert_case(RangeMapping::new(0, 50, 1), 0);
assert_case(RangeMapping::new(10, 50, 5), 14);
}
#[test]
fn create_from_source_to_max_integer_max_correctly() {
fn assert_case(input: RangeMapping) {
const EXPECTED: UnsignedNumber = UnsignedNumber::MAX;
let actual = input.inclusive_end_source();
assert_eq!(EXPECTED, actual, "Given: {:#?}", input);
}
assert_case(RangeMapping::source_to_max(UnsignedNumber::MAX));
assert_case(RangeMapping::source_to_max(UnsignedNumber::MAX - 1));
assert_case(RangeMapping::source_to_max(100));
assert_case(RangeMapping::source_to_max(2));
assert_case(RangeMapping::source_to_max(0));
}
}

View file

@ -0,0 +1,21 @@
use super::UnsignedNumber;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct SeedRange {
start: UnsignedNumber,
end: UnsignedNumber,
}
impl SeedRange {
pub fn new(start: UnsignedNumber, end: UnsignedNumber) -> Self {
Self { start, end }
}
pub fn start(&self) -> UnsignedNumber {
self.start
}
pub fn end(&self) -> UnsignedNumber {
self.end
}
}

View file

@ -1,41 +0,0 @@
---
source: src/solutions/day5/mapping_layer.rs
expression: actual
---
[
RangeMapping {
source: 0,
target: 0,
range: 45,
},
RangeMapping {
source: 45,
target: 81,
range: 19,
},
RangeMapping {
source: 64,
target: 68,
range: 13,
},
RangeMapping {
source: 77,
target: 45,
range: 23,
},
RangeMapping {
source: 100,
target: 100,
range: 110,
},
RangeMapping {
source: 110,
target: 55,
range: 20,
},
RangeMapping {
source: 130,
target: 130,
range: 18446744073709551486,
},
]

View file

@ -1,186 +0,0 @@
---
source: src/solutions/day5/parsing.rs
expression: actual.1
---
[
MappingLayer {
label: "seed-to-soil map:",
ranges: [
RangeMapping {
source: 0,
target: 0,
range: 50,
},
RangeMapping {
source: 50,
target: 52,
range: 48,
},
RangeMapping {
source: 98,
target: 50,
range: 2,
},
RangeMapping {
source: 100,
target: 100,
range: 18446744073709551516,
},
],
},
MappingLayer {
label: "soil-to-fertilizer map:",
ranges: [
RangeMapping {
source: 0,
target: 39,
range: 15,
},
RangeMapping {
source: 15,
target: 0,
range: 37,
},
RangeMapping {
source: 52,
target: 37,
range: 2,
},
RangeMapping {
source: 54,
target: 54,
range: 18446744073709551562,
},
],
},
MappingLayer {
label: "fertilizer-to-water map:",
ranges: [
RangeMapping {
source: 0,
target: 42,
range: 7,
},
RangeMapping {
source: 7,
target: 57,
range: 4,
},
RangeMapping {
source: 11,
target: 0,
range: 42,
},
RangeMapping {
source: 53,
target: 49,
range: 8,
},
RangeMapping {
source: 61,
target: 61,
range: 18446744073709551555,
},
],
},
MappingLayer {
label: "water-to-light map:",
ranges: [
RangeMapping {
source: 0,
target: 0,
range: 18,
},
RangeMapping {
source: 18,
target: 88,
range: 7,
},
RangeMapping {
source: 25,
target: 18,
range: 70,
},
RangeMapping {
source: 95,
target: 95,
range: 18446744073709551521,
},
],
},
MappingLayer {
label: "light-to-temperature map:",
ranges: [
RangeMapping {
source: 0,
target: 0,
range: 45,
},
RangeMapping {
source: 45,
target: 81,
range: 19,
},
RangeMapping {
source: 64,
target: 68,
range: 13,
},
RangeMapping {
source: 77,
target: 45,
range: 23,
},
RangeMapping {
source: 100,
target: 100,
range: 18446744073709551516,
},
],
},
MappingLayer {
label: "temperature-to-humidity map:",
ranges: [
RangeMapping {
source: 0,
target: 1,
range: 69,
},
RangeMapping {
source: 69,
target: 0,
range: 1,
},
RangeMapping {
source: 70,
target: 70,
range: 18446744073709551546,
},
],
},
MappingLayer {
label: "humidity-to-location map:",
ranges: [
RangeMapping {
source: 0,
target: 0,
range: 56,
},
RangeMapping {
source: 56,
target: 60,
range: 37,
},
RangeMapping {
source: 93,
target: 56,
range: 4,
},
RangeMapping {
source: 97,
target: 97,
range: 18446744073709551519,
},
],
},
]

View file

@ -0,0 +1,172 @@
---
source: src/solutions/day5/parsing.rs
expression: actual
---
(
[
79,
14,
55,
13,
],
[
[
ItemMapping {
source_range: SeedRange {
start: 98,
end: 99,
},
target: 50,
range: 2,
},
ItemMapping {
source_range: SeedRange {
start: 50,
end: 97,
},
target: 52,
range: 48,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 15,
end: 51,
},
target: 0,
range: 37,
},
ItemMapping {
source_range: SeedRange {
start: 52,
end: 53,
},
target: 37,
range: 2,
},
ItemMapping {
source_range: SeedRange {
start: 0,
end: 14,
},
target: 39,
range: 15,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 53,
end: 60,
},
target: 49,
range: 8,
},
ItemMapping {
source_range: SeedRange {
start: 11,
end: 52,
},
target: 0,
range: 42,
},
ItemMapping {
source_range: SeedRange {
start: 0,
end: 6,
},
target: 42,
range: 7,
},
ItemMapping {
source_range: SeedRange {
start: 7,
end: 10,
},
target: 57,
range: 4,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 18,
end: 24,
},
target: 88,
range: 7,
},
ItemMapping {
source_range: SeedRange {
start: 25,
end: 94,
},
target: 18,
range: 70,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 77,
end: 99,
},
target: 45,
range: 23,
},
ItemMapping {
source_range: SeedRange {
start: 45,
end: 63,
},
target: 81,
range: 19,
},
ItemMapping {
source_range: SeedRange {
start: 64,
end: 76,
},
target: 68,
range: 13,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 69,
end: 69,
},
target: 0,
range: 1,
},
ItemMapping {
source_range: SeedRange {
start: 0,
end: 68,
},
target: 1,
range: 69,
},
],
[
ItemMapping {
source_range: SeedRange {
start: 56,
end: 92,
},
target: 60,
range: 37,
},
ItemMapping {
source_range: SeedRange {
start: 93,
end: 96,
},
target: 56,
range: 4,
},
],
],
)

129
src/solutions/day5/task2.rs Normal file
View file

@ -0,0 +1,129 @@
use std::collections::HashMap;
use crate::{
iterations::{AdventIterator, Chunck},
solutions::day5::parsing,
};
use super::{
mapping_layer::MappingLayer,
range_mapping::{MappedSeedRange, RangeMapping},
seed_range::SeedRange,
UnsignedNumber,
};
pub fn solve_task_2(input: &str) -> String {
let (seeds, layers) = parsing::parse(input);
let seed_ranges = combine_seeds_to_ranges(seeds);
let mut min = UnsignedNumber::MAX;
for next in seed_ranges {
let loacal_min = simuluate_one_seed_range(next, &layers);
min = min.min(loacal_min);
}
min.to_string()
}
fn simuluate_one_seed_range(seed_range: SeedRange, layers: &[MappingLayer]) -> UnsignedNumber {
let mut current_branches = vec![seed_range];
for next in layers {
let mut cache = CacheOfMappedRanges::new();
let ranges = next.ranges();
#[cfg(debug_assertions)]
{
let mut sorted: Vec<UnsignedNumber> = current_branches
.iter()
.copied()
.map(|e| e.start())
.collect();
let unsorted = sorted.clone();
sorted.sort();
assert_eq!(unsorted, sorted);
}
for next_seed_range in current_branches {
map_sequence(next_seed_range, ranges, &mut cache);
}
current_branches = cache
.into_iter()
.map(|(start, end)| SeedRange::new(start, end))
.collect();
current_branches.sort_by_key(|not_the_key| not_the_key.start());
}
let min_location = current_branches
.into_iter()
.min_by_key(|seed_range| seed_range.start())
.unwrap();
min_location.start()
}
type CacheOfMappedRanges = HashMap<UnsignedNumber, UnsignedNumber>;
fn map_sequence(
seed_range: SeedRange,
mapping: &[RangeMapping],
found_mapping: &mut CacheOfMappedRanges,
) -> Vec<SeedRange> {
fn add_to_cache(range: SeedRange, cache: &mut CacheOfMappedRanges) {
let value = range.end();
cache
.entry(range.start())
.and_modify(|current_max| *current_max = (*current_max).max(value))
.or_insert(value);
}
#[cfg(debug_assertions)]
{
let last = mapping.last().unwrap();
assert!(last.source() == last.target(), "last: {:?}", last);
let to_test_for_sorting = mapping
.into_iter()
.map(|e| e.source())
.collect::<Vec<UnsignedNumber>>();
let is_sorted = crate::sequences::is_sorted(to_test_for_sorting.as_slice());
assert!(is_sorted);
}
let mut new_branches = Vec::new();
let mut currend_seed_range = seed_range;
for next in mapping {
match next.map_range(currend_seed_range) {
MappedSeedRange::OutsideFromLeft => (),
MappedSeedRange::Completely(done) => {
add_to_cache(done, found_mapping);
new_branches.push(done);
return new_branches;
}
MappedSeedRange::Partially { mapped, left } => {
add_to_cache(mapped, found_mapping);
new_branches.push(mapped);
currend_seed_range = left;
}
}
}
unreachable!();
}
fn combine_seeds_to_ranges(seeds: Vec<UnsignedNumber>) -> Vec<SeedRange> {
seeds
.into_iter()
.into_chunks::<2>()
.map(|next_chunk| match next_chunk {
Chunck::Next([start, range]) => {
let end = start + range - 1;
SeedRange::new(start, end)
}
Chunck::Rest(_) => panic!("Uneven number of seeds"),
})
.collect()
}
#[cfg(test)]
mod testing {
use crate::solutions::day5::solve_task_2;
#[test]
fn task2() {
let actual = solve_task_2(include_str!("day5_example_input.txt"));
assert_eq!("46", actual);
}
}