Advent of Code 2023 Day 21
Part 1
I hope it's as easy as I think
- 64 steps
- Even number steps
- Any tile that can be reached in 64 or fewer steps
- And an amount of steps that is even
- Is a valid tile
I plan to write a recursive function that:
- Continually walks away from the starting point
- Catalogues each tile visited
- Adds tiles visited on every other step to a list of valid tiles
- Avoids proceeding if a tile has been visited
This has worked for similar challenges.
I expect it to work here.
But I've been surprised before!
Here I go!
Make grid; Find start; Prepare grid;
Make grid:
input.split('\n').map(line => line.split(''))
Find start:
- Luckily, the start is dead-center of a grid with equal and odd length dimensions
- So, the start is at X and Y of
(length - 1) / 2
let start = [(grid.length - 1) / 2, (grid.length - 1) / 2]
Prepare grid:
grid[start[0]][start[1]] = '.'
That same list of adjacent tiles
Remember this?
const adjacents = [[-1,0],[0,1],[1,0],[0,-1]]
I'll use that in my recursive function to check all four possible next moves at the current tile.
Writing my recursive function
Here it is:
function stepper(current, steps) {
if (steps > 64) return false;
visited.add(current.join(','))
if (steps % 2 == 0) {
valid.add(current.join(','))
}
adjacents.forEach(coord => {
let [row, col] = [current[0] + coord[0], current[1] + coord[1]]
if (row < 0 || row == grid.length || col < 0 || col == grid[0].length) {
return false;
} else {
switch (grid[row][col]) {
case '#':
visited.add(row + ',' + col)
return;
case '.':
return stepper([row,col], steps + 1)
}
}
})
}
It runs. But it doesn't finish when run on my example input.
For the same reason as a few days ago: too many rabbit holes to dig and climb out of.
So, I need to write a non-recursive function.
One that visits all the tiles within a 64 step radius.
And just accumulates tiles to proceed from along it's journey.
Writing my non-recursive function
"This will be straightforward", I thought.
"I did this recently, so it's just recall", I thought.
"Should only take a few minutes to code up", I thought.
Fast-forward an hour.
"Why is my list of cells to visit getting higher than the amount of cells in my grid?", I thought.
"Why are there multiple occurrences of the same cell with the same step count?", I thought.
"Why isn't this working?", I thought.
Fast-forward a half hour.
I figured out my error.
It was simple:
In my case for the next cell being a ., I first check whether that cell has been visited. If it hasn't, I add it to my list of cells to visit.
What I was missing was adding it to the list of visited cells, since I know I'm eventually going to visit it.
Here's my entire program in JavaScript:
let grid = input.split('\n').map(line => line.split(''))
let start = [(grid.length - 1) / 2, (grid.length - 1) / 2]
grid[start[0]][start[1]] = '.'
const adjacents = [[-1,0],[0,1],[1,0],[0,-1]]
let visited = new Set()
visited.add(start.join(','))
let valid = new Set()
let todo = [[start, 0]]
while (todo.length > 0) {
let [xy, steps] = todo.shift()
let [row, col] = xy
if (steps % 2 == 0 && steps <= 64) {
valid.add(xy.join(','))
}
adjacents.forEach(coord => {
let [r, c] = [row + coord[0], col + coord[1]]
if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) {
return false;
} else {
switch (grid[r][c]) {
case '#':
visited.add(r + ',' + c)
break;
case '.':
if (!visited.has([r,c].join(','))) {
visited.add(r + ',' + c)
todo.push([[r,c], steps + 1])
}
break;
}
}
})
}
Running my program with fingers crossed
It generates 42 on the example input.
There are 80 .s in the example input, not including the S.
And since the even step rule is in effect, that feels like the correct answer: half the total .s.
Running it on my puzzle input only took a few seconds to complete.
It generated a number just under 4k.
It is the correct answer!
Woohoo!!!
That wasn't as easy as I'd hoped. Mostly due to my ignorance.
But I got there.
Part 2
To infinity, and...no thanks.
I love the twist:
- Infinite repeating grid
- A step count in the tens of millions
I know for sure I couldn't solve this by simulating single-cell movements.
I have a hunch there's a neat formula that correlates the area of the circle surrounding the starting point with the number of possible valid steps.
Except, in my input I see instances of this pattern:
.#.
#.#
.#.
That middle cell is unreachable, and should never be counted in such a formula.
So, now I'm really at a loss for how I'd solve this.
Sadly, I know I won't be earning another gold star today.
I'll gladly take my one gold star with me to the next challenge.
And hope that I can solve at least two more Part 1's before this year is over.
I have four more chances!
Top comments (0)