Compare commits

..

8 Commits

Author SHA1 Message Date
00000340e7 solution: day 25 2025-01-02 22:45:07 +01:00
0000033060 solution: day 24 2025-01-02 22:43:58 +01:00
000003208e chore: run cargo fmt 2024-12-23 19:38:33 +01:00
000003103e solution: day 23 2024-12-23 19:38:23 +01:00
0000030073 solution: day 22 2024-12-22 12:41:07 +01:00
00000290eb perf: cache bfs for day 21 2024-12-21 21:29:04 +01:00
0000028054 solution: day 21 2024-12-21 17:51:56 +01:00
00000270f6 solution: day 20 2024-12-21 17:51:15 +01:00
16 changed files with 813 additions and 259 deletions

188
Cargo.lock generated
View File

@@ -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",
]

View File

@@ -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"

4
data/examples/22.txt Normal file
View File

@@ -0,0 +1,4 @@
1
2
3
2024

32
data/examples/23.txt Normal file
View File

@@ -0,0 +1,32 @@
kh-tc
qp-kh
de-cg
ka-co
yn-aq
qp-ub
cg-tb
vc-aq
tb-ka
wh-tc
yn-cg
kh-ub
ta-co
de-co
tc-td
tb-wq
wh-td
ta-ka
td-qp
aq-cg
wq-ub
ub-vc
de-ta
wq-aq
wq-vc
wh-yn
ka-de
kh-ta
co-tc
wh-qp
tb-vc
td-yn

47
data/examples/24.txt Normal file
View File

@@ -0,0 +1,47 @@
x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
y00: 1
y01: 1
y02: 1
y03: 1
y04: 1
ntg XOR fgs -> mjb
y02 OR x01 -> tnw
kwq OR kpj -> z05
x00 OR x03 -> fst
tgd XOR rvg -> z01
vdt OR tnw -> bfw
bfw AND frj -> z10
ffh OR nrd -> bqk
y00 AND y03 -> djm
y03 OR y00 -> psh
bqk OR frj -> z08
tnw OR fst -> frj
gnj AND tgd -> z11
bfw XOR mjb -> z00
x03 OR x00 -> vdt
gnj AND wpb -> z02
x04 AND y00 -> kjc
djm OR pbm -> qhw
nrd AND vdt -> hwm
kjc AND fst -> rvg
y04 OR y02 -> fgs
y01 AND x02 -> pbm
ntg OR kjc -> kwq
psh XOR fgs -> tgd
qhw XOR tgd -> z09
pbm OR djm -> kpj
x03 XOR y03 -> ffh
x00 XOR y04 -> ntg
bfw OR bqk -> z06
nrd XOR fgs -> wpb
frj XOR qhw -> z04
bqk OR frj -> z07
y03 OR x01 -> nrd
hwm AND bqk -> z03
tgd XOR rvg -> z12
tnw OR pbm -> gnj

39
data/examples/25.txt Normal file
View File

@@ -0,0 +1,39 @@
#####
.####
.####
.####
.#.#.
.#...
.....
#####
##.##
.#.##
...##
...#.
...#.
.....
.....
#....
#....
#...#
#.#.#
#.###
#####
.....
.....
#.#..
###..
###.#
###.#
#####
.....
.....
.....
#....
#.#..
#.#.#
#####

View File

@@ -200,10 +200,16 @@ mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 15)), Some(10092));
assert_eq!(
part_one(&aoc::template::read_file("examples", 15)),
Some(10092)
);
}
#[test]
fn test_part_two() {
assert_eq!(part_two(&aoc::template::read_file("examples", 15)), Some(9021));
assert_eq!(
part_two(&aoc::template::read_file("examples", 15)),
Some(9021)
);
}
}

View File

@@ -91,4 +91,3 @@ pub fn part_two(input: &str) -> Option<String> {
}
aoc::solution!(18);

View File

@@ -66,6 +66,9 @@ mod tests {
}
#[test]
fn test_part_two() {
assert_eq!(part_two(&aoc::template::read_file("examples", 19)), Some(16));
assert_eq!(
part_two(&aoc::template::read_file("examples", 19)),
Some(16)
);
}
}

View File

@@ -133,16 +133,3 @@ pub fn part_two(input: &str) -> Option<usize> {
}
aoc::solution!(20);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 20)), None);
}
#[test]
fn test_part_two() {
assert_eq!(part_two(&aoc::template::read_file("examples", 20)), None);
}
}

View File

@@ -1,249 +1,143 @@
use std::iter::once;
use std::collections::VecDeque;
use hashbrown::{HashMap, HashSet};
use hashbrown::HashMap;
use itertools::Itertools;
const NUMPAD: [(&str, &str); 110] = [
// 0 - everywhere
("01", "^<"),
("02", "^"),
("03", "^>"),
("04", "^^<"),
("05", "^^"),
("06", "^^>"),
("07", "^^^<"),
("08", "^^^"),
("09", "^^^>"),
("0A", ">"),
// 1 - everywhere
("10", ">v"),
("12", ">"),
("13", ">>"),
("14", "^"),
("15", "^>"),
("16", "^>>"),
("17", "^^"),
("18", "^^>"),
("19", "^^>>"),
("1A", ">>v"),
// 2 - everywhere
("20", "v"),
("21", "<"),
("23", ">"),
("24", "^<"),
("25", "^"),
("26", "^>"),
("27", "^^<"),
("28", "^^"),
("29", "^^>"),
("2A", ">v"),
// 3 - everywhere
("30", "v<"),
("31", "<<"),
("32", "<"),
("34", "^<<"),
("35", "^<"),
("36", "^"),
("37", "^^<<"),
("38", "^^<"),
("39", "^^"),
("3A", "v"),
// 4 - everywhere
("40", ">vv"),
("41", "v"),
("42", "v>"),
("43", "v>>"),
("45", ">"),
("46", ">>"),
("47", "^"),
("48", "^>"),
("49", "^>>"),
("4A", ">>vv"),
// 5 - everywhere
("50", "vv"),
("51", "v<"),
("52", "v"),
("53", "v>"),
("54", "<"),
("56", ">"),
("57", "^<"),
("58", "^"),
("59", "^>"),
("5A", ">vv"),
// 6 - everywhere
("60", "vv<"),
("61", "v<<"),
("62", "v<"),
("63", "v"),
("64", "<<"),
("65", "<"),
("67", "^<<"),
("68", "^<"),
("69", "^"),
("6A", "vv"),
// 7 - everywhere
("70", ">vvv"),
("71", "vv"),
("72", "vv>"),
("73", "vv>>"),
("74", "v"),
("75", "v>"),
("76", "v>>"),
("78", ">"),
("79", ">>"),
("7A", ">>vvv"),
// 8 - everywhere
("80", "vvv"),
("81", "vv<"),
("82", "vv"),
("83", "vv>"),
("84", "v<"),
("85", "v"),
("86", "v>"),
("87", "<"),
("89", ">"),
("8A", ">vvv"),
// 9 - everywhere
("90", "<vvv"),
("91", "vv<<"),
("92", "vv<"),
("93", "vv"),
("94", "v<<"),
("95", "v<"),
("96", "v"),
("97", "<<"),
("98", "<"),
("9A", "vvv"),
// A - everywhere
("A0", "<"),
("A1", "^<<"),
("A2", "^<"),
("A3", "^"),
("A4", "^^<<"),
("A5", "^^<"),
("A6", "^^"),
("A7", "^^^<<"),
("A8", "^^^<"),
("A9", "^^^"),
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]),
];
const KEYPAD: [(&str, &str); 20] = [
// < - everywhere
("<v", ">"),
("<>", ">>"),
("<^", ">^"),
("<A", ">>^"),
// v - everywhere
("v<", "<"),
("v>", ">"),
("v^", "^"),
("vA", ">^"),
// > - everywhere
("><", "<<"),
(">v", "<"),
(">^", "^<"),
(">A", "^"),
// ^ - everywhere
("^<", "v<"),
("^v", "v"),
("^>", "v>"),
("^A", ">"),
// A - everywhere
("A<", "v<<"),
("Av", "v<"),
("A>", "v"),
("A^", "<"),
#[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 build_move_options(s: &str) -> Vec<String> {
let mut set = HashSet::new();
#[cached(
ty = "SizedCache<String, Vec<VecDeque<u8>>>",
create = "{ SizedCache::with_size(150) }",
convert = r#"{ format!("{}{s}{e}", graph.len()) }"#
)]
fn bfs(graph: &HashMap<u8, Vec<(u8, u8)>>, s: u8, e: u8) -> Vec<VecDeque<u8>> {
let mut res = Vec::new();
for c in s.chars() {
set.insert(c);
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));
}
}
let mut v = vec![s.to_string()];
res
}
if set.len() == 2 {
v.push(s.chars().rev().collect())
#[cached(
ty = "SizedCache<String, usize>",
create = "{ SizedCache::with_size(100000) }",
convert = r#"{ format!("{sequence:?}{level}{graph}") }"#
)]
fn dfs(
graphs: &[HashMap<u8, Vec<(u8, u8)>>],
graph: usize,
mut sequence: VecDeque<u8>,
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();
}
}
v
res
}
fn build_graphs() -> [HashMap<u8, Vec<(u8, u8)>>; 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<usize> {
let mut keypad = HashMap::new();
for (k, v) in KEYPAD.iter() {
let key = k.as_bytes();
keypad.insert((key[0], key[1]), build_move_options(v));
}
let mut numpad = HashMap::new();
for (k, v) in NUMPAD.iter() {
let key = k.as_bytes();
numpad.insert((key[0], key[1]), build_move_options(v));
}
let mut s1s = Vec::new();
let mut s2s = Vec::new();
let mut s3s = Vec::new();
let mut s4s = Vec::new();
let mut c = 0;
for line in input.lines() {
s1s.clear();
s2s.clear();
s3s.clear();
s4s.clear();
s1s.push(once(&b'A').chain(line.as_bytes()).copied().collect_vec());
for mapper in [&numpad, &keypad, &keypad] {
for s1 in s1s.iter() {
s3s.push(vec![b'A']);
for i in 1..s1.len() {
let k2 = s1[i - 1];
let k1 = s1[i];
if mapper.contains_key(&(k1, k2)) {
for path in mapper[&(k1, k2)].iter() {
for s3 in s3s.iter() {
let mut cs3 = s3.clone();
cs3.extend(path.as_bytes());
s4s.push(cs3);
}
}
}
s3s.clear();
s3s.append(&mut s4s);
for s3 in s3s.iter_mut() {
s3.push(b'A');
}
}
s2s.append(&mut s3s);
}
s1s.clear();
s1s.append(&mut s2s);
}
let min_len = s1s.iter().map(|v| v.len()).min().unwrap();
let n: usize = line.strip_suffix("A").unwrap().parse().unwrap();
println!("{min_len}, {n}");
c += n * min_len;
}
c.into()
Some(solve_chain(input, 2))
}
pub fn part_two(input: &str) -> Option<usize> {
None
Some(solve_chain(input, 25))
}
aoc::solution!(21);
@@ -253,10 +147,16 @@ mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 21)), None);
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)), None);
assert_eq!(
part_two(&aoc::template::read_file("examples", 21)),
Some(154115708116294)
);
}
}

91
src/bin/22.rs Normal file
View File

@@ -0,0 +1,91 @@
use aoc::parsers::to_vec_map;
use hashbrown::{HashMap, HashSet};
fn pseudo_next(mut n: usize) -> usize {
n = ((n << 6) ^ n) % 16777216;
n = ((n >> 5) ^ n) % 16777216;
n = ((n << 11) ^ n) % 16777216;
n
}
struct MonkeyTrader {
numbers: [usize; 2001],
sequences: HashMap<[isize; 4], usize>,
}
impl MonkeyTrader {
fn new(seed: usize) -> Self {
let mut numbers = [0; 2001];
numbers[0] = seed;
let mut n = seed;
for num in numbers.iter_mut().skip(1) {
n = pseudo_next(n);
*num = n;
}
let mut sequences = HashMap::new();
for i in 4..2001 {
let mut key = [0; 4];
for j in 1..=4 {
let d1 = numbers[i - 4 + j - 1] % 10;
let d2 = numbers[i - 4 + j] % 10;
let diff = d2 as isize - d1 as isize;
key[j - 1] = diff;
}
if sequences.contains_key(&key) {
continue;
}
*sequences.entry(key).or_default() = numbers[i] % 10;
}
MonkeyTrader { numbers, sequences }
}
}
pub fn part_one(input: &str) -> Option<usize> {
to_vec_map(input, '\n', MonkeyTrader::new)
.into_iter()
.map(|x| x.numbers[2000])
.sum::<usize>()
.into()
}
pub fn part_two(input: &str) -> Option<usize> {
let traders = to_vec_map(input, '\n', MonkeyTrader::new);
let key_union: HashSet<[isize; 4]> = traders
.iter()
.flat_map(|x| x.sequences.keys().cloned())
.collect();
key_union
.into_iter()
.map(|k| {
traders
.iter()
.filter_map(|t| t.sequences.get(&k))
.sum::<usize>()
})
.max()
}
aoc::solution!(22);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(
part_one(&aoc::template::read_file("examples", 22)),
Some(37990510)
);
}
#[test]
fn test_part_two() {
assert_eq!(
part_two(&aoc::template::read_file("examples", 22)),
Some(23)
);
}
}

88
src/bin/23.rs Normal file
View File

@@ -0,0 +1,88 @@
use std::collections::BTreeSet;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
fn parse_graph(input: &str) -> HashMap<&str, BTreeSet<&str>> {
let nodes: HashSet<_> = input.lines().filter_map(|x| x.split_once('-')).collect();
let mut graph: HashMap<_, BTreeSet<_>> = HashMap::new();
for (n1, n2) in nodes.iter().copied() {
graph.entry(n1).or_default().insert(n2);
graph.entry(n2).or_default().insert(n1);
}
graph
}
pub fn part_one(input: &str) -> Option<usize> {
let graph = parse_graph(input);
let mut triples = HashSet::new();
for (n1, neigh) in graph.iter() {
for n2 in neigh.iter().copied() {
for n3 in graph[n2].iter() {
if graph[n3].contains(n1) && n1.starts_with('t') {
let mut triple = [n1, n2, n3];
triple.sort();
triples.insert(triple);
}
}
}
}
triples.len().into()
}
pub fn part_two(input: &str) -> Option<String> {
let graph = parse_graph(input);
let p: BTreeSet<_> = graph.keys().copied().collect();
let r = HashSet::new();
let x = BTreeSet::new();
let mut maximals = HashSet::new();
let mut stack = Vec::new();
stack.push((r, p, x));
while let Some((r, mut p, mut x)) = stack.pop() {
if p.is_empty() && x.is_empty() {
let mut password = r.iter().copied().collect_vec();
password.sort();
maximals.insert(password.join(","));
}
while let Some(pp) = p.pop_last() {
let mut nr = r.clone();
nr.insert(pp);
let np = &p & &graph[pp];
let nx = &x & &graph[pp];
stack.push((nr, np, nx));
x.insert(pp);
}
}
maximals.into_iter().max_by_key(|x| x.len())
}
aoc::solution!(23);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 23)), Some(7));
}
#[test]
fn test_part_two() {
assert_eq!(
part_two(&aoc::template::read_file("examples", 23)),
Some("co,de,ka,ta".to_string())
);
}
}

132
src/bin/24.rs Normal file
View File

@@ -0,0 +1,132 @@
use std::collections::VecDeque;
use hashbrown::HashMap;
use itertools::Itertools;
use regex::Regex;
#[derive(Debug, Clone, Copy)]
enum Gate {
And,
Or,
XOr,
}
#[derive(Debug)]
struct LogicGate<'a> {
input_one: &'a str,
input_two: &'a str,
output: &'a str,
gate: Gate,
}
fn parse_input(input: &str) -> (HashMap<&str, bool>, Vec<LogicGate>) {
let (uvals, uevals) = input.split_once("\n\n").unwrap();
let vals: HashMap<_, _> = uvals
.lines()
.map(|x| x.split_once(": ").unwrap())
.map(|(k, v)| (k, v == "1"))
.collect();
let mut logic_gates = Vec::new();
let re = Regex::new(r#"((?:\w|\d){3}) (AND|XOR|OR) ((?:\w|\d){3}) -> ((?:\w|\d){3})"#).unwrap();
for capt in re.captures_iter(uevals) {
let input_one = capt.get(1).unwrap().as_str();
let gate = match capt.get(2).unwrap().as_str() {
"AND" => Gate::And,
"OR" => Gate::Or,
"XOR" => Gate::XOr,
_ => unreachable!(),
};
let input_two = capt.get(3).unwrap().as_str();
let output = capt.get(4).unwrap().as_str();
logic_gates.push(LogicGate {
input_one,
input_two,
output,
gate,
})
}
(vals, logic_gates)
}
pub fn part_one(input: &str) -> Option<usize> {
let (mut vals, logic_gates) = parse_input(input);
let mut queue = VecDeque::from(logic_gates);
while let Some(s) = queue.pop_front() {
if !vals.contains_key(s.input_one) || !vals.contains_key(s.input_two) {
queue.push_back(s);
continue;
}
let v1 = vals[s.input_one];
let v2 = vals[s.input_two];
let o = match s.gate {
Gate::And => v1 & v2,
Gate::Or => v1 | v2,
Gate::XOr => v1 ^ v2,
};
vals.insert(s.output, o);
}
let mut zs = vals
.into_iter()
.filter(|x| x.0.starts_with('z'))
.collect_vec();
zs.sort();
zs.into_iter()
.enumerate()
.filter(|x| x.1 .1)
.map(|(i, _)| 1 << i)
.sum::<usize>()
.into()
}
pub fn part_two(_input: &str) -> Option<String> {
// Used graphviz for this part
// ```rs
// let (_, logic_gates) = parse_input(input);
// println!("digraph A {{");
// for (idx, lg) in logic_gates.iter().enumerate() {
// let LogicGate {
// input_one: i1,
// input_two: i2,
// output: o1,
// gate: g,
// } = lg;
// println!("{i1} -> {g:?}_{idx}");
// println!("{i2} -> {g:?}_{idx}");
// println!("{g:?}_{idx} -> {o1}");
// }
// println!("}}");
// ```
//
// and then pipe to dot
// ```bash
// ... | dot -Tsvg -Kneato > grpah.svg
// ```
//
// i looked for errors in the pattern and found below
// solution for my input
let mut res = ["fkp", "z06", "z11", "ngr", "z31", "mfm", "bpt", "krj"];
res.sort();
res.join(",").to_string().into()
}
aoc::solution!(24);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(
part_one(&aoc::template::read_file("examples", 24)),
Some(2024)
);
}
}

57
src/bin/25.rs Normal file
View File

@@ -0,0 +1,57 @@
pub fn part_one(input: &str) -> Option<usize> {
let mut keys = Vec::new();
let mut locks = Vec::new();
for key_or_lock in input.split("\n\n") {
let lock = key_or_lock
.lines()
.next()
.unwrap()
.chars()
.all(|x| x == '#');
let mut columns = [0; 5];
for (idx, c) in key_or_lock.lines().flat_map(|x| x.chars().enumerate()) {
if c == '#' {
columns[idx] += 1;
}
}
// remove top or bottom row from count
for c in columns.iter_mut() {
*c -= 1;
}
if lock {
locks.push(columns);
} else {
keys.push(columns);
}
}
let mut count = 0;
for k in keys.iter() {
for l in locks.iter() {
if k.iter().zip(l.iter()).all(|(k, l)| k + l <= 5) {
count += 1;
}
}
}
count.into()
}
pub fn part_two(_input: &str) -> Option<String> {
"Happy christmas!".to_string().into()
}
aoc::solution!(25);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
assert_eq!(part_one(&aoc::template::read_file("examples", 25)), Some(3));
}
}

View File

@@ -1,9 +1,7 @@
mod grid;
mod direction;
mod grid;
mod point;
pub use grid::Grid;
pub use direction::Direction;
pub use grid::Grid;
pub use point::Point;