Advent of Code 2021

David Wagner

I solved the Advent of Code, puzzles. This post, like the one from last year, is an experience report of my 25+ day journey in December.

Spoiler alert: If you’re still working or planning to work on the puzzles stop reading now.

Puzzles

Between December 1 and December 25, a programming puzzle appears every day at Advent of Code website. Each problem has two parts, the second part unlocks after you complete the first. To understand each day’s puzzle, go to the website and read the full description (for example Day 1: Sonar Sweep).

Setup

I decided to learn Clojure and to solve the puzzles in this functional dialect of the Lisp language. In the past few years I believe I watched every available conference talk of Rich Hickey, the creator of Clojure. It was time for a full-immersion into the language he designed.

I knew from last year, when I implemented the solutions in Rust, that learning a new programming language while thinking about challenging puzzles is frustrating. So I decided to learn some Clojure before the puzzle series begins.

In November I read a few chapters of The Joy of Clojure and I rewrote some of my Advent of Code 2020 solutions in Clojure. With this preparation I could write decent code from Day 1 but I often restructured my older programs as I learned more about the language.

Just like last year, I didn’t care about code size, performance optimizations (only when it was the goal of the puzzle) and coding speed.

Highlights

My favorite puzzles were Day 8 and Day 13. The former was about decoding the activation signals of a broken seven-segment display and the latter was about folding a large sheet of transparent paper to reveal a secret code. Both problem statements were funny and they fit well into the Christmas-themed story line.

The core algorithms of Day 12, Day 15 and Day 23 were about graph manipulation. I enjoyed implementing traversals and solutions to the shortest path problem.

The puzzles of Day 9, Day 11, Day 15 and Day 25 were stated on a two-dimensional grid. The task of updating the grid or parts of it appeared in many forms. In some cases I could write expressive code using Clojure’s threading macros and the cond-> macro, but I also feel that some of my grid manipulation code is not idiomatic and hard to understand.

I solved Day 6 and Day 14 using infinite sequences. Here Clojure really shines: the core sequence manipulation algorithms work well together resulting in short and expressive code.

On Day 16, Day 18, Day 21 recognizing trees, recursion and the need for memoization were the key elements to write a clean solution.

I spent the most hours on Day 17, Day 19 and Day 22. After multiple failed attempts to solve these days I looked for hints on Reddit. Many smart people hang out in that forum and there’s a lot to learn from the conversations there.

My least favorite day was Day 24. I didn’t see the point in deciphering someone else’s obfuscated code. I do this enough at work already.

Learning Clojure

Using this puzzle series to learn a new programming language is stressful, but effective. Just like last year, solving these puzzles left me with some thoughts about Clojure:

  • REPL: One big selling point of Clojure is its support for interactive development. I set up my editor with Conjure and in a few hours I got used to evaluating pieces of code directly from the editor.

  • Testing: I struggled to set up Cognitect test runner, but once I learned the proper way to structure my project things just worked all right. When I’m using other languages I run the tests from a separate command terminal. With Clojure it’s easy to run the tests without leaving my editor.

  • Core functions: Clojure core contains a ton of useful functions which are easy to use. I used only one external library: data.priority-map. The built-in documentation is terse and short on examples but ClojueDocs improves this and I used it all the time.

  • Regular expressions: Last year I spent a lot of time of writing parsers for the input files. This year I used regular expressions almost exclusively and the parsing code is rarely longer than a few lines. Clojure’s literal regular expressions syntax and the function re-seq, which returns a sequence of successive matches, are great.

Proponents of Clojure claim that programs written in this language are short and expressive because they are mainly about the problem at hand and not about classes, accessors, lifetimes and other incidental programming language constructs.

In the past few weeks coding in Clojure I find this claim justified. In the future I’d love to explore how is it to build a large system in the style of programming Clojure encourages, that is using functions and generic data transformations.

Summary

Completing the Advent of Code and learning a new programming language was challenging, but a rewarding experience. After solving the puzzles I like to see how others approached the same problem. Here are some repositories I regularly checked:

The source code of my solutions are also available on GitHub.

Acknowledgment

Thanks Eric Wastl for creating and running Advent of Code.

Thanks Olivier Dormond for exchanging ideas and congratulations for winning our private leaderboard!