Restarted day 5
Implemented parsing for task 1 and 2 Implemented solution for task 1
This commit is contained in:
parent
79b55bd23a
commit
4c093353cf
17 changed files with 1092 additions and 537 deletions
0
3
Normal file
0
3
Normal file
451
output.txt
Normal file
451
output.txt
Normal 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] ¤t_branches = [
|
||||||
|
SeedRange {
|
||||||
|
start: 1742065688,
|
||||||
|
end: 1750102221,
|
||||||
|
},
|
||||||
|
SeedRange {
|
||||||
|
start: 2567370277,
|
||||||
|
end: 2599723683,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
[src/solutions/day5/task2.rs:42:5] ¤t_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] ¤t_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] ¤t_branches = [
|
||||||
|
SeedRange {
|
||||||
|
start: 475181169,
|
||||||
|
end: 490368072,
|
||||||
|
},
|
||||||
|
SeedRange {
|
||||||
|
start: 836186785,
|
||||||
|
end: 843842518,
|
||||||
|
},
|
||||||
|
SeedRange {
|
||||||
|
start: 2734840928,
|
||||||
|
end: 2738910872,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
[src/solutions/day5/task2.rs:42:5] ¤t_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] ¤t_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] ¤t_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] ¤t_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] ¤t_branches = [
|
||||||
|
SeedRange {
|
||||||
|
start: 1672236638,
|
||||||
|
end: 1707764533,
|
||||||
|
},
|
||||||
|
SeedRange {
|
||||||
|
start: 2064249380,
|
||||||
|
end: 2073429343,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
[src/solutions/day5/task2.rs:42:5] ¤t_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
70
src/iterations.rs
Normal 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])],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
26
src/sequences.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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],
|
||||||
|
|
|
@ -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(¤t_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()
|
||||||
}
|
}
|
||||||
current_position
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
_ => 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
118
src/solutions/day5/item_mapping.rs
Normal file
118
src/solutions/day5/item_mapping.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
21
src/solutions/day5/seed_range.rs
Normal file
21
src/solutions/day5/seed_range.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
|
@ -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
129
src/solutions/day5/task2.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue