year2016/
day07.rs

1use utils::prelude::*;
2
3/// Matching palindromes in bracketed sequences.
4#[derive(Clone, Debug)]
5pub struct Day07 {
6    part1: u32,
7    part2: u32,
8}
9
10impl Day07 {
11    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
12        // As the input is so large (~175KiB) do everything (validation, part 1, part 2) in 1 pass
13        let mut part1_total = 0;
14        let mut part2_total = 0;
15
16        for line in input.lines().map(|l| l.as_bytes()) {
17            let mut part1_valid = true;
18            let mut part1_match = false;
19
20            let mut part2_pairs = [0u8; 26 * 26];
21            let mut part2_match = false;
22
23            let mut in_brackets = false;
24            for i in 0..line.len() {
25                // Validation
26                if line[i] == b'[' {
27                    if in_brackets {
28                        return Err(InputError::new(
29                            input,
30                            &line[i..],
31                            "unexpected nested bracket",
32                        ));
33                    }
34                    in_brackets = true;
35                    continue;
36                }
37
38                if line[i] == b']' {
39                    if !in_brackets {
40                        return Err(InputError::new(
41                            input,
42                            &line[i..],
43                            "unexpected close bracket",
44                        ));
45                    }
46                    in_brackets = false;
47                    continue;
48                }
49
50                if !line[i].is_ascii_lowercase() {
51                    return Err(InputError::new(input, &line[i..], "unexpected character"));
52                }
53
54                // Shared
55                if i + 2 >= line.len() || !line[i + 1].is_ascii_lowercase() {
56                    continue; // Can't match either part
57                }
58                let (a, b, c) = (line[i], line[i + 1], line[i + 2]);
59
60                // Part 1
61                if let Some(&d) = line.get(i + 3)
62                    && a != b
63                    && a == d
64                    && b == c
65                {
66                    if in_brackets {
67                        part1_valid = false;
68                    } else {
69                        part1_match = true;
70                    }
71                }
72
73                // Part 2
74                if a != b && a == c && !part2_match {
75                    let index;
76                    if in_brackets {
77                        index = (b - b'a') as usize * 26 + (a - b'a') as usize; // Reversed
78                        part2_pairs[index] |= 1;
79                    } else {
80                        index = (a - b'a') as usize * 26 + (b - b'a') as usize;
81                        part2_pairs[index] |= 2;
82                    }
83                    part2_match = part2_pairs[index] == 3;
84                }
85            }
86
87            if in_brackets {
88                return Err(InputError::new(
89                    input,
90                    &line[line.len()..],
91                    "expected close bracket",
92                ));
93            }
94
95            if part1_valid && part1_match {
96                part1_total += 1;
97            }
98            if part2_match {
99                part2_total += 1;
100            }
101        }
102
103        Ok(Self {
104            part1: part1_total,
105            part2: part2_total,
106        })
107    }
108
109    #[must_use]
110    pub fn part1(&self) -> u32 {
111        self.part1
112    }
113
114    #[must_use]
115    pub fn part2(&self) -> u32 {
116        self.part2
117    }
118}
119
120examples!(Day07 -> (u32, u32) [
121    {input: "abba[mnop]qrst", part1: 1},
122    {input: "abcd[bddb]xyyx", part1: 0},
123    {input: "aaaa[qwer]tyui", part1: 0},
124    {input: "ioxxoj[asdfgh]zxcvbn", part1: 1},
125    {input: "aba[bab]xyz", part2: 1},
126    {input: "xyx[xyx]xyx", part2: 0},
127    {input: "aaa[kek]eke", part2: 1},
128    {input: "zazbz[bzb]cdb", part2: 1},
129]);