summaryrefslogblamecommitdiffstats
path: root/2023/rs/code/five/src/main.rs
blob: 79ba943a122623c26bce7e0c7c572d58be8b716c (plain) (tree)

















































































                                                                                                                                                                 
#![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}");
}