diff --git a/src/bin/day12.rs b/src/bin/day12.rs new file mode 100644 index 0000000..2cc838c --- /dev/null +++ b/src/bin/day12.rs @@ -0,0 +1,160 @@ +use std::time::Instant; + +use aoc_2025::{Grid, load, print_res}; +use bstr::{BString, ByteSlice}; +use color_eyre::eyre::Context; + +#[derive(PartialEq, Eq)] +enum ShapeSlot { + Full, + Empty, +} + +pub struct Shape(Grid); + +impl std::fmt::Display for ShapeSlot { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ShapeSlot::Full => write!(f, "#"), + ShapeSlot::Empty => write!(f, "."), + } + } +} + +impl std::fmt::Debug for Shape { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Debug)] +pub struct Region { + lines: usize, + cols: usize, + shapes: Vec, +} + +type Parsed = (Vec, Vec); + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + let mut parts = input.split_str("\n\n").peekable(); + let mut shapes = Vec::new(); + + for i in 0.. { + let Some(&part) = parts.peek() else { + color_eyre::eyre::bail!("Unexpected end of input\n"); + }; + let expect = format!("{i}:\n"); + + if !part.starts_with_str(&expect) { + break; + } + + let part = parts.next().unwrap(); + + let line_len = part.lines().nth(1).unwrap().len(); + let values = part + .lines() + .skip(1) + .flat_map(|l| { + l.iter().map(|&b| match b { + b'.' => Ok(ShapeSlot::Empty), + b'#' => Ok(ShapeSlot::Full), + _ => Err(color_eyre::eyre::eyre!("Invalid grid point: {}", b as char)), + }) + }) + .collect::>()?; + + shapes.push(Shape(Grid::new(values, line_len))); + } + + let Some(regions) = parts.next() else { + color_eyre::eyre::bail!("Unexpected end on input\n") + }; + + color_eyre::eyre::ensure!(parts.count() == 0, "Too many parts of the input"); + + Ok(( + shapes, + str::from_utf8(regions)? + .lines() + .map(|line| { + let Some((size, shapes)) = line.split_once(": ") else { + color_eyre::eyre::bail!("Malformed region: {line}"); + }; + + let Some((lines, cols)) = size.split_once('x') else { + color_eyre::eyre::bail!("Malformed region size: {size}"); + }; + + Ok(Region { + lines: lines + .parse() + .wrap_err_with(|| format!("While parsing lines: {lines}"))?, + cols: cols + .parse() + .wrap_err_with(|| format!("While parsing cols: {cols}"))?, + shapes: shapes + .split_whitespace() + .map(|n| { + n.parse() + .wrap_err_with(|| format!("While parsing shape: {n}")) + }) + .collect::>()?, + }) + }) + .collect::>()?, + )) +} + +#[inline(never)] +pub fn part1((shapes, regions): Parsed) { + let shape_areas: Vec<_> = shapes.iter().map(|s| s.0.count(&ShapeSlot::Full)).collect(); + let mut remain = 0; + + for region in regions { + let area = region.lines * region.cols; + let min_area: usize = region + .shapes + .iter() + .zip(&shape_areas) + .map(|(&n, &a)| n as usize * a) + .sum(); + + // Not enough area to even attempt + if min_area > area { + continue + } + + remain += 1; + } + + print_res!("Possible solvable: {remain}"); +} + +#[inline(never)] +pub fn part2(input: Parsed) { + todo!("todo part2") +} + +pub fn main() -> color_eyre::Result<()> { + let context = load()?; + + let start = Instant::now(); + let parsed = parsing(&context.input)?; + let elapsed = humantime::format_duration(start.elapsed()); + + let start = Instant::now(); + if context.part == 1 { + part1(parsed); + } else { + part2(parsed); + } + let elapsed_part = humantime::format_duration(start.elapsed()); + + println!(" Parsing: {elapsed}"); + println!(" Solving: {elapsed_part}"); + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 264dc84..1c75b3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,10 @@ impl Grid { } sum } + + pub fn count(&self, like: &T) -> usize { + self.values.iter().filter(|&v| v == like).count() + } } impl std::ops::Index<(usize, usize)> for Grid {