2023 day5/rust: add solution
This commit is contained in:
parent
61b478ca0e
commit
b1e402180f
4 changed files with 180 additions and 0 deletions
9
2023/day5/rust/Cargo.toml
Normal file
9
2023/day5/rust/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "rust_2023_05"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
aoc = { path = "../../../common/rust" }
|
163
2023/day5/rust/src/main.rs
Normal file
163
2023/day5/rust/src/main.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
|
||||||
|
use std::{io::stdin, ops::ControlFlow};
|
||||||
|
|
||||||
|
use aoc::{SectionRange, UpToTwo};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct MapRange {
|
||||||
|
source: SectionRange<usize>,
|
||||||
|
dest_start: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapRange {
|
||||||
|
/// Returns overlapping (mapped) and non-overlapping (unmapped) ranges
|
||||||
|
pub fn map(
|
||||||
|
&self,
|
||||||
|
source_range: SectionRange<usize>,
|
||||||
|
) -> (Option<SectionRange<usize>>, UpToTwo<SectionRange<usize>>) {
|
||||||
|
if let Some(overlap) = self.source & source_range {
|
||||||
|
let non_overlapping = source_range - overlap;
|
||||||
|
let overlap_offset = overlap
|
||||||
|
.offset_by_neg(*self.source.start())
|
||||||
|
.offset_by(self.dest_start);
|
||||||
|
|
||||||
|
(Some(overlap_offset), non_overlapping)
|
||||||
|
} else {
|
||||||
|
(None, UpToTwo::One(source_range))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(s: &str) -> Self {
|
||||||
|
let &[dest_start, source_start, len] = s
|
||||||
|
.split(' ')
|
||||||
|
.map(|n| n.parse().unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.as_slice()
|
||||||
|
else {
|
||||||
|
panic!("Invalid input: {s}");
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
source: SectionRange::try_new(source_start, source_start + len - 1).unwrap(),
|
||||||
|
dest_start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IdMapping {
|
||||||
|
ranges: Vec<MapRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdMapping {
|
||||||
|
pub fn new(ranges: Vec<MapRange>) -> Self {
|
||||||
|
for window in ranges.windows(2) {
|
||||||
|
let [first, second] = window else { panic!() };
|
||||||
|
assert!(
|
||||||
|
(first.source & second.source).is_none(),
|
||||||
|
"source ranges overlap"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Self { ranges }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(&self, source_range: SectionRange<usize>) -> Vec<SectionRange<usize>> {
|
||||||
|
let result = self.ranges.iter().try_fold(
|
||||||
|
(vec![], vec![source_range]),
|
||||||
|
|(mut mapped, unmapped), range| {
|
||||||
|
if unmapped.is_empty() {
|
||||||
|
return ControlFlow::Break(mapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut remaining_unmapped = Vec::new();
|
||||||
|
for non_mapped in unmapped {
|
||||||
|
let (overlapping, non_overlapping) = range.map(non_mapped);
|
||||||
|
mapped.extend(overlapping);
|
||||||
|
remaining_unmapped.extend(non_overlapping);
|
||||||
|
}
|
||||||
|
ControlFlow::Continue((mapped, remaining_unmapped))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
ControlFlow::Continue((mut mapped, unmapped)) => {
|
||||||
|
// unmapped ranges stay as-is
|
||||||
|
mapped.extend(unmapped);
|
||||||
|
mapped
|
||||||
|
}
|
||||||
|
ControlFlow::Break(mapped) => mapped,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_ranges_once(
|
||||||
|
ranges: &[SectionRange<usize>],
|
||||||
|
mapping: &IdMapping,
|
||||||
|
) -> Vec<SectionRange<usize>> {
|
||||||
|
ranges
|
||||||
|
.iter()
|
||||||
|
.flat_map(|&range| mapping.map(range))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_ranges_completely(
|
||||||
|
init_ranges: Vec<SectionRange<usize>>,
|
||||||
|
mappings: &[IdMapping],
|
||||||
|
) -> Vec<SectionRange<usize>> {
|
||||||
|
mappings
|
||||||
|
.iter()
|
||||||
|
.fold(init_ranges, |ranges, map| map_ranges_once(&ranges, map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn minimum_result(init_ranges: Vec<SectionRange<usize>>, mappings: &[IdMapping]) -> usize {
|
||||||
|
*map_ranges_completely(init_ranges, mappings)
|
||||||
|
.iter()
|
||||||
|
.min()
|
||||||
|
.unwrap()
|
||||||
|
.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut lines = stdin().lines().map(Result::unwrap);
|
||||||
|
|
||||||
|
let seeds: Vec<usize> = lines
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.split(' ')
|
||||||
|
.skip(1)
|
||||||
|
.map(|n| n.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut maps = Vec::new();
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
if line.is_empty() {
|
||||||
|
lines.next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ranges = lines
|
||||||
|
.by_ref()
|
||||||
|
.take_while(|l| !l.is_empty())
|
||||||
|
.map(|l| MapRange::parse(&l))
|
||||||
|
.collect();
|
||||||
|
maps.push(IdMapping::new(ranges));
|
||||||
|
}
|
||||||
|
|
||||||
|
let single_seeds: Vec<_> = seeds
|
||||||
|
.iter()
|
||||||
|
.map(|&seed| SectionRange::try_new(seed, seed).unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("{}", minimum_result(single_seeds, &maps));
|
||||||
|
|
||||||
|
let seed_ranges: Vec<_> = seeds
|
||||||
|
.chunks(2)
|
||||||
|
.map(|pair| {
|
||||||
|
let &[start, len] = pair else {
|
||||||
|
panic!("Not a pair: {pair:?}");
|
||||||
|
};
|
||||||
|
SectionRange::try_new(start, start + len - 1).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
println!("{}", minimum_result(seed_ranges, &maps));
|
||||||
|
}
|
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -663,6 +663,13 @@ dependencies = [
|
||||||
"enum-map",
|
"enum-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust_2023_05"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
|
|
@ -29,4 +29,5 @@ members = [
|
||||||
"2022/day18/rust",
|
"2022/day18/rust",
|
||||||
"2023/day1/rust",
|
"2023/day1/rust",
|
||||||
"2023/day2/rust",
|
"2023/day2/rust",
|
||||||
|
"2023/day5/rust",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue