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: day 18 of AoC 2020
jonas.tngl.sh
3 months ago
e42bdb91
1640e8a7
+362
-3
3 changed files
expand all
collapse all
unified
split
aoc_2020
src
day18.rs
main.rs
aoc_utils
src
iter.rs
+346
aoc_2020/src/day18.rs
···
1
1
+
use anyhow::{anyhow, bail};
2
2
+
use aoc_companion::prelude::*;
3
3
+
use aoc_utils::iter::IterUtils as _;
4
4
+
use itertools::Itertools;
5
5
+
6
6
+
pub(crate) struct Door {
7
7
+
expressions: Vec<Vec<Token>>,
8
8
+
}
9
9
+
10
10
+
impl<'input> Solution<'input> for Door {
11
11
+
fn parse(input: &'input str) -> Result<Self> {
12
12
+
input
13
13
+
.lines()
14
14
+
.map(|line| tokenize(line).try_collect())
15
15
+
.try_collect()
16
16
+
.map(|expressions| Door { expressions })
17
17
+
}
18
18
+
19
19
+
fn part1(&self) -> Result<u64> {
20
20
+
self.expressions
21
21
+
.iter()
22
22
+
.map(parse_part1)
23
23
+
.map_ok(|ast| ast.eval())
24
24
+
.try_sum()
25
25
+
}
26
26
+
27
27
+
fn part2(&self) -> Result<u64> {
28
28
+
self.expressions
29
29
+
.iter()
30
30
+
.map(parse_part2)
31
31
+
.map_ok(|ast| ast.eval())
32
32
+
.try_sum()
33
33
+
}
34
34
+
}
35
35
+
36
36
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
37
37
+
enum Token {
38
38
+
LParen,
39
39
+
RParen,
40
40
+
Plus,
41
41
+
Times,
42
42
+
Number(u64),
43
43
+
}
44
44
+
45
45
+
fn tokenize(expr: &str) -> impl Iterator<Item = Result<Token>> {
46
46
+
expr.bytes()
47
47
+
.filter(|b| !b.is_ascii_whitespace())
48
48
+
.map(|b| match b {
49
49
+
b'(' => Ok(Token::LParen),
50
50
+
b')' => Ok(Token::RParen),
51
51
+
b'+' => Ok(Token::Plus),
52
52
+
b'*' => Ok(Token::Times),
53
53
+
b'0'..=b'9' => Ok(Token::Number((b - b'0') as u64)),
54
54
+
0..128 => Err(anyhow!(
55
55
+
"invalid token {:?}",
56
56
+
char::from_u32(b as u32).unwrap()
57
57
+
)),
58
58
+
_ => Err(anyhow!("invalid token: non-ASCII character")),
59
59
+
})
60
60
+
}
61
61
+
62
62
+
#[derive(Clone, Debug, PartialEq, Eq)]
63
63
+
enum Ast {
64
64
+
Number(u64),
65
65
+
Add(Box<Ast>, Box<Ast>),
66
66
+
Mul(Box<Ast>, Box<Ast>),
67
67
+
}
68
68
+
69
69
+
impl Ast {
70
70
+
fn eval(&self) -> u64 {
71
71
+
match self {
72
72
+
Ast::Number(n) => *n,
73
73
+
Ast::Add(lhs, rhs) => lhs.eval() + rhs.eval(),
74
74
+
Ast::Mul(lhs, rhs) => lhs.eval() * rhs.eval(),
75
75
+
}
76
76
+
}
77
77
+
}
78
78
+
79
79
+
fn parse_part1<'a, I: IntoIterator<Item = &'a Token>>(tokens: I) -> Result<Ast> {
80
80
+
fn do_parse<'a, I: Iterator<Item = &'a Token>>(tokens: &mut I) -> Result<Ast> {
81
81
+
let parse_operand = |tokens: &mut I| -> Result<Ast> {
82
82
+
match tokens.next() {
83
83
+
Some(Token::LParen) => do_parse(tokens),
84
84
+
Some(Token::Number(n)) => Ok(Ast::Number(*n)),
85
85
+
Some(Token::Plus) | Some(Token::Times) => {
86
86
+
Err(anyhow!("expected a number or expression, got an operator"))
87
87
+
}
88
88
+
Some(Token::RParen) | None => Err(anyhow!("unexpected EOL or ')'")),
89
89
+
}
90
90
+
};
91
91
+
let mut ast = parse_operand(tokens)?;
92
92
+
loop {
93
93
+
ast = match tokens.next() {
94
94
+
Some(Token::Plus) => Ast::Add(Box::new(ast), Box::new(parse_operand(tokens)?)),
95
95
+
Some(Token::Times) => Ast::Mul(Box::new(ast), Box::new(parse_operand(tokens)?)),
96
96
+
Some(Token::RParen) | None => return Ok(ast),
97
97
+
_ => bail!("unexpected token"),
98
98
+
}
99
99
+
}
100
100
+
}
101
101
+
do_parse(&mut tokens.into_iter())
102
102
+
}
103
103
+
104
104
+
fn parse_part2<'a, I: IntoIterator<Item = &'a Token>>(tokens: I) -> Result<Ast> {
105
105
+
fn do_parse<'a, I: Iterator<Item = &'a Token>>(tokens: &mut I) -> Result<Ast> {
106
106
+
enum Parens {
107
107
+
Yes(Ast),
108
108
+
No(Ast),
109
109
+
}
110
110
+
111
111
+
let parse_operand = |tokens: &mut I| -> Result<Ast> {
112
112
+
match tokens.next() {
113
113
+
Some(Token::LParen) => do_parse(tokens),
114
114
+
Some(Token::Number(n)) => Ok(Ast::Number(*n)),
115
115
+
Some(Token::Plus) | Some(Token::Times) => {
116
116
+
Err(anyhow!("expected a number or expression, got an operator"))
117
117
+
}
118
118
+
Some(Token::RParen) | None => Err(anyhow!("unexpected EOL or ')'")),
119
119
+
}
120
120
+
};
121
121
+
let mut ast = Parens::Yes(parse_operand(tokens)?);
122
122
+
loop {
123
123
+
ast = match (ast, tokens.next()) {
124
124
+
(Parens::No(Ast::Mul(lhs, rhs)), Some(Token::Plus)) => Parens::No(Ast::Mul(
125
125
+
lhs,
126
126
+
Box::new(Ast::Add(rhs, Box::new(parse_operand(tokens)?))),
127
127
+
)),
128
128
+
(Parens::Yes(ast) | Parens::No(ast), Some(Token::Plus)) => {
129
129
+
Parens::No(Ast::Add(Box::new(ast), Box::new(parse_operand(tokens)?)))
130
130
+
}
131
131
+
(Parens::Yes(ast) | Parens::No(ast), Some(Token::Times)) => {
132
132
+
Parens::No(Ast::Mul(Box::new(ast), Box::new(parse_operand(tokens)?)))
133
133
+
}
134
134
+
(Parens::Yes(ast) | Parens::No(ast), Some(Token::RParen) | None) => return Ok(ast),
135
135
+
_ => bail!("unexpected token"),
136
136
+
}
137
137
+
}
138
138
+
}
139
139
+
do_parse(&mut tokens.into_iter())
140
140
+
}
141
141
+
142
142
+
#[cfg(test)]
143
143
+
mod tests {
144
144
+
use super::*;
145
145
+
146
146
+
const EXAMPLE_INPUTS: &[&str] = &[
147
147
+
"1 + 2 * 3 + 4 * 5 + 6",
148
148
+
"1 + (2 * 3) + (4 * (5 + 6))",
149
149
+
"2 * 3 + (4 * 5)",
150
150
+
"5 + (8 * 3 + 9 + 3 * 4 * 3)",
151
151
+
"5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))",
152
152
+
"((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2",
153
153
+
];
154
154
+
155
155
+
macro_rules! ast {
156
156
+
($n:literal) => {
157
157
+
Ast::Number($n)
158
158
+
};
159
159
+
(($lhs:tt + $rhs:tt)) => {
160
160
+
Ast::Add(Box::new(ast!($lhs)), Box::new(ast!($rhs)))
161
161
+
};
162
162
+
(($lhs:tt * $rhs:tt)) => {
163
163
+
Ast::Mul(Box::new(ast!($lhs)), Box::new(ast!($rhs)))
164
164
+
};
165
165
+
}
166
166
+
167
167
+
fn example_asts_part1() -> Vec<Ast> {
168
168
+
vec![
169
169
+
ast!((((((1 + 2) * 3) + 4) * 5) + 6)),
170
170
+
ast!(((1 + (2 * 3)) + (4 * (5 + 6)))),
171
171
+
ast!(((2 * 3) + (4 * 5))),
172
172
+
ast!((5 + (((((8 * 3) + 9) + 3) * 4) * 3))),
173
173
+
ast!(((5 * 9) * (((((7 * 3) * 3) + 9) * 3) + ((8 + 6) * 4)))),
174
174
+
ast!((((((((2 + 4) * 9) * (((6 + 9) * 8) + 6)) + 6) + 2) + 4) * 2)),
175
175
+
]
176
176
+
}
177
177
+
fn example_asts_part2() -> Vec<Ast> {
178
178
+
vec![
179
179
+
ast!((((1 + 2) * (3 + 4)) * (5 + 6))),
180
180
+
ast!(((1 + (2 * 3)) + (4 * (5 + 6)))),
181
181
+
ast!((2 * (3 + (4 * 5)))),
182
182
+
ast!((5 + (((8 * ((3 + 9) + 3)) * 4) * 3))),
183
183
+
ast!(((5 * 9) * (((7 * 3) * (3 + 9)) * (3 + ((8 + 6) * 4))))),
184
184
+
ast!(((((((2 + 4) * 9) * (((6 + 9) * (8 + 6)) + 6)) + 2) + 4) * 2)),
185
185
+
]
186
186
+
}
187
187
+
188
188
+
const EXAMPLE_EXPRESSIONS: &[&[Token]] = &[
189
189
+
&[
190
190
+
Token::Number(1),
191
191
+
Token::Plus,
192
192
+
Token::Number(2),
193
193
+
Token::Times,
194
194
+
Token::Number(3),
195
195
+
Token::Plus,
196
196
+
Token::Number(4),
197
197
+
Token::Times,
198
198
+
Token::Number(5),
199
199
+
Token::Plus,
200
200
+
Token::Number(6),
201
201
+
],
202
202
+
&[
203
203
+
Token::Number(1),
204
204
+
Token::Plus,
205
205
+
Token::LParen,
206
206
+
Token::Number(2),
207
207
+
Token::Times,
208
208
+
Token::Number(3),
209
209
+
Token::RParen,
210
210
+
Token::Plus,
211
211
+
Token::LParen,
212
212
+
Token::Number(4),
213
213
+
Token::Times,
214
214
+
Token::LParen,
215
215
+
Token::Number(5),
216
216
+
Token::Plus,
217
217
+
Token::Number(6),
218
218
+
Token::RParen,
219
219
+
Token::RParen,
220
220
+
],
221
221
+
&[
222
222
+
Token::Number(2),
223
223
+
Token::Times,
224
224
+
Token::Number(3),
225
225
+
Token::Plus,
226
226
+
Token::LParen,
227
227
+
Token::Number(4),
228
228
+
Token::Times,
229
229
+
Token::Number(5),
230
230
+
Token::RParen,
231
231
+
],
232
232
+
&[
233
233
+
Token::Number(5),
234
234
+
Token::Plus,
235
235
+
Token::LParen,
236
236
+
Token::Number(8),
237
237
+
Token::Times,
238
238
+
Token::Number(3),
239
239
+
Token::Plus,
240
240
+
Token::Number(9),
241
241
+
Token::Plus,
242
242
+
Token::Number(3),
243
243
+
Token::Times,
244
244
+
Token::Number(4),
245
245
+
Token::Times,
246
246
+
Token::Number(3),
247
247
+
Token::RParen,
248
248
+
],
249
249
+
&[
250
250
+
Token::Number(5),
251
251
+
Token::Times,
252
252
+
Token::Number(9),
253
253
+
Token::Times,
254
254
+
Token::LParen,
255
255
+
Token::Number(7),
256
256
+
Token::Times,
257
257
+
Token::Number(3),
258
258
+
Token::Times,
259
259
+
Token::Number(3),
260
260
+
Token::Plus,
261
261
+
Token::Number(9),
262
262
+
Token::Times,
263
263
+
Token::Number(3),
264
264
+
Token::Plus,
265
265
+
Token::LParen,
266
266
+
Token::Number(8),
267
267
+
Token::Plus,
268
268
+
Token::Number(6),
269
269
+
Token::Times,
270
270
+
Token::Number(4),
271
271
+
Token::RParen,
272
272
+
Token::RParen,
273
273
+
],
274
274
+
&[
275
275
+
Token::LParen,
276
276
+
Token::LParen,
277
277
+
Token::Number(2),
278
278
+
Token::Plus,
279
279
+
Token::Number(4),
280
280
+
Token::Times,
281
281
+
Token::Number(9),
282
282
+
Token::RParen,
283
283
+
Token::Times,
284
284
+
Token::LParen,
285
285
+
Token::Number(6),
286
286
+
Token::Plus,
287
287
+
Token::Number(9),
288
288
+
Token::Times,
289
289
+
Token::Number(8),
290
290
+
Token::Plus,
291
291
+
Token::Number(6),
292
292
+
Token::RParen,
293
293
+
Token::Plus,
294
294
+
Token::Number(6),
295
295
+
Token::RParen,
296
296
+
Token::Plus,
297
297
+
Token::Number(2),
298
298
+
Token::Plus,
299
299
+
Token::Number(4),
300
300
+
Token::Times,
301
301
+
Token::Number(2),
302
302
+
],
303
303
+
];
304
304
+
305
305
+
#[test]
306
306
+
fn example_expressions_are_tokenized() {
307
307
+
itertools::assert_equal(
308
308
+
EXAMPLE_INPUTS
309
309
+
.iter()
310
310
+
.map(|line| -> Vec<Token> { tokenize(line).try_collect().unwrap() }),
311
311
+
EXAMPLE_EXPRESSIONS.iter().cloned(),
312
312
+
);
313
313
+
}
314
314
+
315
315
+
#[test]
316
316
+
fn example_expressions_are_parsed_for_part1() {
317
317
+
itertools::assert_equal(
318
318
+
EXAMPLE_EXPRESSIONS
319
319
+
.iter()
320
320
+
.map(|tokens| parse_part1(*tokens).unwrap()),
321
321
+
example_asts_part1(),
322
322
+
);
323
323
+
}
324
324
+
325
325
+
#[test]
326
326
+
fn example_expressions_are_parsed_for_part2() {
327
327
+
itertools::assert_equal(
328
328
+
EXAMPLE_EXPRESSIONS
329
329
+
.iter()
330
330
+
.map(|tokens| parse_part2(*tokens).unwrap()),
331
331
+
example_asts_part2(),
332
332
+
);
333
333
+
}
334
334
+
335
335
+
#[test]
336
336
+
fn evaluate_example_expressions() {
337
337
+
itertools::assert_equal(
338
338
+
example_asts_part1().iter().map(Ast::eval),
339
339
+
[71, 51, 26, 437, 12240, 13632],
340
340
+
);
341
341
+
itertools::assert_equal(
342
342
+
example_asts_part2().iter().map(Ast::eval),
343
343
+
[231, 51, 46, 1445, 669060, 23340],
344
344
+
);
345
345
+
}
346
346
+
}
+2
-2
aoc_2020/src/main.rs
···
17
17
mod day15;
18
18
mod day16;
19
19
mod day17;
20
20
-
// mod day18;
20
20
+
mod day18;
21
21
// mod day19;
22
22
// mod day20;
23
23
// mod day21;
···
48
48
door!(2020-12-15 ~> day15),
49
49
door!(2020-12-16 ~> day16),
50
50
door!(2020-12-17 ~> day17),
51
51
-
// door!(2020-12-18 ~> day18),
51
51
+
door!(2020-12-18 ~> day18),
52
52
// door!(2020-12-19 ~> day19),
53
53
// door!(2020-12-20 ~> day20),
54
54
// door!(2020-12-21 ~> day21),
+14
-1
aoc_utils/src/iter.rs
···
1
1
-
use std::{iter::FusedIterator, rc::Rc};
1
1
+
use std::{
2
2
+
iter::{FusedIterator, Sum},
3
3
+
rc::Rc,
4
4
+
};
2
5
3
6
pub trait IterUtils: Iterator {
7
7
+
fn try_sum<T, E>(mut self) -> Result<T, E>
8
8
+
where
9
9
+
Self: Sized + Iterator<Item = Result<T, E>>,
10
10
+
T: Sum,
11
11
+
{
12
12
+
self.try_fold(std::iter::empty::<T>().sum(), |acc, val| {
13
13
+
Ok(AtMostTwo::two(acc, val?).sum())
14
14
+
})
15
15
+
}
16
16
+
4
17
fn try_unzip<A, B, E, FromA, FromB>(mut self) -> Result<(FromA, FromB), E>
5
18
where
6
19
Self: Sized + Iterator<Item = Result<(A, B), E>>,