aoc/year2016/day13.rs
1//! # A Maze of Twisty Little Cubicles
2//!
3//! We first generate the maze dynamically then explore it using a
4//! [BFS](https://en.wikipedia.org/wiki/Breadth-first_search) solving both part one and part
5//! two simultaneously.
6//!
7//! As we start at (1, 1) and the most steps that we are interested in for part two is 50, while
8//! part one requires more than 50 steps but should easily be reachable without exceeding bounds,
9//! we can bound the maze to 2 + 50 = 52 in each dimension and use a fixed size array. Rather
10//! than filling the array up front, we can lazily populate it as the horizon expands.
11
12use crate::util::parse::*;
13use std::collections::VecDeque;
14
15type Input = (u32, u32);
16
17pub fn parse(input: &str) -> Input {
18 let favorite: usize = input.unsigned();
19
20 // Lazy evaluation: set maze[x][y] to true once a point is visited
21 let mut maze = [[false; 52]; 52];
22 maze[1][1] = true;
23 let mut at = |x: usize, y: usize| -> bool {
24 if maze[x][y] {
25 return false;
26 }
27 maze[x][y] = true;
28 let n = (x * x) + (3 * x) + (2 * x * y) + y + (y * y) + favorite;
29 n.count_ones().is_multiple_of(2)
30 };
31
32 let mut part_two = 0;
33 let mut todo = VecDeque::new();
34
35 todo.push_back((1, 1, 0));
36
37 while let Some((x, y, cost)) = todo.pop_front() {
38 // Target is at least 68 moves from the start. Since we're doing a BFS we're guaranteed
39 // to have checked all locations less than or equal to 50 before reaching the target.
40 if x == 31 && y == 39 {
41 return (cost, part_two);
42 }
43 if cost <= 50 {
44 part_two += 1;
45 }
46
47 if x > 0 && at(x - 1, y) {
48 todo.push_back((x - 1, y, cost + 1));
49 }
50 if y > 0 && at(x, y - 1) {
51 todo.push_back((x, y - 1, cost + 1));
52 }
53 if at(x + 1, y) {
54 todo.push_back((x + 1, y, cost + 1));
55 }
56 if at(x, y + 1) {
57 todo.push_back((x, y + 1, cost + 1));
58 }
59 }
60
61 unreachable!()
62}
63
64pub fn part1(input: &Input) -> u32 {
65 input.0
66}
67
68pub fn part2(input: &Input) -> u32 {
69 input.1
70}