From d04d193f4cf3f417b761d9e5bc0927eca0fa4e3c Mon Sep 17 00:00:00 2001 From: Lars Martens Date: Tue, 19 Dec 2023 23:53:54 +0100 Subject: [PATCH] Solve day 12 part 2 --- Cargo.lock | 180 +++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/bin/12.rs | 108 +++++++++++++++++------------- 3 files changed, 242 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 760ce59..97c49c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -11,6 +23,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anyhow" version = "1.0.75" @@ -22,6 +40,7 @@ name = "aoc2023" version = "0.0.0" dependencies = [ "anyhow", + "cached", "indicatif", "itertools", "rayon", @@ -36,6 +55,39 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "cached" +version = "0.46.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c8c50262271cdf5abc979a5f76515c234e764fa025d1ba4862c0f0bcda0e95" +dependencies = [ + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "hashbrown", + "instant", + "once_cell", + "thiserror", +] + +[[package]] +name = "cached_proc_macro" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" + [[package]] name = "cfg-if" version = "1.0.0" @@ -88,6 +140,41 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "either" version = "1.9.0" @@ -100,6 +187,28 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indicatif" version = "0.17.7" @@ -171,6 +280,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "portable-atomic" version = "1.5.1" @@ -273,7 +388,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.39", ] [[package]] @@ -287,6 +402,23 @@ dependencies = [ "serde", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.39" @@ -298,6 +430,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -310,6 +462,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "windows-sys" version = "0.45.0" @@ -375,3 +533,23 @@ name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] diff --git a/Cargo.toml b/Cargo.toml index aee7bc2..b027114 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow = "1.0.75" +cached = "0.46.1" indicatif = { version = "0.17.7", features = ["rayon"] } itertools = "0.12.0" rayon = "1.8.0" diff --git a/src/bin/12.rs b/src/bin/12.rs index 5445abe..f64adad 100644 --- a/src/bin/12.rs +++ b/src/bin/12.rs @@ -1,12 +1,14 @@ use aoc2023::*; -use std::collections::VecDeque; +use cached::proc_macro::cached; +use itertools::Itertools; +use std::{collections::VecDeque, fmt::Debug, iter}; const INPUT: &str = include_str!("../../input/12"); fn main() { assert_example!(part1, "12-test", 21); println!("Part 1: {}", part1(INPUT)); - assert_example!(part2, "12-test", 0); + assert_example!(part2, "12-test", 525152); println!("Part 2: {}", part2(INPUT)); } @@ -14,15 +16,20 @@ fn part1(input: &str) -> usize { input .lines() .map(Row::parse) - .map(Row::possible_arrangements) + .map(possible_arrangements) .sum() } fn part2(input: &str) -> usize { - 0 + input + .lines() + .map(Row::parse) + .map(Row::unfold) + .map(possible_arrangements) + .sum() } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] struct Row { springs: VecDeque, groups: VecDeque, @@ -36,51 +43,60 @@ impl Row { Self { springs, groups } } - fn possible_arrangements(mut self) -> usize { - // Pop all operational springs on the front and then get the first different one. - let first = loop { - match self.springs.pop_front() { - None => return if self.groups.is_empty() { 1 } else { 0 }, - Some(Spring::Operational) => continue, - Some(Spring::Broken) => break InterestingSpring::Broken, - Some(Spring::Unknown) => break InterestingSpring::Unknown, - } - }; + fn unfold(self) -> Self { + let springs = iter::repeat(self.springs).take(5); + let springs = Itertools::intersperse(springs, [Spring::Unknown].into()) + .flatten() + .collect(); + let groups = iter::repeat(self.groups).take(5).flatten().collect(); + Self { springs, groups } + } +} - match first { - InterestingSpring::Broken => { - // A broken spring requires a run that matches the group. - let Some(mut group) = self.groups.pop_front() else { - // There was a broken spring but no group - return 0; - }; +#[cached] +fn possible_arrangements(mut row: Row) -> usize { + // Pop all operational springs on the front and then get the first different one. + let first = loop { + match row.springs.pop_front() { + None => return if row.groups.is_empty() { 1 } else { 0 }, + Some(Spring::Operational) => continue, + Some(Spring::Broken) => break InterestingSpring::Broken, + Some(Spring::Unknown) => break InterestingSpring::Unknown, + } + }; - // We already encountered the first broken spring. - group -= 1; + match first { + InterestingSpring::Broken => { + // A broken spring requires a run that matches the group. + let Some(mut group) = row.groups.pop_front() else { + // There was a broken spring but no group + return 0; + }; - // Pop all required springs or return if impossible. - loop { - match self.springs.pop_front() { - None if group > 0 => return 0, - None if group == 0 => break, - Some(Spring::Broken | Spring::Unknown) if group > 0 => group -= 1, - Some(Spring::Broken) if group == 0 => return 0, - Some(Spring::Operational) if group > 0 => return 0, - Some(Spring::Operational | Spring::Unknown) => break, - other => panic!("group detection weird case {other:?}, group={group}"), - } + // We already encountered the first broken spring. + group -= 1; + + // Pop all required springs or return if impossible. + loop { + match row.springs.pop_front() { + None if group > 0 => return 0, + None => break, + Some(Spring::Broken | Spring::Unknown) if group > 0 => group -= 1, + Some(Spring::Broken) => return 0, + Some(Spring::Operational) if group > 0 => return 0, + Some(Spring::Operational | Spring::Unknown) => break, } + } - self.possible_arrangements() - } - InterestingSpring::Unknown => { - // We don't know what kind of spring is here, recursively try both options. - let mut a = self.clone(); - let mut b = self; - a.springs.push_front(Spring::Broken); - b.springs.push_front(Spring::Operational); - a.possible_arrangements() + b.possible_arrangements() - } + possible_arrangements(row) + } + InterestingSpring::Unknown => { + // We don't know what kind of spring is here, recursively try both options. + let mut a = row.clone(); + let mut b = row; + a.springs.push_front(Spring::Broken); + b.springs.push_front(Spring::Operational); + possible_arrangements(a) + possible_arrangements(b) } } } @@ -91,7 +107,7 @@ enum InterestingSpring { Unknown, } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] enum Spring { Broken, Unknown,