diff --git a/day14/Cargo.lock b/day14/Cargo.lock new file mode 100644 index 0000000..039737f --- /dev/null +++ b/day14/Cargo.lock @@ -0,0 +1,32 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "day14" +version = "0.1.0" +dependencies = [ + "itertools", + "md5", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + +[[package]] +name = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" diff --git a/day14/Cargo.toml b/day14/Cargo.toml new file mode 100644 index 0000000..128f681 --- /dev/null +++ b/day14/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day14" +version = "0.1.0" +edition = "2024" + +[dependencies] +md5 = "0.8.0" +itertools = "*" diff --git a/day14/src/main.rs b/day14/src/main.rs new file mode 100644 index 0000000..2ffe2b0 --- /dev/null +++ b/day14/src/main.rs @@ -0,0 +1,99 @@ +use std::collections::VecDeque; + +use itertools::Itertools; + +#[cfg(debug_assertions)] +const INPUT: &str = "abc"; +#[cfg(not(debug_assertions))] +const INPUT: &str = "ihaygndm"; + +fn main() { + part_1(); + part_2(); +} + +fn part_2() { + let mut next_hashes = VecDeque::new(); + + let mut idx = 0; + let mut found_key_counter = 0; + + loop { + while next_hashes.len() < 1_000 { + next_hashes.push_back(calculate_hash_repeated(INPUT, idx, 2016)); + idx += 1; + } + + let this_hash = next_hashes.pop_front().unwrap(); + + if let Some(found_char) = find_triple_char(&this_hash) + && next_hashes + .iter() + .any(|s| contains_quintuple(s.as_str(), found_char)) + { + found_key_counter += 1; + if found_key_counter == 64 { + println!("{}", idx - 1000); + break; + } + } + } +} + +fn part_1() { + let mut next_hashes = VecDeque::new(); + + let mut idx = 0; + let mut found_key_counter = 0; + + loop { + while next_hashes.len() < 1_000 { + next_hashes.push_back(calculate_hash(INPUT, idx)); + idx += 1; + } + + let this_hash = next_hashes.pop_front().unwrap(); + + if let Some(found_char) = find_triple_char(&this_hash) + && next_hashes + .iter() + .any(|s| contains_quintuple(s.as_str(), found_char)) + { + found_key_counter += 1; + if found_key_counter == 64 { + println!("{}", idx - 1000); + break; + } + } + } +} + +fn calculate_hash(s: &str, idx: usize) -> String { + format!("{:x}", md5::compute(format!("{s}{idx}"))) +} + +fn calculate_hash_repeated(s: &str, idx: usize, count: usize) -> String { + let mut hash = calculate_hash(s, idx); + + for _ in 0..count { + hash = format!("{:x}", md5::compute(hash)); + } + + hash +} + +fn find_triple_char(s: &str) -> Option { + s.chars() + .tuple_windows() + .filter(|(a, b, c)| a == b && b == c) + .map(|(c, _, _)| c) + .next() +} + +fn contains_quintuple(s: &str, value: char) -> bool { + s.chars().tuple_windows().any(|(a, b, c, d, e)| { + let head = a; + [b, c, d, e].into_iter().all(|ch| head == ch) // all items are identical + && head == value + }) +}