From 000002805482602aa6c279f220fdbfaa11d0ba1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Sat, 21 Dec 2024 17:48:14 +0100 Subject: [PATCH] solution: day 21 --- Cargo.lock | 188 ++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + data/examples/21.txt | 5 ++ src/bin/21.rs | 157 ++++++++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 9 deletions(-) create mode 100644 data/examples/21.txt create mode 100644 src/bin/21.rs diff --git a/Cargo.lock b/Cargo.lock index f3af966..5067aa2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -36,6 +48,7 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" name = "aoc" version = "48.0.0" dependencies = [ + "cached", "hashbrown 0.15.2", "itertools", "num-integer", @@ -87,6 +100,39 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +[[package]] +name = "cached" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9718806c4a2fe9e8a56fd736f97b340dd10ed1be8ed733ed50449f351dc33cae" +dependencies = [ + "ahash", + "cached_proc_macro", + "cached_proc_macro_types", + "hashbrown 0.14.5", + "once_cell", + "thiserror", + "web-time", +] + +[[package]] +name = "cached_proc_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f42a145ed2d10dce2191e1dcf30cfccfea9026660e143662ba5eec4017d5daa" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "cached_proc_macro_types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" + [[package]] name = "cc" version = "1.0.78" @@ -115,6 +161,41 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.90", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.90", +] + [[package]] name = "dotenvy" version = "0.15.6" @@ -275,6 +356,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -366,6 +457,12 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.3.0" @@ -535,9 +632,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" @@ -562,7 +659,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -616,18 +713,18 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -829,6 +926,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "1.0.107" @@ -840,6 +943,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -875,6 +989,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1000,6 +1134,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "want" version = "0.3.0" @@ -1037,7 +1177,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-shared", ] @@ -1071,7 +1211,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1092,6 +1232,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1232,3 +1382,23 @@ dependencies = [ "cfg-if", "windows-sys 0.48.0", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/Cargo.toml b/Cargo.toml index 0aeeaee..3fd8298 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ authors.workspace = true repository.workspace = true [dependencies] +cached = "0.54.0" hashbrown = "0.15.2" itertools = "0.13.0" num-integer = "0.1.46" diff --git a/data/examples/21.txt b/data/examples/21.txt new file mode 100644 index 000000000..4cf0c29 --- /dev/null +++ b/data/examples/21.txt @@ -0,0 +1,5 @@ +029A +980A +179A +456A +379A diff --git a/src/bin/21.rs b/src/bin/21.rs new file mode 100644 index 000000000..0792491 --- /dev/null +++ b/src/bin/21.rs @@ -0,0 +1,157 @@ +use std::collections::VecDeque; + +use hashbrown::HashMap; +use itertools::Itertools; + +use cached::proc_macro::cached; +use cached::SizedCache; + +#[rustfmt::skip] +#[allow(clippy::type_complexity)] +const NUMPAD: [(u8, [Option<(u8, u8)>; 4]); 11] = [ + (b'0', [Some((b'2', b'^')), Some((b'A', b'>')), None, None]), + (b'1', [Some((b'2', b'>')), Some((b'4', b'^')), None, None]), + (b'2', [Some((b'0', b'v')), Some((b'1', b'<')), Some((b'3', b'>')), Some((b'5', b'^')),]), + (b'3', [Some((b'2', b'<')), Some((b'6', b'^')), Some((b'A', b'v')), None,]), + (b'4', [Some((b'1', b'v')), Some((b'5', b'>')), Some((b'7', b'^')), None,]), + (b'5', [Some((b'2', b'v')), Some((b'4', b'<')), Some((b'6', b'>')), Some((b'8', b'^')),]), + (b'6', [Some((b'3', b'v')), Some((b'5', b'<')), Some((b'9', b'^')), None,]), + (b'7', [Some((b'4', b'v')), Some((b'8', b'>')), None, None]), + (b'8', [Some((b'5', b'v')), Some((b'7', b'<')), Some((b'9', b'>')), None,]), + (b'9', [Some((b'6', b'v')), Some((b'8', b'<')), None, None]), + (b'A', [Some((b'0', b'<')), Some((b'3', b'^')), None, None]), +]; + +#[rustfmt::skip] +#[allow(clippy::type_complexity)] +const KEYPAD: [(u8, [Option<(u8, u8)>; 3]); 5] = [ + (b'^', [Some((b'A', b'>')), Some((b'v', b'v')), None]), + (b'<', [Some((b'v', b'>')), None, None]), + (b'v', [Some((b'<', b'<')), Some((b'^', b'^')), Some((b'>', b'>'))]), + (b'>', [Some((b'v', b'<')), Some((b'A', b'^')), None]), + (b'A', [Some((b'^', b'<')), Some((b'>', b'v')), None]), +]; + +fn bfs(graph: &HashMap>, s: u8, e: u8) -> Vec> { + let mut res = Vec::new(); + + let mut queue = VecDeque::new(); + let mut shortest = usize::MAX; + + queue.push_back((s, VecDeque::new())); + + while let Some((n, mut p)) = queue.pop_front() { + if n == e { + if shortest == usize::MAX { + shortest = p.len(); + } + + if p.len() == shortest { + p.push_back(b'A'); + res.push(p); + } + continue; + } + + if p.len() >= shortest { + continue; + } + + for (nn, c) in graph[&n].iter().copied() { + let mut np = p.clone(); + np.push_back(c); + + queue.push_back((nn, np)); + } + } + + res +} + +#[cached( + ty = "SizedCache", + create = "{ SizedCache::with_size(100000) }", + convert = r#"{ format!("{sequence:?}{level}{graph}") }"# +)] +fn dfs( + graphs: &[HashMap>], + graph: usize, + mut sequence: VecDeque, + level: usize, +) -> usize { + let graph = &graphs[graph]; + sequence.push_front(b'A'); + let mut res = 0; + + for (n1, n2) in sequence.into_iter().tuple_windows() { + let paths = bfs(graph, n1, n2); + if level == 0 { + res += paths.into_iter().map(|p| p.len()).min().unwrap(); + } else { + res += paths + .into_iter() + .map(|p| dfs(graphs, 1, p, level - 1)) + .min() + .unwrap(); + } + } + + res +} + +fn build_graphs() -> [HashMap>; 2] { + let mut numpad = HashMap::new(); + for (k, v) in NUMPAD.iter() { + numpad.insert(*k, v.iter().filter_map(|&x| x).collect_vec()); + } + + let mut keypad = HashMap::new(); + for (k, v) in KEYPAD.iter() { + keypad.insert(*k, v.iter().filter_map(|&x| x).collect_vec()); + } + + [numpad, keypad] +} + +fn solve_chain(input: &str, level: usize) -> usize { + let graphs = build_graphs(); + + let mut res = 0; + for line in input.lines() { + let n: usize = line.strip_suffix("A").unwrap().parse().unwrap(); + let sequence = VecDeque::from_iter(line.as_bytes().iter().copied()); + let min_len = dfs(&graphs, 0, sequence, level); + res += min_len * n; + } + + res +} + +pub fn part_one(input: &str) -> Option { + Some(solve_chain(input, 2)) +} + +pub fn part_two(input: &str) -> Option { + Some(solve_chain(input, 25)) +} + +aoc::solution!(21); + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_part_one() { + assert_eq!( + part_one(&aoc::template::read_file("examples", 21)), + Some(126384) + ); + } + #[test] + fn test_part_two() { + assert_eq!( + part_two(&aoc::template::read_file("examples", 21)), + Some(154115708116294) + ); + } +}