advent_of_code/year2020/
day19.rs1use crate::input::Input;
2
3#[derive(Clone)]
4enum Rule {
5 Character(u8),
7
8 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 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}