#![feature(iter_array_chunks)]
use nom::{Parser, IResult, Finish};
use nom::character::complete::*;
use nom::bytes::complete::*;
use nom::sequence::*;
use nom::multi::*;
use std::io::Write;
static INPUT: &str = include_str!("input.txt");
#[derive(Debug, Copy, Clone)]
struct Entry {
source_start: u32,
dest_start: u32,
len: u32
}
fn parse(i: &str) -> IResult<&str, (Vec<u32>, Vec<Vec<Entry>>)> {
let (i, (_, seeds, mut entries)) = tuple((
tag("seeds: "),
separated_list1(tag(" "), digit1.map(|s: &str| s.parse().unwrap())),
many1(preceded(
tuple((line_ending, line_ending, not_line_ending, line_ending)),
separated_list1(
line_ending,
tuple((
digit1,
tag(" "),
digit1,
tag(" "),
digit1))
.map(|(a, _, b, _, c): (&str, _, &str, _, &str)|
Entry { dest_start: a.parse().unwrap(), source_start: b.parse().unwrap(), len: c.parse().unwrap() }))))))
(i)?;
for entry in &mut entries {
entry.sort_by_key(|e| e.source_start);
}
Ok((i, (seeds, entries)))
}
fn main() {
let (seeds, entries) = parse(INPUT).finish().unwrap().1;
let map_seed = |s: u32|
entries.iter().fold(
s,
|s, el| {
let entry = el.iter().cloned().find(
|i| i.source_start <= s && s - i.source_start < i.len)
.unwrap_or(Entry { source_start: 0, dest_start: 0, len: u32::MAX });
s - entry.source_start + entry.dest_start
});
let min_loc = seeds.iter().cloned().map(map_seed).min().unwrap();
println!("Minimum `location`: {min_loc}");
// [12:48 AM] got p1
// [12:48 AM] oh this sucks
// [12:50 AM] do i just
// [12:50 AM] brute force it
// [12:50 AM] i could like
// [12:50 AM] write something clever here
// [12:51 AM] like i think i could do something like lifting the mapping between the layers and creating one final mapping, and then just checking against that
// [12:51 AM] and just finding the lowest start number within the bounds of all the seed ranges
// [12:51 AM] but i dont feel like doing that
#[cfg(debug_assertions)]
eprintln!("Warning: Debug build detected! This next bit takes a while, so I'd suggest compiling `--release`.");
let min_loc_ranged = seeds.iter().array_chunks::<2>()
.inspect(|[a, b]| { print!("\x1B[2KChecking {} => {}...\r", *a, *a + *b); std::io::stdout().flush().unwrap(); })
.flat_map(|[a, b]| *a..(*a+*b))
.map(map_seed)
.min().unwrap();
println!("Minimum `location` within ranges: {min_loc_ranged}");
}