141 lines
3.8 KiB
Rust
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));
|
|
}
|
|
}
|