From 7b73f3f020b323ec8e4c37d3a63bd96bbc4063e0 Mon Sep 17 00:00:00 2001 From: Anthony Oteri Date: Tue, 12 Dec 2023 14:55:22 -0500 Subject: [PATCH] Day 12 - Part 2 Signed-off-by: Anthony Oteri --- Cargo.toml | 1 + day-12/Cargo.toml | 2 + day-12/benches/benchmark.rs | 4 +- day-12/src/part2.rs | 84 +++++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f33f37b..78d281b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ dhat = "0.3.2" env_logger = "0.10.1" test-log = "0.2.13" regex = "1.10.2" +memoize = "0.4.1" [profile.flamegraph] diff --git a/day-12/Cargo.toml b/day-12/Cargo.toml index 3d596f5..296e223 100644 --- a/day-12/Cargo.toml +++ b/day-12/Cargo.toml @@ -14,6 +14,8 @@ miette = { workspace = true } thiserror = { workspace = true } dhat = { workspace = true } regex = { workspace = true } +rayon = { workspace = true } +memoize = { workspace = true } [dev-dependencies] divan = { workspace = true } diff --git a/day-12/benches/benchmark.rs b/day-12/benches/benchmark.rs index 0250bdf..67decf5 100644 --- a/day-12/benches/benchmark.rs +++ b/day-12/benches/benchmark.rs @@ -6,10 +6,10 @@ fn main() { #[divan::bench] fn part1() { - part1::process(divan::black_box(include_str!("../test-input.txt"))).unwrap(); + part1::process(divan::black_box(include_str!("../input.txt"))).unwrap(); } #[divan::bench] fn part2() { - part2::process(divan::black_box(include_str!("../test-input.txt"))).unwrap(); + part2::process(divan::black_box(include_str!("../input.txt"))).unwrap(); } diff --git a/day-12/src/part2.rs b/day-12/src/part2.rs index 1ff3976..21580cd 100644 --- a/day-12/src/part2.rs +++ b/day-12/src/part2.rs @@ -1,8 +1,86 @@ +use nom::{ + branch::alt, + bytes::complete::{tag, take_until}, + character::complete::{self, line_ending, space1}, + combinator::eof, + multi::{many1, separated_list1}, + sequence::{self, terminated}, + IResult, +}; +use rayon::prelude::*; + use crate::error::AocError; +fn parse_input(input: &str) -> IResult<&str, &str> { + terminated(take_until(" "), space1)(input) +} + +fn parse_limits(input: &str) -> IResult<&str, Vec> { + let (input, limits) = separated_list1(tag(","), complete::u32)(input)?; + Ok((input, limits.into_iter().map(|n| n as usize).collect())) +} + +fn parser(input: &str) -> IResult<&str, Vec<(&str, Vec)>> { + many1(terminated( + sequence::tuple((parse_input, parse_limits)), + alt((line_ending, eof)), + ))(input) +} + +#[memoize::memoize] +fn count(input: String, limits: Vec) -> usize { + if input.is_empty() { + if limits.is_empty() { + return 1; + } else { + return 0; + } + } + + if limits.is_empty() { + if input.contains('#') { + return 0; + } else { + return 1; + } + } + let mut n = 0; + + if input.starts_with('.') || input.starts_with('?') { + n += count(input[1..].to_string(), limits.clone()); + } + if (input.starts_with('#') || input.starts_with('?')) && limits[0] <= input.len() + && !input.get(..limits[0]).unwrap().contains('.') && (limits[0] == input.len() || input.chars().nth(limits[0]).unwrap() != '#') { + n += count( + input.get(limits[0] + 1..).unwrap_or_default().to_string(), + limits[1..].to_vec(), + ); + } + n +} + #[tracing::instrument] -pub fn process(input: &str) -> miette::Result { - Ok(0) +pub fn process(input: &str) -> miette::Result { + let (_, parsed) = parser(input).unwrap(); + + Ok(parsed + .par_iter() + .map(|(string, limits)| { + let string = std::iter::repeat(string) + .take(5) + .cloned() + .collect::>() + .join("?"); + let limits = limits + .iter() + .cycle() + .take(5 * limits.len()) + .cloned() + .collect::>(); + + count(string.clone(), limits) + }) + .sum()) } #[cfg(test)] @@ -12,7 +90,7 @@ mod tests { #[test_log::test] fn test_process() -> miette::Result<()> { let input = include_str!("../test-input.txt"); - assert_eq!(0, process(input)?); + assert_eq!(525152, process(input)?); Ok(()) } }