diff --git a/.aoc_tiles/tiles/2024/19.png b/.aoc_tiles/tiles/2024/19.png index 291b2ef..9da34f0 100644 Binary files a/.aoc_tiles/tiles/2024/19.png and b/.aoc_tiles/tiles/2024/19.png differ diff --git a/README.md b/README.md index 0e6a135..b2594e8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- 2024 - 36 ⭐ - Rust + 2024 - 38 ⭐ - Rust

@@ -56,4 +56,7 @@ + + + diff --git a/src/day19.rs b/src/day19.rs index bb922e8..42f7856 100644 --- a/src/day19.rs +++ b/src/day19.rs @@ -1,31 +1,193 @@ use aoc_runner_derive::aoc; +use cached::proc_macro::cached; +use itertools::Itertools; +use rustc_hash::{FxHashMap, FxHasher}; +use std::{ + collections::VecDeque, + fmt::{Display, Write}, +}; -fn parse(input: &str) -> String { - todo!() +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +enum Stripe { + White = b'w', + Blue = b'u', + Black = b'b', + Red = b'r', + Green = b'g', +} + +impl From<&u8> for Stripe { + fn from(value: &u8) -> Self { + match value { + b'w' => Self::White, + b'u' => Self::Blue, + b'b' => Self::Black, + b'r' => Self::Red, + b'g' => Self::Green, + _ => unimplemented!(), + } + } +} + +impl Into for &Stripe { + fn into(self) -> char { + let v = *self as u8; + v.into() + } +} + +impl Display for Stripe { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_char(self.into()) + } +} + +#[derive(Debug)] +struct Design { + stripes: Vec, +} + +impl From<&[u8]> for Design { + fn from(input: &[u8]) -> Self { + let stripes = input.iter().map(|c| c.into()).collect(); + Self { stripes } + } +} + +impl Display for Design { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for stripe in &self.stripes { + f.write_char(stripe.into())?; + } + Ok(()) + } +} + +#[derive(Debug)] +struct Onsen { + towels: Vec, + designs: Vec, + // ways_cache: FxHashMap<&'a [Stripe], i64> +} + +impl Display for Onsen { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.towels.iter().join(", "))?; + writeln!(f)?; + writeln!(f)?; + for d in &self.designs { + d.fmt(f)?; + writeln!(f)?; + } + Ok(()) + } +} + +impl Onsen { + fn possible(&self, d: &[Stripe]) -> bool { + if d.len() == 0 { + return true; + } + for t in &self.towels { + if d.starts_with(&t.stripes) { + if self.possible(d.split_at(t.stripes.len()).1) { + return true; + } + } + } + false + } + // Count the ways to construct a given substring + fn ways<'a>(&'a self, d: &'a [Stripe], mut cache: FxHashMap<&'a [Stripe], i64>) -> (FxHashMap<&'a [Stripe], i64>, i64) { + if d.len() == 0 { + // println!("ways to make {} - 0", d.iter().join("")); + return (cache, 1); + } + if cache.contains_key(d) { + let val = cache[d]; + return (cache, val); + } + + let mut count = 0; + for t in &self.towels { + if d.starts_with(&t.stripes) { + let res_count; + (cache, res_count) = self.ways(&d[t.stripes.len()..d.len()], cache); + count += res_count; + } + } + // println!("ways to make {} - {}", d.iter().join(""), count); + cache.insert(d, count); + (cache, count) + } + fn count_possible(&self) -> i64 { + self.designs + .iter() + .map(|d| self.possible(&d.stripes)) + .filter(|p| *p) + .count() as i64 + } + fn count_ways(&self) -> i64 { + self.designs.iter().map(|d| self.ways(&d.stripes, FxHashMap::default()).1).sum::() + } +} + +fn parse(input: &str) -> Onsen { + let mut lines = input.lines(); + + let towels = lines + .next() + .unwrap() + .split(&[',', ' ']) + .filter(|s| !s.is_empty()) + .map(|s| s.as_bytes().into()) + .collect(); + lines.next().unwrap(); // discard empty line + + let designs = lines.map(|l| l.as_bytes().into()).collect(); + + Onsen { towels, designs } } #[aoc(day19, part1)] fn part1(input: &str) -> i64 { - todo!() + let onsen = parse(input); + + onsen.count_possible() } #[aoc(day19, part2)] fn part2(input: &str) -> i64 { - todo!() + let onsen = parse(input); + + onsen.count_ways() } #[cfg(test)] mod tests { use super::*; - const EXAMPLE: &str = ""; + const EXAMPLE: &str = "r, wr, b, g, bwu, rb, gb, br + +brwrr +bggr +gbbr +rrbgbr +ubwu +bwurrg +brgr +bbrgwb"; +const EXAMPLE2: &str = "r, wr, b, g, bwu, rb, gb, br + +rrbgbr"; #[test] fn part1_example() { - assert_eq!(part1(EXAMPLE), 0); + assert_eq!(part1(EXAMPLE), 6); } #[test] fn part2_example() { - assert_eq!(part2(EXAMPLE), 0); + assert_eq!(part2(EXAMPLE), 16); } }