2023 day5/rust: add solution

This commit is contained in:
Xiretza 2023-12-05 19:39:27 +00:00
parent 61b478ca0e
commit b1e402180f
4 changed files with 180 additions and 0 deletions

View 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
View 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
View file

@ -663,6 +663,13 @@ dependencies = [
"enum-map",
]
[[package]]
name = "rust_2023_05"
version = "0.1.0"
dependencies = [
"aoc",
]
[[package]]
name = "rustversion"
version = "1.0.11"

View file

@ -29,4 +29,5 @@ members = [
"2022/day18/rust",
"2023/day1/rust",
"2023/day2/rust",
"2023/day5/rust",
]