1
0
Fork 0
advent-of-code/2023/src/bin/05.rs

141 lines
3.8 KiB
Rust

advent_of_code::solution!(5);
use std::ops::RangeInclusive;
use std::str::Lines;
struct RangeMap {
source: RangeInclusive<u64>,
destination: RangeInclusive<u64>,
}
struct Almanac {
seeds: Vec<u64>,
seed_to_soil: Vec<RangeMap>,
soil_to_fertilizer: Vec<RangeMap>,
fertilizer_to_water: Vec<RangeMap>,
water_to_light: Vec<RangeMap>,
light_to_temperature: Vec<RangeMap>,
temperature_to_humidity: Vec<RangeMap>,
humidity_to_location: Vec<RangeMap>,
}
fn line_to_triple(input: &str) -> [u64; 3] {
let mut vals: [u64; 3] = [0; 3];
for (index, i) in input.split(" ").enumerate() {
vals[index] = i.parse::<u64>().unwrap();
}
vals
}
fn next_rangemap(lines: &mut Lines) -> Vec<RangeMap> {
let mut rm: Vec<RangeMap> = vec![];
while let Some(line) = lines.next() {
if line.is_empty() {
break;
}
let triple = line_to_triple(&line);
rm.push(RangeMap {
source: triple[1]..=(triple[1] + triple[2]),
destination: triple[0]..=(triple[0] + triple[2]),
});
}
rm
}
fn parse(input: &str) -> Almanac {
let mut lines = input.lines();
let seeds = lines.next().unwrap().split(": ").collect::<Vec<&str>>()[1]
.split(" ")
.map(|s| s.parse::<u64>().unwrap())
.collect();
lines.next();
lines.next();
let seed_to_soil = next_rangemap(&mut lines);
lines.next();
let soil_to_fertilizer = next_rangemap(&mut lines);
lines.next();
let fertilizer_to_water = next_rangemap(&mut lines);
lines.next();
let water_to_light = next_rangemap(&mut lines);
lines.next();
let light_to_temperature = next_rangemap(&mut lines);
lines.next();
let temperature_to_humidity = next_rangemap(&mut lines);
lines.next();
let humidity_to_location = next_rangemap(&mut lines);
Almanac {
seeds,
seed_to_soil,
soil_to_fertilizer,
fertilizer_to_water,
water_to_light,
light_to_temperature,
temperature_to_humidity,
humidity_to_location,
}
}
fn match_to(s: u64, maps: &Vec<RangeMap>) -> u64 {
if let Some(rm) = maps.iter().find(|m| m.source.contains(&s)) {
let offset = s - rm.source.start();
rm.destination.start() + offset
} else {
s
}
}
fn min_location(seeds: &Vec<u64>, input: &Almanac) -> u64 {
let locations = seeds
.iter()
.map(|s| match_to(*s, &input.seed_to_soil))
.map(|s| match_to(s, &input.soil_to_fertilizer))
.map(|s| match_to(s, &input.fertilizer_to_water))
.map(|s| match_to(s, &input.water_to_light))
.map(|s| match_to(s, &input.light_to_temperature))
.map(|s| match_to(s, &input.temperature_to_humidity))
.map(|s| match_to(s, &input.humidity_to_location))
.collect::<Vec<u64>>();
*locations.iter().min().unwrap()
}
pub fn part_one(input_str: &str) -> Option<u64> {
let input = parse(input_str);
Some(min_location(&input.seeds, &input))
}
pub fn part_two(input_str: &str) -> Option<u64> {
let input = parse(input_str);
let mut seeds: Vec<u64> = vec![];
let mut index = 0;
while index < input.seeds.len() {
let start = input.seeds[index];
let count = input.seeds[index + 1];
index += 2;
for n in start..(start + count) {
seeds.push(n);
}
}
Some(min_location(&seeds, &input))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(35));
}
#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(46));
}
}