Skip to main content

advent_of_code/year2020/
day19.rs

1use crate::input::Input;
2
3#[derive(Clone)]
4enum Rule {
5    /// A literal character represented by its ASCII value.
6    Character(u8),
7
8    /// A list of options, where each option is a sequence of rule id:s.
9    ///
10    /// Example: The rule
11    ///     "119 92 | 87 13"
12    /// is represented as:
13    ///     [[119, 92], [87, 13]]
14    Sequences(Vec<Vec<RuleId>>),
15}
16
17impl Rule {
18    fn parse(rule_str: &str) -> Result<Self, ()> {
19        if rule_str.is_empty() {
20            return Err(());
21        }
22
23        let bytes = rule_str.as_bytes();
24        Ok(if bytes[0] == b'"' {
25            if bytes.len() != 3 || bytes[2] != b'"' {
26                return Err(());
27            }
28            Self::Character(bytes[1])
29        } else {
30            let sequences = rule_str
31                .split(" | ")
32                .map(|s| s.split(' ').map(str::parse).collect())
33                .collect::<Result<_, _>>()
34                .map_err(|_| ())?;
35            Self::Sequences(sequences)
36        })
37    }
38}
39
40type RuleId = u8;
41
42struct Rules {
43    /// The rules indexed by rule id:s.
44    rules: Vec<Rule>,
45}
46
47impl Rules {
48    fn parse(rules_str: &str) -> Result<Self, ()> {
49        let mut rules = Self {
50            rules: vec![Rule::Character(0); 255],
51        };
52        for rule_line in rules_str.lines() {
53            rules.add_line(rule_line)?;
54        }
55        Ok(rules)
56    }
57
58    fn add_line(&mut self, rule_line: &str) -> Result<(), ()> {
59        let (rule_idx_str, pattern_str) = rule_line.rsplit_once(": ").ok_or(())?;
60
61        let rule_idx = rule_idx_str.parse::<RuleId>().map_err(|_| ())?;
62        let pattern = Rule::parse(pattern_str)?;
63
64        self.rules[rule_idx as usize] = pattern;
65        Ok(())
66    }
67
68    fn matches(&self, line: &str) -> bool {
69        struct PartialMatch<'a> {
70            remaining_input: &'a [u8],
71            remaining_sequence: Vec<RuleId>,
72        }
73
74        if line.is_empty() {
75            return false;
76        }
77
78        let mut stack = vec![PartialMatch {
79            remaining_input: line.as_bytes(),
80            remaining_sequence: vec![0],
81        }];
82
83        while let Some(partial_match) = stack.pop() {
84            match &self.rules[partial_match.remaining_sequence[0] as usize] {
85                &Rule::Character(value) => {
86                    if partial_match.remaining_input[0] == value {
87                        let end_of_input = partial_match.remaining_input.len() == 1;
88                        let end_of_rule_sequence = partial_match.remaining_sequence.len() == 1;
89
90                        match (end_of_input, end_of_rule_sequence) {
91                            (true, true) => {
92                                return true;
93                            }
94                            (false, false) => {
95                                stack.push(PartialMatch {
96                                    remaining_input: &partial_match.remaining_input[1..],
97                                    remaining_sequence: partial_match.remaining_sequence[1..]
98                                        .to_vec(),
99                                });
100                            }
101                            _ => {}
102                        }
103                    }
104                }
105                Rule::Sequences(sequences) => {
106                    for chosen_sequence in sequences.iter() {
107                        let mut remaining_sequence = chosen_sequence.clone();
108                        remaining_sequence.extend(&partial_match.remaining_sequence[1..]);
109
110                        stack.push(PartialMatch {
111                            remaining_input: partial_match.remaining_input,
112                            remaining_sequence,
113                        });
114                    }
115                }
116            }
117        }
118        false
119    }
120}
121
122pub fn solve(input: &Input) -> Result<u64, String> {
123    let on_error = || "Invalid input".to_string();
124    let map_error = |_| on_error();
125
126    let (rules_str, messages_str) = input.text.split_once("\n\n").ok_or_else(on_error)?;
127
128    let mut rules = Rules::parse(rules_str).map_err(map_error)?;
129
130    if input.is_part_two() {
131        rules.add_line("8: 42 | 42 8").map_err(map_error)?;
132        rules.add_line("11: 42 31 | 42 11 31").map_err(map_error)?;
133    }
134
135    Ok(messages_str
136        .lines()
137        .filter(|line| rules.matches(line))
138        .count() as u64)
139}
140
141#[test]
142pub fn tests() {
143    let example_part_one = "0: 4 1 5
1441: 2 3 | 3 2
1452: 4 4 | 5 5
1463: 4 5 | 5 4
1474: \"a\"
1485: \"b\"
149
150ababbb
151bababa
152abbbab
153aaabbb
154aaaabbb";
155    test_part_one!(example_part_one => 2);
156    let example_part_two = "42: 9 14 | 10 1
1579: 14 27 | 1 26
15810: 23 14 | 28 1
1591: \"a\"
16011: 42 31
1615: 1 14 | 15 1
16219: 14 1 | 14 14
16312: 24 14 | 19 1
16416: 15 1 | 14 14
16531: 14 17 | 1 13
1666: 14 14 | 1 14
1672: 1 24 | 14 4
1680: 8 11
16913: 14 3 | 1 12
17015: 1 | 14
17117: 14 2 | 1 7
17223: 25 1 | 22 14
17328: 16 1
1744: 1 1
17520: 14 14 | 1 15
1763: 5 14 | 16 1
17727: 1 6 | 14 18
17814: \"b\"
17921: 14 1 | 1 14
18025: 1 1 | 1 14
18122: 14 14
1828: 42
18326: 14 22 | 1 20
18418: 15 15
1857: 14 5 | 1 21
18624: 14 1
187
188abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa
189bbabbbbaabaabba
190babbbbaabbbbbabbbbbbaabaaabaaa
191aaabbbbbbaaaabaababaabababbabaaabbababababaaa
192bbbbbbbaaaabbbbaaabbabaaa
193bbbababbbbaaaaaaaabbababaaababaabab
194ababaaaaaabaaab
195ababaaaaabbbaba
196baabbaaaabbaaaababbaababb
197abbbbabbbbaaaababbbbbbaaaababb
198aaaaabbaabaaaaababaa
199aaaabbaaaabbaaa
200aaaabbaabbaaaaaaabbbabbbaaabbaabaaa
201babaaabbbaaabaababbaabababaaab
202aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba";
203    test_part_two!(example_part_two => 12);
204
205    let real_input = include_str!("day19_input.txt");
206    test_part_one!(real_input => 126);
207    test_part_two!(real_input => 282);
208}