From 10245f3faa1652240eeb751e0576538be6e3fa3d Mon Sep 17 00:00:00 2001 From: Quentin Boyer Date: Sat, 4 Jan 2025 20:38:53 +0100 Subject: [PATCH] Day 19 --- src/bin/day19.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/bin/day19.rs diff --git a/src/bin/day19.rs b/src/bin/day19.rs new file mode 100644 index 0000000..8c9386b --- /dev/null +++ b/src/bin/day19.rs @@ -0,0 +1,141 @@ +use core::str; +use std::{collections::HashMap, time::Instant}; + +use aoc_2024::{load, print_res}; +use bstr::BString; +use color_eyre::eyre::{bail, OptionExt}; + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum Color { + White, + Blue, + Black, + Red, + Green, +} + +impl Color { + fn from_b(b: u8) -> color_eyre::Result { + Ok(match b { + b'w' => Color::White, + b'u' => Color::Blue, + b'b' => Color::Black, + b'r' => Color::Red, + b'g' => Color::Green, + _ => bail!("Invalid color: {}", b as char), + }) + } +} + +type Parsed = (Vec>, Vec>); + +#[inline(never)] +pub fn parsing(input: &BString) -> color_eyre::Result { + let (towels, patterns) = str::from_utf8(input)? + .split_once("\n\n") + .ok_or_eyre("Missing blank line")?; + + let towels = towels + .split(", ") + .map(|towel| { + towel + .bytes() + .map(Color::from_b) + .collect::, _>>() + }) + .collect::>()?; + + let patterns = patterns + .lines() + .map(|line| { + line.bytes() + .map(Color::from_b) + .collect::, _>>() + }) + .collect::>()?; + + Ok((towels, patterns)) +} + +#[inline(never)] +pub fn part1((towels, patterns): Parsed) { + let mut possible = 0; + + 'pattern: for pattern in patterns { + let mut current = vec![&*pattern]; + + while let Some(v) = current.pop() { + if v.is_empty() { + possible += 1; + continue 'pattern; + } + + for towel in &towels { + if v.starts_with(towel) { + current.push(&v[towel.len()..]); + } + } + } + } + + print_res!("Number of possible patterns: {possible}"); +} + +#[inline(never)] +pub fn part2((towels, patterns): Parsed) { + let mut memo = HashMap::new(); + memo.insert(&[][..], 1); + + let mut possible = 0; + + fn arrangements<'a>( + pattern: &'a [Color], + towels: &[Vec], + memo: &mut HashMap<&'a [Color], usize>, + ) -> usize { + match memo.get(pattern) { + Some(&v) => v, + None => { + let value = towels + .iter() + .map(|towel| { + if pattern.starts_with(towel) { + arrangements(&pattern[towel.len()..], towels, memo) + } else { + 0 + } + }) + .sum(); + memo.insert(pattern, value); + value + } + } + } + + for pattern in &patterns { + possible += arrangements(pattern, &towels, &mut memo); + } + + print_res!("Number of possible arrangements: {possible}"); +} + +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(()) +}