Advent of Code 2023 Recap

This post is of a different sort, so buckle up for something new. I recently completed the Advent of Code 2023 and wanted to recap what I learned. The Advent of Code is a yearly set of puzzles posted daily between December 1st and 25th. Programmers all over the world solve them with different technologies and tactics. I chose to solve the puzzles in Python to continue learning the language.

The first few days I tried completing the puzzles as quickly as possible. Unfortunately, to know how fast you are relative to everyone else you have to start at midnight Eastern Time which isn’t my preferred working window. I alternated between staying up and attempting them at midnight (to see my absolute ranking) or waiting until the next morning after my coffee. 

The early puzzles were fun and I was able to complete them in Python relatively quickly. I read the discussions on Reddit after completing the puzzles and learned how others solved the problem. I learned how to use the “all” function, reinforced the good use of “enumerate”, and realized how powerful “zip” is for grid puzzles. I used all of these in future puzzles and felt myself getting better at Python.

I am amazed to see how some solutions are so compact, especially with Python’s ability to use list comprehensions to create complex lists. I found grid manipulation much easier as I used list comprehensions and the zip function.

I learned Tuples are hashable when lists are not. So paying attention to whether you’re using a Tuple or a generic collection object becomes important if you’re trying to cache parameters or use a set. 

Here’s my recap of specific days with some details that might be considered spoilers.

I did the best on Day 14 completing both parts in the first 1500 people. The ability to quickly switch between rows and columns using “zip” along with quickly realizing how to shuffle the rocks with “split” and “join” helped me get there quickly. And having an inkling of how part two was going to pan out from prior puzzles was key.

I hated Day 12 for making me think there was a math solution I was missing. Day 13 was easy but I made it hard, which seems to be my experience as these puzzles go on. Day 15 was fun and I finished part 1 quickly (as did many people), only to find part 2 took more time to comprehend than eventually implement once the requirement was clear.

I learned on Day 16 that recursion is slower than iteration. I came to appreciate using “pop” and “append” with a list in a loop and the various uses of depth-first and breadth-first search. Also, I realized the value of a set as a collection of unique items rather than just a list.

Day 17 was the first day whose solution I didn’t complete within 24 hours. Puzzles have gotten much harder. I struggled to get the pathfinding right on part 2 with the customized rules. For Day 18, I implemented a simple flood fill that failed on the massive part 2 but I had insight from prior puzzles on how to compute with math.

Day 20 seemed like a folly - a Rube Goldberg machine designed solely for this puzzle. Reading and parsing the requirement felt like the real challenge, although I felt accomplished for implementing a solution fairly quickly that worked. I felt satisfied that I used a queue for signal management.  

I spent a lot of time thinking caching was going to solve my problems on Day 21, only to realize I didn’t need caching or recursion to make the solution as fast as it was going to get. I had an intuition that there was a mathematical formula that could predict the answer, some kind of equation as a function of the length squared since we were moving in two dimensions. I saw patterns in the ever-expanding grid, and it helped me understand how the relationship worked.

I was simulating falling bricks like three-dimensional Tetris on Day 22. I had a pretty good solution early on but struggled with bugs. I originally built from the ground up and eventually realized I needed to somehow simulate the top-down falling to allow bricks to hit lower levels if nothing was in their way.

I found Day 23 part 2 hard to optimize and I eventually got a good answer after letting my original solution run a very long time. I did a lot of work reducing the computation required because it seemed I needed to calculate every path to find the longest. I eventually refactored to just use the vertices of the junctions and eliminated any unnecessary computation and that worked fastest.

I hate to ever be critical of AoC because I can’t imagine how hard it is to put it all together, but Day 24 was poorly worded in my opinion. I struggled in part one to understand the problem. It seemed that something was missing in the description. I eventually sought help from Jonathan Paulson’s YouTube (he posts his solutions as he’s solving them). It was nice to see he made the same (wrong) assumption I did at the beginning (he ended fifth on the global leaderboard in 2023). 

Jonathan’s video helped me to realize the problem was asking for intercepts of paths regardless of time. The sample indicates intercepts that “happened in the past” weren’t valid and led me to feel like time was still in play. Once I understood the problem, I enjoyed the math and did some hand-solving of the equations. I learned that Python has a library to solve simultaneous equations.

Day 25 would have been easier with a better foundation of discrete math and graphs. I learned there are whole algorithms available to solve this problem (and libraries in Python with them implemented). I ended up with a slow brute force method which finished within an hour on Christmas day. I learned how many ways there are to solve this problem and implemented some to better understand them.

By the numbers:
Most Favorite: Day 14, Parabolic Reflector Dish
Least Favorite: Day 24, Never Tell Me The Odds
Fastest Solution: Day 14, finished 1,473rd globally for both parts (slightly better than last year’s 1,516th globally)
Three solutions took more than a day for both parts (similar to last year where four solutions took >24 hrs)

I used some new libraries for cool stuff including sympy (equations solver) and numpy (polynomial fit).  I learned some new concepts like the shoelace formula for polygon area and the min-cut theorem for graphs. I got much better at implementing various search algorithms. 

Thanks to Eric Wastl for putting together another great Advent of Code in 2023.