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 20 of AoC 2020
jonas.tngl.sh
2 months ago
6df651a8
b047bc24
1/1
rust.yml
success
3m 31s
+757
-2
2 changed files
expand all
collapse all
unified
split
aoc_2020
src
day20.rs
main.rs
+755
aoc_2020/src/day20.rs
···
1
1
+
use std::collections::{HashMap, HashSet};
2
2
+
3
3
+
use anyhow::{Context, anyhow, bail};
4
4
+
use aoc_companion::prelude::*;
5
5
+
use itertools::Itertools;
6
6
+
use ndarray::s;
7
7
+
8
8
+
pub(crate) struct Door {
9
9
+
tiles: HashMap<TileId, Tile>,
10
10
+
}
11
11
+
12
12
+
impl<'input> Solution<'input> for Door {
13
13
+
fn parse(input: &'input str) -> Result<Self> {
14
14
+
input
15
15
+
.split("\n\n")
16
16
+
.map(|block| {
17
17
+
let Some((preamble, tile)) = block.split_once(':') else {
18
18
+
bail!("missing colon after tile preample");
19
19
+
};
20
20
+
let tile_id = preamble
21
21
+
.strip_prefix("Tile ")
22
22
+
.ok_or_else(|| anyhow!("missing tile introducer"))?
23
23
+
.parse()
24
24
+
.context("failed to parse tile ID")
25
25
+
.map(TileId)?;
26
26
+
let tile = aoc_utils::geometry::try_parse_map(tile.trim(), |b| match b {
27
27
+
b'.' => Ok(0),
28
28
+
b'#' => Ok(1),
29
29
+
_ => Err(InvalidTileChar { byte: b }),
30
30
+
})
31
31
+
.context("")?;
32
32
+
Ok((tile_id, tile))
33
33
+
})
34
34
+
.try_collect()
35
35
+
.map(|tiles| Self { tiles })
36
36
+
}
37
37
+
38
38
+
fn part1(&self) -> u64 {
39
39
+
corner_tiles(
40
40
+
tiles_by_edge_signature(self.tiles.iter().map(|(&id, tile)| (id, tile.view())))
41
41
+
.values(),
42
42
+
)
43
43
+
.map(|TileId(id)| id)
44
44
+
.product()
45
45
+
}
46
46
+
47
47
+
fn part2(&self) -> Result<usize> {
48
48
+
Ok(
49
49
+
purge_monsters(puzzle(self.tiles.iter().map(|(&id, tile)| (id, tile.view()))).view())?
50
50
+
.into_iter()
51
51
+
.filter(|&x| x == 1)
52
52
+
.count(),
53
53
+
)
54
54
+
}
55
55
+
}
56
56
+
57
57
+
#[derive(Debug, thiserror::Error)]
58
58
+
#[error("invalid tile character {:?} ({byte:x})", String::from_utf8_lossy(&[self.byte]))]
59
59
+
struct InvalidTileChar {
60
60
+
byte: u8,
61
61
+
}
62
62
+
63
63
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
64
64
+
struct TileId(u64);
65
65
+
66
66
+
type Tile = ndarray::Array2<u8>;
67
67
+
type TileView<'a> = ndarray::ArrayView2<'a, u8>;
68
68
+
69
69
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
70
70
+
struct EdgeSignature(u16);
71
71
+
72
72
+
impl EdgeSignature {
73
73
+
fn new<'a>(edge: impl IntoIterator<Item = &'a u8>) -> Self {
74
74
+
let sig = edge.into_iter().fold(0, |acc, e| (acc << 1) | *e as u16);
75
75
+
Self(sig.min(sig.reverse_bits() >> 6))
76
76
+
}
77
77
+
}
78
78
+
79
79
+
fn edge_signatures(tile: TileView) -> [EdgeSignature; 4] {
80
80
+
[
81
81
+
EdgeSignature::new(tile.slice(s!(.., 0))),
82
82
+
EdgeSignature::new(tile.slice(s!(0, ..))),
83
83
+
EdgeSignature::new(tile.slice(s!(.., -1))),
84
84
+
EdgeSignature::new(tile.slice(s!(-1, ..))),
85
85
+
]
86
86
+
}
87
87
+
88
88
+
fn tiles_by_edge_signature<'a>(
89
89
+
tiles: impl IntoIterator<Item = (TileId, TileView<'a>)>,
90
90
+
) -> HashMap<EdgeSignature, Vec<TileId>> {
91
91
+
tiles
92
92
+
.into_iter()
93
93
+
.flat_map(|(tile_id, tile)| edge_signatures(tile).map(|e| (e, tile_id)))
94
94
+
.into_group_map()
95
95
+
}
96
96
+
97
97
+
fn corner_tiles<'a>(
98
98
+
tiles_with_same_edges: impl IntoIterator<Item = &'a Vec<TileId>>,
99
99
+
) -> impl Iterator<Item = TileId> {
100
100
+
tiles_with_same_edges
101
101
+
.into_iter()
102
102
+
.filter_map(|v| match v[..] {
103
103
+
[tile_id] => Some(tile_id),
104
104
+
_ => None,
105
105
+
})
106
106
+
.duplicates()
107
107
+
}
108
108
+
109
109
+
fn border_tiles<'a>(
110
110
+
tiles_with_same_edges: impl IntoIterator<Item = &'a Vec<TileId>>,
111
111
+
) -> HashSet<TileId> {
112
112
+
tiles_with_same_edges
113
113
+
.into_iter()
114
114
+
.filter_map(|v| match v[..] {
115
115
+
[tile_id] => Some(tile_id),
116
116
+
_ => None,
117
117
+
})
118
118
+
.collect()
119
119
+
}
120
120
+
121
121
+
fn shares_edge_with(edge: EdgeSignature, tile: TileView) -> bool {
122
122
+
edge_signatures(tile).into_iter().any(|e| e == edge)
123
123
+
}
124
124
+
125
125
+
fn puzzle<'a>(tiles: impl IntoIterator<Item = (TileId, TileView<'a>)>) -> ndarray::Array2<u8> {
126
126
+
let tiles = HashMap::<_, _>::from_iter(tiles);
127
127
+
let tiles_by_edges = tiles_by_edge_signature(tiles.iter().map(|(&k, &v)| (k, v)));
128
128
+
129
129
+
let dim = tiles.len().isqrt();
130
130
+
let mut puzzled_ids = ndarray::Array2::from_elem((dim, dim), None);
131
131
+
132
132
+
// Identify corner and border tiles based on edge-sharing statistics
133
133
+
let top_left_corner = corner_tiles(tiles_by_edges.values()).next().unwrap();
134
134
+
let mut border_tiles = border_tiles(tiles_by_edges.values());
135
135
+
136
136
+
// Trace out the border tiles in order
137
137
+
border_tiles.remove(&top_left_corner);
138
138
+
let mut border_ids = std::iter::successors(Some(top_left_corner), |prev| {
139
139
+
let prev_edges = edge_signatures(tiles[prev]);
140
140
+
border_tiles
141
141
+
.extract_if(|b| {
142
142
+
prev_edges
143
143
+
.iter()
144
144
+
.any(|&edge| shares_edge_with(edge, tiles[b]))
145
145
+
})
146
146
+
.next()
147
147
+
});
148
148
+
149
149
+
// Spread the border tile IDs on the square border
150
150
+
for slice in [s![0, ..-1], s![..-1, -1], s![-1,1..;-1], s![1..;-1,0]] {
151
151
+
for (dest, source) in puzzled_ids.slice_mut(slice).iter_mut().zip(&mut border_ids) {
152
152
+
*dest = Some(source);
153
153
+
}
154
154
+
}
155
155
+
156
156
+
// Fill in the inner tiles based two neighboring known tiles
157
157
+
for (i, j) in (0..dim - 2).cartesian_product(0..dim - 2) {
158
158
+
let upper_edges = edge_signatures(tiles[&puzzled_ids[(i, j + 1)].unwrap()]);
159
159
+
let left_edges = edge_signatures(tiles[&puzzled_ids[(i + 1, j)].unwrap()]);
160
160
+
puzzled_ids[(i + 1, j + 1)] = Some(
161
161
+
*tiles
162
162
+
.iter()
163
163
+
.filter(|(id, _)| **id != puzzled_ids[(i, j)].unwrap())
164
164
+
.find(|(_, tile)| {
165
165
+
upper_edges
166
166
+
.iter()
167
167
+
.any(|&edge| shares_edge_with(edge, **tile))
168
168
+
&& left_edges
169
169
+
.iter()
170
170
+
.any(|&edge| shares_edge_with(edge, **tile))
171
171
+
})
172
172
+
.unwrap()
173
173
+
.0,
174
174
+
);
175
175
+
}
176
176
+
177
177
+
// Place matching rotoreflection of tile in the final grid
178
178
+
let cell_dim = tiles.values().next().unwrap().dim().0 - 2;
179
179
+
let grid_dim = cell_dim * dim;
180
180
+
let mut grid = ndarray::Array2::from_elem((grid_dim, grid_dim), 0);
181
181
+
182
182
+
dbg!(puzzled_ids.clone().map(|t| t.unwrap().0));
183
183
+
184
184
+
for (((i, j), id), mut dest) in puzzled_ids
185
185
+
.indexed_iter()
186
186
+
.zip_eq(grid.exact_chunks_mut((cell_dim, cell_dim)))
187
187
+
{
188
188
+
let id = id.unwrap();
189
189
+
let fitting_rotoreflection = rotoreflections(tiles[&id])
190
190
+
.iter()
191
191
+
.copied()
192
192
+
.filter(|&rr| {
193
193
+
if i > 0 {
194
194
+
shares_edge_with(
195
195
+
edge_signatures(rr)[1],
196
196
+
tiles[&puzzled_ids[(i - 1, j)].unwrap()],
197
197
+
)
198
198
+
} else {
199
199
+
shares_edge_with(
200
200
+
edge_signatures(rr)[3],
201
201
+
tiles[&puzzled_ids[(i + 1, j)].unwrap()],
202
202
+
)
203
203
+
}
204
204
+
})
205
205
+
.find(|&rr| {
206
206
+
if j > 0 {
207
207
+
shares_edge_with(
208
208
+
edge_signatures(rr)[0],
209
209
+
tiles[&puzzled_ids[(i, j - 1)].unwrap()],
210
210
+
)
211
211
+
} else {
212
212
+
shares_edge_with(
213
213
+
edge_signatures(rr)[2],
214
214
+
tiles[&puzzled_ids[(i, j + 1)].unwrap()],
215
215
+
)
216
216
+
}
217
217
+
})
218
218
+
.with_context(|| format!("failed to find fitting rotoreflection for {:?}", (i, j)))
219
219
+
.unwrap();
220
220
+
221
221
+
dest.assign(
222
222
+
&fitting_rotoreflection
223
223
+
.slice(s![1.., 1..])
224
224
+
.slice(s![..-1, ..-1]),
225
225
+
);
226
226
+
}
227
227
+
228
228
+
grid
229
229
+
}
230
230
+
231
231
+
fn rotoreflections(tile: TileView) -> [TileView; 8] {
232
232
+
let mut swapped = tile;
233
233
+
swapped.swap_axes(0, 1);
234
234
+
[
235
235
+
tile.slice_move(s![.., ..]),
236
236
+
tile.slice_move(s![..;-1,..]),
237
237
+
tile.slice_move(s![..,..;-1]),
238
238
+
tile.slice_move(s![..;-1,..;-1]),
239
239
+
swapped.slice_move(s![.., ..]),
240
240
+
swapped.slice_move(s![..;-1,..]),
241
241
+
swapped.slice_move(s![..,..;-1]),
242
242
+
swapped.slice_move(s![..;-1,..;-1]),
243
243
+
]
244
244
+
}
245
245
+
246
246
+
fn purge_monsters(puzzle: TileView) -> Result<Tile> {
247
247
+
const MONSTER_MASK: TileView = ndarray::aview2(&[
248
248
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
249
249
+
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1],
250
250
+
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0],
251
251
+
]);
252
252
+
253
253
+
let Some(puzzle) = rotoreflections(puzzle).iter().copied().find(|puzzle| {
254
254
+
puzzle
255
255
+
.windows(MONSTER_MASK.dim())
256
256
+
.into_iter()
257
257
+
.any(|window| &window * &MONSTER_MASK == MONSTER_MASK)
258
258
+
}) else {
259
259
+
bail!("did not find any sea monsters");
260
260
+
};
261
261
+
262
262
+
let mut monsters = Tile::from_elem(puzzle.dim(), 0);
263
263
+
for (puzzle_win, monsters_win) in puzzle
264
264
+
.windows(MONSTER_MASK.dim())
265
265
+
.into_iter()
266
266
+
.zip_eq(monsters.cell_view().windows(MONSTER_MASK.dim()))
267
267
+
{
268
268
+
if &puzzle_win * &MONSTER_MASK == MONSTER_MASK {
269
269
+
ndarray::Zip::from(monsters_win)
270
270
+
.and(MONSTER_MASK)
271
271
+
.for_each(|dest, mask| dest.update(|d| d.max(*mask)));
272
272
+
}
273
273
+
}
274
274
+
275
275
+
Ok(monsters ^ puzzle)
276
276
+
}
277
277
+
278
278
+
#[cfg(test)]
279
279
+
mod tests {
280
280
+
use std::convert::Infallible;
281
281
+
282
282
+
use itertools::Itertools;
283
283
+
284
284
+
use super::*;
285
285
+
286
286
+
#[test]
287
287
+
fn parse_example_input() {
288
288
+
let Door { tiles } = Door::parse(EXAMPLE_INPUT).unwrap();
289
289
+
let expected_tiles = HashMap::from(EXAMPLE_TILES);
290
290
+
for tile_id in tiles.keys().chain(expected_tiles.keys()).unique() {
291
291
+
assert_eq!(
292
292
+
tiles.get(tile_id).map(|a| a.view()).as_ref(),
293
293
+
expected_tiles.get(tile_id),
294
294
+
"mismatch for {tile_id:?}"
295
295
+
)
296
296
+
}
297
297
+
}
298
298
+
299
299
+
#[test]
300
300
+
fn edge_signatures_of_example_tile_2311() {
301
301
+
assert_eq!(
302
302
+
HashSet::from(edge_signatures(EXAMPLE_TILES[0].1)),
303
303
+
HashSet::from(TILE_2311_EDGE_SIGNATURES)
304
304
+
);
305
305
+
}
306
306
+
307
307
+
#[test]
308
308
+
fn rotoreflections_have_the_same_edge_signatures() {
309
309
+
for rotoreflection in rotoreflections(EXAMPLE_TILES[0].1) {
310
310
+
assert_eq!(
311
311
+
HashSet::from(edge_signatures(rotoreflection)),
312
312
+
HashSet::from(TILE_2311_EDGE_SIGNATURES)
313
313
+
);
314
314
+
}
315
315
+
}
316
316
+
317
317
+
#[test]
318
318
+
fn example_tiles_by_edge_signature() {
319
319
+
assert_eq!(
320
320
+
tiles_by_edge_signature(EXAMPLE_TILES),
321
321
+
HashMap::from([
322
322
+
// corner tiles
323
323
+
(EdgeSignature(391), vec![TileId(1171)]),
324
324
+
(EdgeSignature(24), vec![TileId(1171)]),
325
325
+
(EdgeSignature(587), vec![TileId(1951)]),
326
326
+
(EdgeSignature(177), vec![TileId(1951)]),
327
327
+
(EdgeSignature(78), vec![TileId(2971)]),
328
328
+
(EdgeSignature(161), vec![TileId(2971)]),
329
329
+
(EdgeSignature(501), vec![TileId(3079)]),
330
330
+
(EdgeSignature(66), vec![TileId(3079)]),
331
331
+
// border tiles
332
332
+
(EdgeSignature(43), vec![TileId(1489)]),
333
333
+
(EdgeSignature(481), vec![TileId(2473)]),
334
334
+
(EdgeSignature(231), vec![TileId(2311)]),
335
335
+
(EdgeSignature(271), vec![TileId(2729)]),
336
336
+
// inner tiles
337
337
+
(EdgeSignature(234), vec![TileId(1427), TileId(2473)]),
338
338
+
(EdgeSignature(85), vec![TileId(2971), TileId(2729)]),
339
339
+
(EdgeSignature(399), vec![TileId(1171), TileId(2473)]),
340
340
+
(EdgeSignature(9), vec![TileId(1427), TileId(2729)]),
341
341
+
(EdgeSignature(318), vec![TileId(2311), TileId(1951)]),
342
342
+
(EdgeSignature(89), vec![TileId(2311), TileId(3079)]),
343
343
+
(EdgeSignature(210), vec![TileId(2311), TileId(1427)]),
344
344
+
(EdgeSignature(18), vec![TileId(1171), TileId(1489)]),
345
345
+
(EdgeSignature(565), vec![TileId(1489), TileId(2971)]),
346
346
+
(EdgeSignature(116), vec![TileId(2473), TileId(3079)]),
347
347
+
(EdgeSignature(183), vec![TileId(1427), TileId(1489)]),
348
348
+
(EdgeSignature(397), vec![TileId(1951), TileId(2729)]),
349
349
+
])
350
350
+
);
351
351
+
}
352
352
+
353
353
+
#[test]
354
354
+
fn identify_example_corner_tiles() {
355
355
+
assert_eq!(
356
356
+
HashSet::from_iter(corner_tiles(
357
357
+
tiles_by_edge_signature(EXAMPLE_TILES).values()
358
358
+
)),
359
359
+
HashSet::from([TileId(1171), TileId(1951), TileId(2971), TileId(3079)])
360
360
+
);
361
361
+
}
362
362
+
363
363
+
#[test]
364
364
+
fn identify_example_border_tiles() {
365
365
+
assert_eq!(
366
366
+
border_tiles(tiles_by_edge_signature(EXAMPLE_TILES).values()),
367
367
+
HashSet::from([
368
368
+
TileId(1951),
369
369
+
TileId(2311),
370
370
+
TileId(3079),
371
371
+
TileId(2473),
372
372
+
TileId(1171),
373
373
+
TileId(1489),
374
374
+
TileId(2971),
375
375
+
TileId(2729),
376
376
+
])
377
377
+
);
378
378
+
}
379
379
+
380
380
+
#[test]
381
381
+
fn solve_example_puzzle() {
382
382
+
assert!(
383
383
+
rotoreflections(puzzle(EXAMPLE_TILES).view())
384
384
+
.iter()
385
385
+
.contains(&EXAMPLE_PUZZLE)
386
386
+
);
387
387
+
}
388
388
+
389
389
+
#[test]
390
390
+
fn purge_monsters_from_example() {
391
391
+
let expected: Tile =
392
392
+
aoc_utils::geometry::try_parse_map::<_, Infallible>(PURGED_EXAMPLE, |b| {
393
393
+
Ok((b == b'#') as u8)
394
394
+
})
395
395
+
.unwrap();
396
396
+
for puzzle_roto in rotoreflections(EXAMPLE_PUZZLE) {
397
397
+
assert_eq!(purge_monsters(puzzle_roto).unwrap(), expected);
398
398
+
}
399
399
+
}
400
400
+
401
401
+
const TILE_2311_EDGE_SIGNATURES: [EdgeSignature; 4] = [
402
402
+
EdgeSignature(0b0100111110),
403
403
+
EdgeSignature(0b0001011001),
404
404
+
EdgeSignature(0b0011010010),
405
405
+
EdgeSignature(0b0011100111),
406
406
+
];
407
407
+
408
408
+
const EXAMPLE_INPUT: &str = "\
409
409
+
Tile 2311:
410
410
+
..##.#..#.
411
411
+
##..#.....
412
412
+
#...##..#.
413
413
+
####.#...#
414
414
+
##.##.###.
415
415
+
##...#.###
416
416
+
.#.#.#..##
417
417
+
..#....#..
418
418
+
###...#.#.
419
419
+
..###..###
420
420
+
421
421
+
Tile 1951:
422
422
+
#.##...##.
423
423
+
#.####...#
424
424
+
.....#..##
425
425
+
#...######
426
426
+
.##.#....#
427
427
+
.###.#####
428
428
+
###.##.##.
429
429
+
.###....#.
430
430
+
..#.#..#.#
431
431
+
#...##.#..
432
432
+
433
433
+
Tile 1171:
434
434
+
####...##.
435
435
+
#..##.#..#
436
436
+
##.#..#.#.
437
437
+
.###.####.
438
438
+
..###.####
439
439
+
.##....##.
440
440
+
.#...####.
441
441
+
#.##.####.
442
442
+
####..#...
443
443
+
.....##...
444
444
+
445
445
+
Tile 1427:
446
446
+
###.##.#..
447
447
+
.#..#.##..
448
448
+
.#.##.#..#
449
449
+
#.#.#.##.#
450
450
+
....#...##
451
451
+
...##..##.
452
452
+
...#.#####
453
453
+
.#.####.#.
454
454
+
..#..###.#
455
455
+
..##.#..#.
456
456
+
457
457
+
Tile 1489:
458
458
+
##.#.#....
459
459
+
..##...#..
460
460
+
.##..##...
461
461
+
..#...#...
462
462
+
#####...#.
463
463
+
#..#.#.#.#
464
464
+
...#.#.#..
465
465
+
##.#...##.
466
466
+
..##.##.##
467
467
+
###.##.#..
468
468
+
469
469
+
Tile 2473:
470
470
+
#....####.
471
471
+
#..#.##...
472
472
+
#.##..#...
473
473
+
######.#.#
474
474
+
.#...#.#.#
475
475
+
.#########
476
476
+
.###.#..#.
477
477
+
########.#
478
478
+
##...##.#.
479
479
+
..###.#.#.
480
480
+
481
481
+
Tile 2971:
482
482
+
..#.#....#
483
483
+
#...###...
484
484
+
#.#.###...
485
485
+
##.##..#..
486
486
+
.#####..##
487
487
+
.#..####.#
488
488
+
#..#.#..#.
489
489
+
..####.###
490
490
+
..#.#.###.
491
491
+
...#.#.#.#
492
492
+
493
493
+
Tile 2729:
494
494
+
...#.#.#.#
495
495
+
####.#....
496
496
+
..#.#.....
497
497
+
....#..#.#
498
498
+
.##..##.#.
499
499
+
.#.####...
500
500
+
####.#.#..
501
501
+
##.####...
502
502
+
##..#.##..
503
503
+
#.##...##.
504
504
+
505
505
+
Tile 3079:
506
506
+
#.#.#####.
507
507
+
.#..######
508
508
+
..#.......
509
509
+
######....
510
510
+
####.#..#.
511
511
+
.#...#.##.
512
512
+
#.#####.##
513
513
+
..#.###...
514
514
+
..#.......
515
515
+
..#.###...";
516
516
+
517
517
+
const EXAMPLE_TILES: [(TileId, TileView); 9] = [
518
518
+
(
519
519
+
TileId(2311),
520
520
+
ndarray::aview2(&[
521
521
+
[0, 0, 1, 1, 0, 1, 0, 0, 1, 0],
522
522
+
[1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
523
523
+
[1, 0, 0, 0, 1, 1, 0, 0, 1, 0],
524
524
+
[1, 1, 1, 1, 0, 1, 0, 0, 0, 1],
525
525
+
[1, 1, 0, 1, 1, 0, 1, 1, 1, 0],
526
526
+
[1, 1, 0, 0, 0, 1, 0, 1, 1, 1],
527
527
+
[0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
528
528
+
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0],
529
529
+
[1, 1, 1, 0, 0, 0, 1, 0, 1, 0],
530
530
+
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1],
531
531
+
]),
532
532
+
),
533
533
+
(
534
534
+
TileId(1951),
535
535
+
ndarray::aview2(&[
536
536
+
[1, 0, 1, 1, 0, 0, 0, 1, 1, 0],
537
537
+
[1, 0, 1, 1, 1, 1, 0, 0, 0, 1],
538
538
+
[0, 0, 0, 0, 0, 1, 0, 0, 1, 1],
539
539
+
[1, 0, 0, 0, 1, 1, 1, 1, 1, 1],
540
540
+
[0, 1, 1, 0, 1, 0, 0, 0, 0, 1],
541
541
+
[0, 1, 1, 1, 0, 1, 1, 1, 1, 1],
542
542
+
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0],
543
543
+
[0, 1, 1, 1, 0, 0, 0, 0, 1, 0],
544
544
+
[0, 0, 1, 0, 1, 0, 0, 1, 0, 1],
545
545
+
[1, 0, 0, 0, 1, 1, 0, 1, 0, 0],
546
546
+
]),
547
547
+
),
548
548
+
(
549
549
+
TileId(1171),
550
550
+
ndarray::aview2(&[
551
551
+
[1, 1, 1, 1, 0, 0, 0, 1, 1, 0],
552
552
+
[1, 0, 0, 1, 1, 0, 1, 0, 0, 1],
553
553
+
[1, 1, 0, 1, 0, 0, 1, 0, 1, 0],
554
554
+
[0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
555
555
+
[0, 0, 1, 1, 1, 0, 1, 1, 1, 1],
556
556
+
[0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
557
557
+
[0, 1, 0, 0, 0, 1, 1, 1, 1, 0],
558
558
+
[1, 0, 1, 1, 0, 1, 1, 1, 1, 0],
559
559
+
[1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
560
560
+
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
561
561
+
]),
562
562
+
),
563
563
+
(
564
564
+
TileId(1427),
565
565
+
ndarray::aview2(&[
566
566
+
[1, 1, 1, 0, 1, 1, 0, 1, 0, 0],
567
567
+
[0, 1, 0, 0, 1, 0, 1, 1, 0, 0],
568
568
+
[0, 1, 0, 1, 1, 0, 1, 0, 0, 1],
569
569
+
[1, 0, 1, 0, 1, 0, 1, 1, 0, 1],
570
570
+
[0, 0, 0, 0, 1, 0, 0, 0, 1, 1],
571
571
+
[0, 0, 0, 1, 1, 0, 0, 1, 1, 0],
572
572
+
[0, 0, 0, 1, 0, 1, 1, 1, 1, 1],
573
573
+
[0, 1, 0, 1, 1, 1, 1, 0, 1, 0],
574
574
+
[0, 0, 1, 0, 0, 1, 1, 1, 0, 1],
575
575
+
[0, 0, 1, 1, 0, 1, 0, 0, 1, 0],
576
576
+
]),
577
577
+
),
578
578
+
(
579
579
+
TileId(1489),
580
580
+
ndarray::aview2(&[
581
581
+
[1, 1, 0, 1, 0, 1, 0, 0, 0, 0],
582
582
+
[0, 0, 1, 1, 0, 0, 0, 1, 0, 0],
583
583
+
[0, 1, 1, 0, 0, 1, 1, 0, 0, 0],
584
584
+
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
585
585
+
[1, 1, 1, 1, 1, 0, 0, 0, 1, 0],
586
586
+
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
587
587
+
[0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
588
588
+
[1, 1, 0, 1, 0, 0, 0, 1, 1, 0],
589
589
+
[0, 0, 1, 1, 0, 1, 1, 0, 1, 1],
590
590
+
[1, 1, 1, 0, 1, 1, 0, 1, 0, 0],
591
591
+
]),
592
592
+
),
593
593
+
(
594
594
+
TileId(2473),
595
595
+
ndarray::aview2(&[
596
596
+
[1, 0, 0, 0, 0, 1, 1, 1, 1, 0],
597
597
+
[1, 0, 0, 1, 0, 1, 1, 0, 0, 0],
598
598
+
[1, 0, 1, 1, 0, 0, 1, 0, 0, 0],
599
599
+
[1, 1, 1, 1, 1, 1, 0, 1, 0, 1],
600
600
+
[0, 1, 0, 0, 0, 1, 0, 1, 0, 1],
601
601
+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
602
602
+
[0, 1, 1, 1, 0, 1, 0, 0, 1, 0],
603
603
+
[1, 1, 1, 1, 1, 1, 1, 1, 0, 1],
604
604
+
[1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
605
605
+
[0, 0, 1, 1, 1, 0, 1, 0, 1, 0],
606
606
+
]),
607
607
+
),
608
608
+
(
609
609
+
TileId(2971),
610
610
+
ndarray::aview2(&[
611
611
+
[0, 0, 1, 0, 1, 0, 0, 0, 0, 1],
612
612
+
[1, 0, 0, 0, 1, 1, 1, 0, 0, 0],
613
613
+
[1, 0, 1, 0, 1, 1, 1, 0, 0, 0],
614
614
+
[1, 1, 0, 1, 1, 0, 0, 1, 0, 0],
615
615
+
[0, 1, 1, 1, 1, 1, 0, 0, 1, 1],
616
616
+
[0, 1, 0, 0, 1, 1, 1, 1, 0, 1],
617
617
+
[1, 0, 0, 1, 0, 1, 0, 0, 1, 0],
618
618
+
[0, 0, 1, 1, 1, 1, 0, 1, 1, 1],
619
619
+
[0, 0, 1, 0, 1, 0, 1, 1, 1, 0],
620
620
+
[0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
621
621
+
]),
622
622
+
),
623
623
+
(
624
624
+
TileId(2729),
625
625
+
ndarray::aview2(&[
626
626
+
[0, 0, 0, 1, 0, 1, 0, 1, 0, 1],
627
627
+
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0],
628
628
+
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0],
629
629
+
[0, 0, 0, 0, 1, 0, 0, 1, 0, 1],
630
630
+
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0],
631
631
+
[0, 1, 0, 1, 1, 1, 1, 0, 0, 0],
632
632
+
[1, 1, 1, 1, 0, 1, 0, 1, 0, 0],
633
633
+
[1, 1, 0, 1, 1, 1, 1, 0, 0, 0],
634
634
+
[1, 1, 0, 0, 1, 0, 1, 1, 0, 0],
635
635
+
[1, 0, 1, 1, 0, 0, 0, 1, 1, 0],
636
636
+
]),
637
637
+
),
638
638
+
(
639
639
+
TileId(3079),
640
640
+
ndarray::aview2(&[
641
641
+
[1, 0, 1, 0, 1, 1, 1, 1, 1, 0],
642
642
+
[0, 1, 0, 0, 1, 1, 1, 1, 1, 1],
643
643
+
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
644
644
+
[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
645
645
+
[1, 1, 1, 1, 0, 1, 0, 0, 1, 0],
646
646
+
[0, 1, 0, 0, 0, 1, 0, 1, 1, 0],
647
647
+
[1, 0, 1, 1, 1, 1, 1, 0, 1, 1],
648
648
+
[0, 0, 1, 0, 1, 1, 1, 0, 0, 0],
649
649
+
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
650
650
+
[0, 0, 1, 0, 1, 1, 1, 0, 0, 0],
651
651
+
]),
652
652
+
),
653
653
+
];
654
654
+
655
655
+
const EXAMPLE_PUZZLE: ndarray::ArrayView2<u8> = ndarray::aview2(&[
656
656
+
[
657
657
+
0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1,
658
658
+
],
659
659
+
[
660
660
+
1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
661
661
+
],
662
662
+
[
663
663
+
1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0,
664
664
+
],
665
665
+
[
666
666
+
1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1,
667
667
+
],
668
668
+
[
669
669
+
1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1,
670
670
+
],
671
671
+
[
672
672
+
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1,
673
673
+
],
674
674
+
[
675
675
+
0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
676
676
+
],
677
677
+
[
678
678
+
0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
679
679
+
],
680
680
+
[
681
681
+
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0,
682
682
+
],
683
683
+
[
684
684
+
1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0,
685
685
+
],
686
686
+
[
687
687
+
1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1,
688
688
+
],
689
689
+
[
690
690
+
1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
691
691
+
],
692
692
+
[
693
693
+
1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
694
694
+
],
695
695
+
[
696
696
+
0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1,
697
697
+
],
698
698
+
[
699
699
+
0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0,
700
700
+
],
701
701
+
[
702
702
+
1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
703
703
+
],
704
704
+
[
705
705
+
0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0,
706
706
+
],
707
707
+
[
708
708
+
0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1,
709
709
+
],
710
710
+
[
711
711
+
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1,
712
712
+
],
713
713
+
[
714
714
+
1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0,
715
715
+
],
716
716
+
[
717
717
+
1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
718
718
+
],
719
719
+
[
720
720
+
1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1,
721
721
+
],
722
722
+
[
723
723
+
0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
724
724
+
],
725
725
+
[
726
726
+
0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1,
727
727
+
],
728
728
+
]);
729
729
+
730
730
+
const PURGED_EXAMPLE: &str = "\
731
731
+
.####...#####..#...###..
732
732
+
#####..#..#.#.####..#.#.
733
733
+
.#.#...#.###...#.##.O#..
734
734
+
#.O.##.OO#.#.OO.##.OOO##
735
735
+
..#O.#O#.O##O..O.#O##.##
736
736
+
...#.#..##.##...#..#..##
737
737
+
#.##.#..#.#..#..##.#.#..
738
738
+
.###.##.....#...###.#...
739
739
+
#.####.#.#....##.#..#.#.
740
740
+
##...#..#....#..#...####
741
741
+
..#.##...###..#.#####..#
742
742
+
....#.##.#.#####....#...
743
743
+
..##.##.###.....#.##..#.
744
744
+
#...#...###..####....##.
745
745
+
.#.##...#.##.#.#.###...#
746
746
+
#.###.#..####...##..#...
747
747
+
#.###...#.##...#.##O###.
748
748
+
.O##.#OO.###OO##..OOO##.
749
749
+
..O#.O..O..O.#O##O##.###
750
750
+
#.#..##.########..#..##.
751
751
+
#.#####..#.#...##..#....
752
752
+
#....##..#.#########..##
753
753
+
#...#.....#..##...###.##
754
754
+
#..###....##.#...##.##.#";
755
755
+
}
+2
-2
aoc_2020/src/main.rs
···
19
19
mod day17;
20
20
mod day18;
21
21
mod day19;
22
22
-
// mod day20;
22
22
+
mod day20;
23
23
// mod day21;
24
24
// mod day22;
25
25
// mod day23;
···
50
50
door!(2020-12-17 ~> day17),
51
51
door!(2020-12-18 ~> day18),
52
52
door!(2020-12-19 ~> day19),
53
53
-
// door!(2020-12-20 ~> day20),
53
53
+
door!(2020-12-20 ~> day20),
54
54
// door!(2020-12-21 ~> day21),
55
55
// door!(2020-12-22 ~> day22),
56
56
// door!(2020-12-23 ~> day23),