advent-of-code/2023/day5/src/main.rs

161 lines
4.0 KiB
Rust

use std::collections::HashMap;
use std::fs;
#[derive(Debug)]
struct Range {
input: usize,
output: usize,
offset: usize,
}
#[derive(Debug)]
struct Map {
ranges: Vec<Range>,
next: String,
}
fn parse() -> Option<(Vec<usize>, HashMap<String, Map>)> {
// let contents = fs::read_to_string("./day5.txt").expect("Don't find the file");
let contents = fs::read_to_string("./input.txt").expect("Don't find the file");
let mut seeds: Vec<usize> = vec![];
let mut maps: HashMap<String, Map> = HashMap::new();
let segments: Vec<&str> = contents.split("\n\n").collect();
match segments[..] {
[s, ..] => {
let segments: Vec<&str> = s.split(':').collect();
match segments[..] {
[_, s] => {
s.split_whitespace()
.map(|x| x.parse::<usize>().unwrap())
.collect::<Vec<_>>()
.iter()
.for_each(|s| seeds.push(*s));
}
_ => {
return None;
}
}
}
_ => {
return None;
}
}
for seg in &segments[1..] {
let mut ranges: Vec<Range> = vec![];
let segments: Vec<&str> = seg.split('\n').collect();
let Some((names, r)) = segments.split_first() else {
return None;
};
let names = names.replace(' ', "-");
let split = names.split('-').collect::<Vec<_>>();
let [name, _, next, _] = &split[..] else {
return None;
};
for range in r {
if range.is_empty() {
continue;
}
let split = range
.split_whitespace()
.map(|x| x.parse::<usize>().unwrap())
.collect::<Vec<_>>();
match split[..] {
[output, input, off] => ranges.push(Range {
input,
output,
offset: off,
}),
_ => return None,
}
}
maps.insert(
name.to_string(),
Map {
ranges,
next: next.to_string(),
},
);
}
// println!("Seeds : {:?}", seeds);
// println!("Ranges : {:#?}", maps);
Some((seeds, maps))
}
fn check_range(seed: usize, map: &Map, maps: &HashMap<String, Map>) -> usize {
for range in &map.ranges {
let input = range.input;
let output = range.output;
let offset = range.offset;
let min = input;
let max = input + offset;
if min <= seed && seed < max {
let o = seed + output - input;
let Some(m) = maps.get(&map.next) else {
return o;
};
return check_range(o, m, maps);
}
}
let Some(m) = maps.get(&map.next) else {
return seed;
};
check_range(seed, m, maps)
}
fn puzzle1(seeds: &Vec<usize>, maps: &HashMap<String, Map>) {
let mut s: Vec<usize> = vec![];
let map = maps.get("seed").unwrap();
for seed in seeds {
s.push(check_range(*seed, map, maps));
}
// println!(" Seeds → : {:?}", s);
let min = s.iter().min().unwrap();
println!("Puzzle 1 : {:?}", min);
}
fn puzzle2(seeds: Vec<usize>, maps: HashMap<String, Map>) {
let map = maps.get("seed").unwrap();
let ranges: Vec<_> = seeds
.chunks(2)
.map(|i| std::ops::Range {
start: i[0],
end: i[0] + i[1],
})
.collect();
let min: Vec<_> = ranges
.iter()
.map(|range| {
range
.clone()
.map(|r| check_range(r, map, &maps))
.min()
.unwrap()
})
.collect::<Vec<_>>();
let min = min.iter().min().unwrap();
println!("Puzzle 2 : {:?}", min);
}
fn main() {
let Some((seeds, maps)) = parse() else {
panic!("Fail input parsing")
};
puzzle1(&seeds, &maps);
puzzle2(seeds, maps);
}