AoC-2023/src/days/day5.rs

228 lines
7.7 KiB
Rust

use std::{path::Path, str::FromStr};
use crate::{error::AdventError, input::read_into};
#[derive(Debug)]
struct RangeMap {
start: u32,
dest: u32,
length: u32,
}
impl RangeMap {
fn map(self: &Self, input: u32) -> Option<u32> {
let upper_bound: u64 = self.start as u64 + self.length as u64;
if input >= self.start && (input as u64) < upper_bound {
Some(input - self.start + self.dest)
}
else {
None
}
}
fn inverse_map(&self, input: u32) -> Option<u32> {
let upper_bound: u64 = self.dest as u64 + self.length as u64;
if input >= self.dest && (input as u64) < upper_bound {
Some(input - self.dest + self.start)
}
else {
None
}
}
}
#[derive(Debug)]
struct Mapping {
range_maps: Vec<RangeMap>
}
impl Mapping {
fn map(self: &Self, input: u32) -> u32 {
for range_map in self.range_maps.iter() {
match range_map.map(input) {
Some(x) => return x,
None => continue
}
}
input
}
fn inverse_map(self: &Self, input: u32) -> u32 {
for range_map in self.range_maps.iter() {
match range_map.inverse_map(input) {
Some(x) => return x,
None => continue
}
}
input
}
}
#[derive(Debug)]
struct Almanac {
seeds: Vec<u32>,
seed_to_soil_map: Mapping,
soil_to_fertilizer_map: Mapping,
fertilizer_to_water_map: Mapping,
water_to_light_map: Mapping,
light_to_temperature_map: Mapping,
temperature_to_humidity_map: Mapping,
humidity_to_location_map: Mapping
}
impl Almanac {
fn new() -> Self {
Almanac {
seeds: vec![],
seed_to_soil_map: Mapping{range_maps: vec![] },
soil_to_fertilizer_map: Mapping{range_maps: vec![] },
fertilizer_to_water_map: Mapping{range_maps: vec![] },
water_to_light_map: Mapping{range_maps: vec![] },
light_to_temperature_map: Mapping{range_maps: vec![] },
temperature_to_humidity_map: Mapping{range_maps: vec![] },
humidity_to_location_map: Mapping{range_maps: vec![] },
}
}
fn find_closest(self: &Self, forward: bool) -> Result<u32, AdventError> {
let mut total_mapping = vec![
&self.seed_to_soil_map,
&self.soil_to_fertilizer_map,
&self.fertilizer_to_water_map,
&self.water_to_light_map,
&self.light_to_temperature_map,
&self.temperature_to_humidity_map,
&self.humidity_to_location_map,
];
if forward {
self.seeds.iter().map(|seed| {
total_mapping
.iter()
.fold(*seed, | input, map| {
map.map(input)
})
}).min().ok_or(AdventError("mapping procedure went wrong".into()))
} else {
total_mapping.reverse();
for loc in 0..u32::MAX {
let seed = total_mapping
.iter()
.fold(loc, | input, map| {
map.inverse_map(input)
});
match self.seeds.binary_search(&seed) {
Ok(_) => return Ok(loc),
Err(_) => continue
}
};
Err(AdventError("Scanned location space, found nothing".into()))
}
}
fn interpret_range(self: &mut Self) -> () {
let mut vec:Vec<u32> = vec![];
let upper_bound = self.seeds.len() / 2;
for i in 0..upper_bound {
for seed in self.seeds[i*2]..self.seeds[i*2]+self.seeds[i*2+1] {
vec.push(seed);
}
}
vec.sort();
self.seeds = vec
}
}
enum State {
None,
SeedToSoil,
SoilToFertilizer,
FertilizerToWater,
WaterToLight,
LightToTemperature,
TemperatureToHumidity,
HumidityToLocation,
}
impl FromStr for Almanac {
type Err = AdventError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
const SEEDS_HEADER: &'static str = "seeds: ";
const SEED_TO_SOIL_MAP_HEADER: &'static str = "seed-to-soil map:";
const SOIL_TO_FERTILIZER_MAP_HEADER: &'static str = "soil-to-fertilizer map:";
const FERTILIZER_TO_WATER_MAP_HEADER: &'static str = "fertilizer-to-water map:";
const WATER_TO_LIGHT_MAP_HEADER: &'static str = "water-to-light map:";
const LIGHT_TO_TEMPERATURE_MAP_HEADER: &'static str = "light-to-temperature map:";
const TEMPERATURE_TO_HUMIDITY_MAP_HEADER: &'static str = "temperature-to-humidity map:";
const HUMIDITY_TO_LOCATION_MAP_HEADER: &'static str = "humidity-to-location map:";
let mut almanac = Almanac::new();
let mut state = State::None;
for line in s.lines() {
if line.trim().is_empty() { continue; }
if line.starts_with(SEEDS_HEADER) {
almanac.seeds = line
.trim()
.split_ascii_whitespace()
.skip(1)
.map(|s| -> Result<u32, AdventError> {
Ok(s.trim().parse::<u32>()?)
}).collect::<Result<Vec<u32>, AdventError>>()?;
continue;
}
if !match line.trim() {
SEED_TO_SOIL_MAP_HEADER => { state = State::SeedToSoil; true }
SOIL_TO_FERTILIZER_MAP_HEADER => { state = State::SoilToFertilizer; true }
FERTILIZER_TO_WATER_MAP_HEADER => { state = State::FertilizerToWater; true }
WATER_TO_LIGHT_MAP_HEADER => { state = State::WaterToLight; true }
LIGHT_TO_TEMPERATURE_MAP_HEADER => { state = State::LightToTemperature; true }
TEMPERATURE_TO_HUMIDITY_MAP_HEADER => { state = State::TemperatureToHumidity; true }
HUMIDITY_TO_LOCATION_MAP_HEADER => { state = State::HumidityToLocation; true }
_ => false
} {
let mapping = line
.trim()
.split_ascii_whitespace()
.map(|s| -> Result<u32, AdventError> {
Ok(s.parse::<u32>()?)
}).collect::<Result<Vec<u32>, AdventError>>()?;
let range_map = RangeMap {
dest: mapping[0],
start: mapping[1],
length: mapping[2]
};
match state {
State::None => Err(AdventError("Not in any state".into())),
State::SeedToSoil => Ok(almanac.seed_to_soil_map.range_maps.push(range_map)),
State::SoilToFertilizer => Ok(almanac.soil_to_fertilizer_map.range_maps.push(range_map)),
State::FertilizerToWater => Ok(almanac.fertilizer_to_water_map.range_maps.push(range_map)),
State::WaterToLight => Ok(almanac.water_to_light_map.range_maps.push(range_map)),
State::LightToTemperature => Ok(almanac.light_to_temperature_map.range_maps.push(range_map)),
State::TemperatureToHumidity => Ok(almanac.temperature_to_humidity_map.range_maps.push(range_map)),
State::HumidityToLocation => Ok(almanac.humidity_to_location_map.range_maps.push(range_map)),
}?
}
}
Ok(almanac)
}
}
pub fn one() -> Result<u64, AdventError>
{
let almanac = read_into::<Almanac>(Path::new("resources/input5.txt".into()))?;
Ok(almanac.find_closest(true)? as u64)
}
pub fn two() -> Result<u64, AdventError>
{
let mut almanac = read_into::<Almanac>(Path::new("resources/input5.txt".into()))?;
almanac.interpret_range();
Ok(almanac.find_closest(false)? as u64)
}