Advent of Code: 500 Stars

A few days ago I finally finished getting all 500 stars in Advent of Code. For those of you that aren't familiar, Advent of Code is a yearly series of programming challenges which are released each day from December 1 to December 25, like an advent calendar. Each challenge has two parts, and the second part is only revealed once you solve the first part. Some people compete to solve both parts as fast as possible but I don't really bother with that, because a) I'm not very fast nor do I want to practice being fast and b) the problems come out at 9pm my time. I had also missed parts of a few years and missed two years entirely, so before I started working on the backlog I had something like 270 stars out of 500 total.

Over the years, I've done Advent in Rust once, Fennel once, Scheme (or really Racket) twice, Forth once (although I only got partway), and C the rest of the years. I've found that all of these languages are perfectly nice to do Advent in, although I struggled a couple of times with getting the number-crunching I needed to do to be fast enough in Fennel, and my lack of experience with Forth made it very challenging. In general I think that you could probably do Advent in any language, as long as you made good choices for algorithms and data structures.

Aside: if you care about speed, an obvious best choice has emerged on the leaderboards: Python. The combination of a pretty fast interpreter, access to libraries like pandas and numpy, and integration with the powerful constraint solver z3 gives this language a clear edge and most of the fastest solvers every year are working in it. Z3 in particular is amazingly useful, and a good chunk of the problems can be solved quickly by massaging them into something Z3 can handle.

Second aside: recently, especially on the first few days, the fastest solves have gone to AIs, which can read the problems and spit out something kinda-working faster than humans can, so you will see a bunch of people on the leaderboard with solve times of under 10 seconds. This is obviously very cringe and I hope these people feel shame about it.

In my experience the Advent problems can generally be solved with a first year CS student's level of algorithmic and mathematical knowledge, and good solutions run in under a minute on a ten-year-old laptop. Down below I made a list of all the algorithms and data structures I found myself using as I worked through the puzzles. There are a few one-off challenges that require stuff outside that set, but I've listed all of the most common building blocks.

# Favorite Challenges

There are very few Advent puzzles that I don't enjoy, so picking favorites isn't easy, but here's a list of the puzzles I liked the most from each year:

# Common Algorithms & Data Structures

# Advice

Read the whole problem. It's really shocking and kind of embarassing how often I skimmed over a key detail while reading the problem, then implemented entirely the wrong thing. What I eventually settled on was reading the problem, taking notes (in my solution file) about what the problem was, then reading over it again from the top after making those notes to check whether I had missed anything. There are very rarely extraneous remarks in the problem descriptions, so it's best to read them closely.

Pay attention to bolded text. For example, 2019/22 part b bolds the word "overflow" in the problem description, which was a warning that I ignored. My solution was overflowing 64-bit integers and it took me quite a while to track down why it was misbehaving, since integer overflow is silent in C. If I'd paid attention to that, I would have checked for overflow and noticed the problem earlier.

Specialize. Sometimes, Advent problems are very difficult or even impossible to solve in their general form, but your input (and everyone else's input) has some property not mentioned in the problem description that makes it possible to solve more easily. If you find yourself reaching for advanced mathematics or trying to do some arcane optimization to your solution, it might mean there's a special property in your input that you haven't noticed yet - so look for it, before you wander off and spend three weeks implementing heuristic solutions for general integer programming problems or something like that. Remember: your solution only has to work on your input.

Run the test cases. Run all of them, and check that not just the final outputs but any intermediate states you're given match what the problem says. They are there to help check that you understand the problem!

Don't guess at part b. Sometimes when you're working through part a, you will have a guess about what part b is, and you will be tempted to start implementing things you are confident you are going to need, or optimizations you think will be necessary, or whatever. Don't.. The second part is almost always not what you think, and your extra work will be either wasted or actively counter-productive. Do part a in the simplest, most naive way that you can, and then start getting clever once you know what part b is.

Ignore the leaderboard. There are always going to be people out there who are faster than you are; don't let it stress you out and don't try to compete with them. The best way to enjoy Advent is to compete with yourself to see how far you can get.

Advent is not a good way to learn a new language. It's fine for the first few days, but as you get into the later days, you will find yourself trying to do too many things at once - grappling with an unfamiliar language while you problem-solve and debug. It's overwhelming, and it prevents you from learning as well as you would if you just focused on learning. I recommend sticking with a language you already know at least somewhat well, so you can focus your energy on the problem-solving part. This is partly from my own experience trying in Forth, but also from watching friends decide they're going to use Advent to finally learn Lisp / Rust / Forth / Smalltalk / APL / whatever else. It just doesn't work very well. Learn one thing at a time!

# Closing Thoughts

Including re-solutions, tests, attempts that I didn't end up finishing and everything else, while working through the first 500 stars of Advent of Code, I wrote:

My shortest solution to a problem after day 5 (since the first few days are usually very short) is 2015/17, which I solved in 12 lines of Racket. My longest solution to any problem, in any language, is 2020/20, the aforementioned Jurassic Jigsaw, for which my solution is 560 lines of C. Whew!

On a personal note, I am very very proud of having finished all of these. It's one thing to work through them at the same time as everyone else is, where you get to share your solutions and talk through the problems with people; it's another to grind through the ones you missed on your own, and I am happy with myself for sticking with it and putting in the effort. Good work, past-Elly. I'm planning to write another post later on about the libraries I ended up building in more detail, but it'll be focused mostly on the implementation; this is my main post about the experience.

I am also very grateful to Eric Wastl and all the other folks who make Advent of Code happen every year. It's free to participate and run entirely by volunteers, and I'm sure that running a time-boxed event for thousands of people every year as smoothly as they always do is a major undertaking. I donate to support Advent of Code and you should too.

I'll end by saying that you, whoever you are, should also give Advent of Code a try! It's never a bad time to start, and there's a lot of fun programming and computer science / math practice to be had out there. Plus, at the end you get something (your solutions) that you can show off to other people. If you're curious, you can see my quite messy repo of solutions on sourcehut.