Skip to main content

advent_of_code/year2015/
day11.rs

1use crate::input::Input;
2use std::collections::HashSet;
3
4fn is_valid(password: &[u8]) -> bool {
5    // "Passwords must include one increasing straight of at least three letters,
6    // like abc, bcd, cde, and so on, up to xyz. They cannot skip letters; abd doesn't count."
7    if !password
8        .windows(3)
9        .any(|w| w[1] == w[0] + 1 && w[2] == w[1] + 1)
10    {
11        return false;
12    }
13
14    // "Passwords may not contain the letters i, o, or l, as these letters can be mistaken for other characters and are therefore confusing."
15    if password.iter().any(|b| [b'i', b'o', b'l'].contains(b)) {
16        return false;
17    }
18
19    // Passwords must contain at least two different, non-overlapping pairs of letters, like aa, bb, or zz.
20    let mut pairs = HashSet::new();
21    for window in password.windows(2) {
22        if window[0] == window[1] {
23            pairs.insert(window);
24        }
25    }
26    pairs.len() > 1
27}
28
29pub fn solve(input: &Input) -> Result<String, String> {
30    let bytes = input.text.as_bytes();
31    if bytes.len() != 8 || bytes.iter().any(|b| !b.is_ascii_lowercase()) {
32        return Err("Invalid current password (not 8 lower ASCII characters)".to_string());
33    }
34
35    let mut current_password = [0_u8; 8];
36    current_password.copy_from_slice(bytes);
37    let mut return_next_password = input.is_part_one();
38
39    'outer: loop {
40        if is_valid(&current_password) {
41            if return_next_password {
42                return Ok(std::str::from_utf8(&current_password)
43                    .map_err(|_| "Invalid utf-8 in password")?
44                    .to_string());
45            } else {
46                return_next_password = true;
47            }
48        }
49        if current_password[7] == b'z' {
50            for idx in (0..=6).rev() {
51                if current_password[idx] != b'z' {
52                    current_password[idx] += 1;
53                    for c in current_password.iter_mut().skip(idx + 1) {
54                        *c = b'a';
55                    }
56                    continue 'outer;
57                }
58            }
59            return Err("Unable to generate valid password".to_string());
60        } else {
61            current_password[7] += 1;
62        }
63    }
64}
65
66#[test]
67pub fn tests() {
68    test_part_one!("abcdefgh" => "abcdffaa".to_string());
69
70    let real_input = include_str!("day11_input.txt");
71    test_part_one!(real_input => "hepxxyzz".to_string());
72    test_part_two!(real_input => "heqaabcc".to_string());
73}