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                    if a != b && a == d && b == c {
63                        if in_brackets {
64                            part1_valid = false;
65                        } else {
66                            part1_match = true;
67                        }
68                    }
69                }
70
71                // Part 2
72                if a != b && a == c && !part2_match {
73                    let index;
74                    if in_brackets {
75                        index = (b - b'a') as usize * 26 + (a - b'a') as usize; // Reversed
76                        part2_pairs[index] |= 1;
77                    } else {
78                        index = (a - b'a') as usize * 26 + (b - b'a') as usize;
79                        part2_pairs[index] |= 2;
80                    }
81                    part2_match = part2_pairs[index] == 3;
82                }
83            }
84
85            if in_brackets {
86                return Err(InputError::new(
87                    input,
88                    &line[line.len()..],
89                    "expected close bracket",
90                ));
91            }
92
93            if part1_valid && part1_match {
94                part1_total += 1;
95            }
96            if part2_match {
97                part2_total += 1;
98            }
99        }
100
101        Ok(Self {
102            part1: part1_total,
103            part2: part2_total,
104        })
105    }
106
107    #[must_use]
108    pub fn part1(&self) -> u32 {
109        self.part1
110    }
111
112    #[must_use]
113    pub fn part2(&self) -> u32 {
114        self.part2
115    }
116}
117
118examples!(Day07 -> (u32, u32) [
119    {input: "abba[mnop]qrst", part1: 1},
120    {input: "abcd[bddb]xyyx", part1: 0},
121    {input: "aaaa[qwer]tyui", part1: 0},
122    {input: "ioxxoj[asdfgh]zxcvbn", part1: 1},
123    {input: "aba[bab]xyz", part2: 1},
124    {input: "xyx[xyx]xyx", part2: 0},
125    {input: "aaa[kek]eke", part2: 1},
126    {input: "zazbz[bzb]cdb", part2: 1},
127]);