tangled
alpha
login
or
join now
jonas.tngl.sh
/
advent-of-code
0
fork
atom
Advent of Code solutions in Rust
0
fork
atom
overview
issues
pulls
pipelines
feat: part 1 of day 19 of AoC 2020
jonas.tngl.sh
3 months ago
c88b130f
e42bdb91
+189
-2
2 changed files
expand all
collapse all
unified
split
aoc_2020
src
day19.rs
main.rs
+187
aoc_2020/src/day19.rs
···
1
1
+
use anyhow::{Context, bail};
2
2
+
use aoc_companion::prelude::*;
3
3
+
use itertools::{Either, Itertools};
4
4
+
5
5
+
pub(crate) struct Door {
6
6
+
rules: Vec<Rule>,
7
7
+
messages: Vec<Box<[u8]>>,
8
8
+
}
9
9
+
10
10
+
impl<'input> Solution<'input> for Door {
11
11
+
fn parse(input: &'input str) -> Result<Self> {
12
12
+
let Some((rules, messages)) = input.split_once("\n\n") else {
13
13
+
bail!("missing empty line separating rules from messages");
14
14
+
};
15
15
+
let rules: Vec<Rule> = rules
16
16
+
.lines()
17
17
+
.map(|line| -> Result<(usize, Rule)> {
18
18
+
let Some((idx, body)) = line.split_once(':') else {
19
19
+
bail!("missing colon separating rule index from body");
20
20
+
};
21
21
+
let idx = idx.parse().context("failed to parse rule index")?;
22
22
+
23
23
+
let rule = if let Some(lit_str) = body.trim().strip_prefix('"') {
24
24
+
let Some(lit_str) = lit_str.strip_suffix('"') else {
25
25
+
bail!("missing closing quotation mark in string literal rule");
26
26
+
};
27
27
+
let &[byte] = lit_str.as_bytes() else {
28
28
+
bail!("string literal rule is more than one ASCII character");
29
29
+
};
30
30
+
Rule::Literal(byte)
31
31
+
} else {
32
32
+
body.split('|')
33
33
+
.map(|alt| {
34
34
+
alt.split_whitespace()
35
35
+
.map(|ref_str| {
36
36
+
ref_str
37
37
+
.parse()
38
38
+
.context("failed to parse rule reference index")
39
39
+
.map(RuleRef)
40
40
+
})
41
41
+
.try_collect()
42
42
+
.map(All)
43
43
+
})
44
44
+
.try_collect()
45
45
+
.map(Rule::Any)?
46
46
+
};
47
47
+
48
48
+
Ok((idx, rule))
49
49
+
})
50
50
+
.try_fold(Vec::new(), |mut rules, res| -> Result<Vec<Rule>> {
51
51
+
let (idx, rule) = res?;
52
52
+
if rules.len() <= idx {
53
53
+
rules.resize_with(idx + 1, || Rule::Any(vec![]));
54
54
+
}
55
55
+
rules[idx] = rule;
56
56
+
Ok(rules)
57
57
+
})?;
58
58
+
59
59
+
let messages = messages
60
60
+
.lines()
61
61
+
.map(|line| line.as_bytes().to_vec().into_boxed_slice())
62
62
+
.collect();
63
63
+
64
64
+
Ok(Self { rules, messages })
65
65
+
}
66
66
+
67
67
+
fn part1(&self) -> usize {
68
68
+
self.messages
69
69
+
.iter()
70
70
+
.filter(matches(RuleRef(0), &self.rules))
71
71
+
.count()
72
72
+
}
73
73
+
}
74
74
+
75
75
+
fn munch<'c>(
76
76
+
RuleRef(rule_idx): RuleRef,
77
77
+
rules: &[Rule],
78
78
+
candidate: &'c [u8],
79
79
+
) -> impl Iterator<Item = &'c [u8]> {
80
80
+
let rule = &rules[rule_idx];
81
81
+
match rule {
82
82
+
Rule::Literal(b) => Either::Left(candidate.strip_prefix(&[*b]).into_iter()),
83
83
+
Rule::Any(alternatives) => {
84
84
+
Either::Right(alternatives.iter().flat_map(move |All(rule_seq)| {
85
85
+
rule_seq
86
86
+
.iter()
87
87
+
.copied()
88
88
+
.fold(vec![candidate], |rests, rule_ref| {
89
89
+
rests
90
90
+
.into_iter()
91
91
+
.flat_map(|rest| munch(rule_ref, rules, rest))
92
92
+
.collect()
93
93
+
})
94
94
+
}))
95
95
+
}
96
96
+
}
97
97
+
}
98
98
+
99
99
+
fn matches<C: AsRef<[u8]>>(rule_ref: RuleRef, rules: &[Rule]) -> impl Fn(&C) -> bool {
100
100
+
move |candidate| munch(rule_ref, rules, candidate.as_ref()).contains(b"".as_slice())
101
101
+
}
102
102
+
103
103
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
104
104
+
struct RuleRef(usize);
105
105
+
106
106
+
#[derive(Clone, Debug, PartialEq, Eq)]
107
107
+
struct All(Vec<RuleRef>);
108
108
+
109
109
+
#[derive(Clone, Debug, PartialEq, Eq)]
110
110
+
enum Rule {
111
111
+
Literal(u8),
112
112
+
Any(Vec<All>),
113
113
+
}
114
114
+
115
115
+
#[cfg(test)]
116
116
+
mod tests {
117
117
+
use super::*;
118
118
+
119
119
+
const EXAMPLE_INPUT: &str = r#"0: 4 1 5
120
120
+
1: 2 3 | 3 2
121
121
+
2: 4 4 | 5 5
122
122
+
3: 4 5 | 5 4
123
123
+
4: "a"
124
124
+
5: "b"
125
125
+
126
126
+
ababbb
127
127
+
bababa
128
128
+
abbbab
129
129
+
aaabbb
130
130
+
aaaabbb"#;
131
131
+
132
132
+
fn example_rules() -> Vec<Rule> {
133
133
+
vec![
134
134
+
Rule::Any(vec![All(vec![RuleRef(4), RuleRef(1), RuleRef(5)])]),
135
135
+
Rule::Any(vec![
136
136
+
All(vec![RuleRef(2), RuleRef(3)]),
137
137
+
All(vec![RuleRef(3), RuleRef(2)]),
138
138
+
]),
139
139
+
Rule::Any(vec![
140
140
+
All(vec![RuleRef(4), RuleRef(4)]),
141
141
+
All(vec![RuleRef(5), RuleRef(5)]),
142
142
+
]),
143
143
+
Rule::Any(vec![
144
144
+
All(vec![RuleRef(4), RuleRef(5)]),
145
145
+
All(vec![RuleRef(5), RuleRef(4)]),
146
146
+
]),
147
147
+
Rule::Literal(b'a'),
148
148
+
Rule::Literal(b'b'),
149
149
+
]
150
150
+
}
151
151
+
152
152
+
const EXAMPLE_MESSAGES: &[&[u8]] = &[b"ababbb", b"bababa", b"abbbab", b"aaabbb", b"aaaabbb"];
153
153
+
154
154
+
#[test]
155
155
+
fn parse_example_input() {
156
156
+
let Door { rules, messages } = Door::parse(EXAMPLE_INPUT).unwrap();
157
157
+
itertools::assert_equal(rules, example_rules());
158
158
+
itertools::assert_equal(
159
159
+
messages.iter().map(|m| m.as_ref()),
160
160
+
EXAMPLE_MESSAGES.iter().copied(),
161
161
+
);
162
162
+
}
163
163
+
164
164
+
#[test]
165
165
+
fn munch_some_rules() {
166
166
+
let rules = example_rules();
167
167
+
itertools::assert_equal(munch(RuleRef(4), &rules, b"ababbb"), [b"babbb"]);
168
168
+
itertools::assert_equal(munch(RuleRef(3), &rules, b"babbb"), [b"bbb"]);
169
169
+
itertools::assert_equal(
170
170
+
munch(RuleRef(2), &rules, b"babbb"),
171
171
+
std::iter::empty::<&[u8]>(),
172
172
+
);
173
173
+
itertools::assert_equal(munch(RuleRef(2), &rules, b"bbb"), [b"b"]);
174
174
+
itertools::assert_equal(munch(RuleRef(1), &rules, b"babbb"), [b"b"]);
175
175
+
itertools::assert_equal(munch(RuleRef(0), &rules, b"ababbb"), [b""]);
176
176
+
}
177
177
+
178
178
+
#[test]
179
179
+
fn matching_example_messages() {
180
180
+
itertools::assert_equal(
181
181
+
EXAMPLE_MESSAGES
182
182
+
.iter()
183
183
+
.map(matches(RuleRef(0), &example_rules())),
184
184
+
[true, false, true, false, false],
185
185
+
);
186
186
+
}
187
187
+
}
+2
-2
aoc_2020/src/main.rs
···
18
18
mod day16;
19
19
mod day17;
20
20
mod day18;
21
21
-
// mod day19;
21
21
+
mod day19;
22
22
// mod day20;
23
23
// mod day21;
24
24
// mod day22;
···
49
49
door!(2020-12-16 ~> day16),
50
50
door!(2020-12-17 ~> day17),
51
51
door!(2020-12-18 ~> day18),
52
52
-
// door!(2020-12-19 ~> day19),
52
52
+
door!(2020-12-19 ~> day19),
53
53
// door!(2020-12-20 ~> day20),
54
54
// door!(2020-12-21 ~> day21),
55
55
// door!(2020-12-22 ~> day22),