advent_of_code/year2016/
day14.rs1use crate::common::md5::Context;
2use crate::input::Input;
3
4fn to_hash_chars(hash: &[u8]) -> [u8; 32] {
5 let mut hash_chars = [0_u8; 32];
6 for i in 0..32 {
7 hash_chars[i] = if i % 2 == 0 {
8 (hash[i / 2] & 0xF0) >> 4
9 } else {
10 hash[i / 2] & 0x0F
11 };
12 }
13 hash_chars
14}
15
16fn first_triplet(hash: &[u8]) -> Option<u8> {
17 let hash_chars = to_hash_chars(hash);
18 hash_chars
19 .windows(3)
20 .find(|w| w[0] == w[1] && w[1] == w[2])
21 .map(|w| w[0])
22}
23
24fn contains_five_in_a_row(hash: &[u8], desired_char: u8) -> bool {
25 let hash_chars = to_hash_chars(hash);
26 hash_chars
27 .windows(5)
28 .any(|w| w[0] == desired_char && w.windows(2).all(|adjacent| adjacent[0] == adjacent[1]))
29}
30
31pub fn solve(input: &Input) -> Result<u32, String> {
32 let salt = input.text;
33 if salt.len() > 8 {
34 return Err("Too long salt (max length: 8)".to_string());
35 }
36
37 let mut hash_cache = Vec::new();
38 let mut orig_hasher = Context::new();
39 orig_hasher.consume(salt.as_bytes());
40
41 for i in 0..1000 {
42 let mut hasher = orig_hasher.clone();
43 hasher.consume(i.to_string().as_bytes());
44 if input.is_part_two() {
45 for _ in 0..2016 {
46 let hash: [u8; 16] = hasher.compute();
47 let hash_str = to_hash_chars(&hash)
48 .iter()
49 .map(|&b| if b <= 9 { b'0' + b } else { b'a' + (b - 10) })
50 .collect::<Vec<_>>();
51 hasher = Context::new();
52 hasher.consume(&hash_str);
53 }
54 }
55 let hash: [u8; 16] = hasher.compute();
56 hash_cache.push(hash);
57 }
58
59 let mut valid_key_count = 0;
60 let mut index = 0;
61 loop {
62 let current_hash = hash_cache[index % 1000];
63 hash_cache[index % 1000] = {
64 let content_to_hash = format!("{}{}", salt, index + 1000);
65 let mut hasher = Context::new();
66 hasher.consume(content_to_hash.as_bytes());
67 if input.is_part_two() {
68 for _ in 0..2016 {
69 let hash: [u8; 16] = hasher.compute();
70 hasher = Context::new();
71 let hash_str = to_hash_chars(&hash)
72 .iter()
73 .map(|&b| if b <= 9 { b'0' + b } else { b'a' + (b - 10) })
74 .collect::<Vec<_>>();
75 hasher.consume(&hash_str);
76 }
77 }
78 hasher.compute()
79 };
80
81 if let Some(triplet_value) = first_triplet(¤t_hash) {
82 if hash_cache
83 .iter()
84 .any(|hash| contains_five_in_a_row(hash, triplet_value))
85 {
86 valid_key_count += 1;
87 if valid_key_count == 64 {
88 return Ok(index as u32);
89 }
90 }
91 }
92
93 if index > 100_000 {
94 break;
95 }
96 index += 1;
97 }
98
99 Err("Time out".to_string())
100}
101
102#[test]
103pub fn tests() {
104 use crate::input::{test_part_one, test_part_two};
105
106 let real_input = include_str!("day14_input.txt");
107 test_part_one!(real_input => 15168);
108 test_part_two!(real_input => 20864);
109}