Advent of Code solutions in Rust

feat: day 20 of AoC 2020

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