Metadata
-
Date
-
Tagged
-
Part of series
- Advent of Code 2023 Day 1
- Advent of Code 2023 Day 2
- Advent of Code 2023 Day 3
- Advent of Code 2023 Day 4
- Advent of Code 2023 Day 5
- Advent of Code 2023 Day 6
- Advent of Code 2023 Day 7
- Advent of Code 2023 Day 8
- Advent of Code 2023 Day 9
- Advent of Code 2023 Day 10
- Advent of Code 2023 Day 11
- Advent of Code 2023 Day 12
- Advent of Code 2023 Day 13
- Advent of Code 2023 Day 14
- Advent of Code 2023 Day 15
- Advent of Code 2023 Day 16
- Advent of Code 2023 Day 17
- Advent of Code 2023 Day 18
- Advent of Code 2023 Day 19
- Advent of Code 2023 Day 20
- Advent of Code 2023 Day 21
- Advent of Code 2023 Day 22
- Advent of Code 2023 Day 23
- Advent of Code 2023 Day 25
-
Older post
-
Newer post
Advent of Code 2023 Day 10
Day 10: Pipe Maze
https://adventofcode.com/2023/day/10
You go up to a metal island with a hang glider.
There, you see an animal run into a maze of pipes.
You scan the maze of pipes, that scan is today’s input.
An example input looks like this:
The pipes are arranged in a two-dimensional grid of tiles:
- | is a vertical pipe connecting north and south.
-
- is a horizontal pipe connecting east and west.
- L is a 90-degree bend connecting north and east.
- J is a 90-degree bend connecting north and west.
- 7 is a 90-degree bend connecting south and west.
- F is a 90-degree bend connecting south and east.
- . is ground; there is no pipe in this tile.
- S is the starting position of the animal; there is a pipe on this tile, but your sketch doesn’t show what shape the pipe has.
The pipe the animal ran into is a large, continuous loop.
There are other pipes in your scan that are not connected to the main loop.
Parsing
I decided to represent each type of input as a Tile
enum.
A helper to turn characters into variants of that enum:
I keep track of a coordinate in the 2D grid with Coord
:
From the input, I parse the coordinate of the start position, and a 2D list of Tile
s:
Part 1
You want to look at the animal that ran into the pipes. You decide to wait for the animal at the spot that’s the most steps along the loop away from the start, regardless of which direction it took.
The question asks how many steps along the loop it takes to get from the starting position to the point farthest from the starting position?
In other words, you wait at the coordinate that marks the middle of the loop.
Some skeleton/pseudo-code I want to work towards:
Helpers
The function that turns the map into a set of coordinates that belong to the main loop:
That function used a helper that returns the coordinates of the pipes that are directly connected to the current pipe. This is the main logic of this problem, it’s a huge decision-tree.
It figures out which type of Tile
I am currently on.
Based on that, it figures out which types of Tile
are allowed to connect to that.
The coordinates a valid connecting tiles are returned by this function.
example: An L
pipe (NorthWest
in our Tile
enum), can only accept a connecting tile in the north, or the west.
- It looks in the north location, and if a valid pipe is there, it marks that location as a valid neighbour
- It looks in the west location, and if a valid pipe is there, it marks that location as a valid neighbour
Code
The reused code is not listed here, because otherwise this post would become huge.
Part 2
The animal did not emerge. Its nest is probably within an area that’s enclosed by the loop.
This is an example loop with only pipes that are part of the main loop.
There are 4 tiles that are completely inside the loop, marked as I
below.
The tiles marked as O
are not inside.
The animal is small, and can squeeze between 2 pipes, even if there isn’t a .
tile in between!
Here O
tiles are still not inside the loop, and I
tiles are:
The question asks how many tiles are enclosed by the loop.
Option 1: flood fill
Because of that rule where the animal can fit between 2 pipes, a regular floodfill approach will not work. You would end up also counting tiles that are still accessible from the outside.
However, if you expend every tile into a 3x3 grid of tiles, there are guaranteed to be gaps between tiles, where floodfill can pass.
In ASCII:
|
turns into:
-
turns into:
J
turns into:
and so forth…
Start the floodfill at an empty tile. The tiles that are still empty at the end are inside the loop. Remember to account for the fact that 1 empty tile turned into a 3x3 grid of empty tiles when you first expanded the grid.
Option 2: even-odd rule
This is the solution I ended up coding.
- Get the coordinates of the loop
- Clean the map:
- Replace that starting coordinate with a segment of pipe
- Only keep segments of pipe that are part of the main loop
- Apply the even-odd rule to the clean map
in pseudo-code:
Helpers
The helpers that takes the initial map, and returns a cleaned up map:
- It only hold pieces of pipe that are part of the main loop, all other coordinates are ground tiles.
- It replaces that start position with a valid piece of pipe
That function used another helper to figure out which piece of pipe is located at the start coordinates:
With a clean map, I used the even-odd rule to count the amount of tiles that are enclosed by that loop.
Basically if it’s an S shaped joint (FJ, L7) it’s a cross over into the other side, while if it’s a U shaped joint (F7, LJ) you end up on the same side
Code
The reused code is not listed here, because otherwise this post would become huge.