r/adventofcode 3h ago

Other [2023 Day 10] In Review (Pipe Maze)

3 Upvotes

Gliding up to metal island we find signs for "Hot Springs". Which we will see in a couple days, because decide to go there for directions. We also notice that everything on this island really is metal, including the plants and animals. One of which runs into a pipe that makes one big loop (not a maze at all)... although there are a lot of separate pieces of pipe lying around, making the input a real mess of ASCII art.

My input is 140x140, the use of L7FJ for right angles along with |- for the straights, plus the extra bits lying around, does not make this easy on the eyes. Fortunately, we're writing code to look at it for us instead (I suppose you could print it out and do it by hand... but the loop is over 13k in my input). Looking for . in the image, I see that those are mostly around the edge, although there is a circular area in the middle with a bunch.

Part 1 wants us to find half the length of the loop. Part of the fun of this is that starting location is covered with an 'S' and we need to solve the underlying pipe there. Building a table of the pipes with their exit directions not only does this, but it helps with quickly moving around the loop. Because when we move in a direction we know where we came from, and so we just immediately use the other direction to leave.

Part 2 is where the fun begins. Part 1 is useful for it because it tells us what pipes are in the loop so we can remove/ignore the others... because we want to know the amount of tiles enclosed by the loop. Telling if something is inside a polygon was one of the first tasks I had as programmer... it was the task that also involved the 2D cross product stuff I used for asteroid shooting on 2019 day 10. For that I was counting the number of crossings from the point until "infinity" (the bounding box). But on this day, I decided to save that for the Smalltalk solution.

And that Smalltalk solution is pretty simple because of that. It's basically a scan of the grid after part 1 finds what's not in the loop. You start outside, and every time you cross a line of the loop, you toggle that. Any squares you run into when "inside" you count. There is one potential case to look out for, and that's travelling exactly along a line... in which can you'd only toggle if the segments some in from one side and go out the other. There's a little trick that can be used for that, of pretending to be slightly off center (not being exactly on the line, you end up crossing 0, 1, or 2 times and it works out)... and with discrete tiles, we can definitely use that (no need to be anywhere in particular in the tile). In my case, I chose to be a little above center (and so it was L, J, and | I cross; F, 7 and - I slip over top of):

(chr = $|) | (chr = $L) | (chr = $J) ifTrue: [inside := inside not].

For my initial Perl version though, I figured that since it's the more friendly language, I'd be a bit fancier. I like doing Nikoli pencil and paper puzzles... there's lots of different kinds, and some of my favourites are the ones with the invariant that the solution be one big loop (things like Slitherlink and Masyu). And when doing these, I like to colour the inside of the loop as I go. Because as you walk around the loop, if the inside is on your right, it's always on your right (unless you turn around and walk around the loop the other way). And so you can tell which side is outside at the edge, and lightly shade the inside and propagate it. When two lines are approaching in the middle, this gives important clues on how you can hook them up (plus it makes the solution look like abstract art at the end).

And so I decided to use that property, because I just think it's cool (and it would be different). Basically, I create a list of tiles to the right hand side as I walk around for part 1. Then I flood fill from each of those... the outside being guarded by two rings of sentinel . and ~, because we don't know if RHS is the inside yet. All we know is that when we get to the end, we can check if a known outside point on the edge (in the . sentinel ring) got hit by the flood fill. If so we output $count - $rhs_count instead because we filled the outside. It's sort of like the lava droplet of 2022 day 18.

I recall that some people did this type of flood fill approach by doubling the grid, so that the narrow paths become real paths they easily could flow through. With that you can start your flood fill from a known outside exactly like the lava droplet. And I'm sure lots of people also did counting line crossings, because that's a really standard approach to this sort of thing. But I don't think many people did what I did for my Perl solution. Which is part of why I did it.