1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! # Day 5: Doesn't He Have Intern-Elves For This?
//!
//! Santa needs help figuring out which strings in his text file are naughty or nice.
//!
//! A **nice string** is one with all of the following properties:
//!
//! - It contains at least three vowels (`aeiou` only), like `aei`, `xazegov`, or `aeiouaeiouaeiou`.
//! - It contains at least one letter that appears twice in a row, like `xx`, `abcdde` (`dd`), or
//!   `aabbccdd` (`aa`, `bb`, `cc`, or `dd`).
//! - It does **not** contain the strings `ab`, `cd`, `pq`, or `xy`, even if they are part of one of
//!   the other requirements.
//!
//! For example:
//!
//! - `ugknbfddgicrmopn` is nice because it has at least three vowels (`u...i...o...`), a double
//!   letter (`...dd...`), and none of the disallowed substrings.
//! - `aaa` is nice because it has at least three vowels and a double letter, even though the
//!   letters used by different rules overlap.
//! - `jchzalrnumimnmhp` is naughty because it has no double letter.
//! - `haegwjzuvuyypxyu` is naughty because it contains the string `xy`.
//! - `dvszwmarrgswjxmb` is naughty because it contains only one vowel.
//!
//! How many strings are nice?

use anyhow::Result;

pub const INPUT: &str = include_str!("d05.txt");

const VOWELS: &[char] = &['a', 'i', 'e', 'o', 'u'];
const BAD_STRINGS: &[[char; 2]] = &[['a', 'b'], ['c', 'd'], ['p', 'q'], ['x', 'y']];

pub fn solve_part_one(input: &str) -> Result<usize> {
    Ok(input
        .lines()
        .map(|l| {
            let mut vowels = 0;
            let mut double = false;
            let mut bad = false;

            l.chars().fold(None, |prev, c| {
                if VOWELS.contains(&c) {
                    vowels += 1;
                }

                if let Some(p) = prev {
                    double = double || p == c;
                    bad = bad || BAD_STRINGS.contains(&[p, c])
                }

                Some(c)
            });

            usize::from(vowels >= 3 && double && !bad)
        })
        .sum())
}

pub fn solve_part_two(input: &str) -> Result<i64> {
    Ok(0)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part_one() {
        assert_eq!(1, solve_part_one("ugknbfddgicrmopn").unwrap());
        assert_eq!(1, solve_part_one("aaa").unwrap());
        assert_eq!(0, solve_part_one("jchzalrnumimnmhp").unwrap());
        assert_eq!(0, solve_part_one("haegwjzuvuyypxyu").unwrap());
        assert_eq!(0, solve_part_one("dvszwmarrgswjxmb").unwrap());
    }

    #[test]
    fn part_two() {}
}