Geoffrey Wiseman

Advent of Code 2021 (I-IV)

I do enjoy Advent of Code (AoC), although it has a tendency to soak up too much time until I give up and accept that I don't have the time to finish the month. Some years I give in sooner than others.

JetBrains is encouraging people to write some Advent of Code solutions in Kotlin, which reminded me to start up again this year. I've used Kotlin for AoC before, and I couldn't remember what I felt about it.

Day 1 (Kotlin)

I wrote Day 1 in Kotlin.

Getting started took me a few minutes. This reminded me of why I often don't use Kotlin for small things, the same way I tend not to use Java for small things. There's a lot of overhead in a typical Java / Kotlin project:

  • build tools (Maven, Gradle) that are familiar, but perhaps overkill for a project with one or a small handful of files
  • an expansive project structure used by those tools which is a bit overkill as well
  • tooling that tends to assume those build tools and project structures

Sure, there are alternatives:

  • JetBrains has a template repository you can use if you're going to write all the solutions in Kotlin to share a project across all the days, but I like the flexibility of picking a different technology for different days, so I didn't use that path
  • You can simply write some Java/Kotlin classes and compile them by hand, with limited tool support.
  • You can customize the project structure and the the build tool and editor about it.
  • You can use a lightweight editor that has fewer assumptions but maybe offers less of the tooling support you get pretty used to on a Java/Kotlin project.

Anyway, the code itself was fine, and fun to write, but the setup was a bit painful and feels like too much for this kind of work.

This exercise was pretty easy with Kotlin -- you can do each part in one line of code (as long as you count a sequence of stream/sequence operations as a line). Judicious use of zip and windowed, sum and count and you're done.

Day 2 (Kotlin)

I did Day 2 in Kotlin again.

This time, I skipped the build tools and project setup, and built it using Kotlin Script (.kts), which I'd done on some of the previous years.

Since I wasn't planning a project structure, I tried opening up the script in JetBrains LiteEdit mode. It didn't go well. There's a bug that impacts LiteEdit on MacOS, has been around for a year and a half and makes LightEdit borderline unusable because the menu bar is constantly rebuilding which is both distracting and also slows down the editor.

I tried VSCode as well, but that wasn't much better -- I wasn't getting much in the way of completions or error detection, so I was using VSCode like a text editor and discovering my problems when I ran the code using kotlinc. The language was fine, but the tools weren't persuasive.

Again, the problem was a reasonable fit for Kotlin -- parse with a simple split, use a when to match the commands, and a little bit of math.

Day 3 (Kotlin)

Third attempt with Kotlin on Day 3. During Day 2, I noticed a new Kotlin extension for VSCode but I decided I'd give it a shot next. I tried, but the language server crashed right away. I did a little poking around, found some issues that might be related and decided it wasn't worth the effort for now to try and diagnose. So, finished using VS Code as a text editor again.

For the problem, I used maxByOrNull for part 1, but when I got to part 2, I hit an issue that when two items in a list are equal, maxByOrNull doesn't really let you intervene directly. I'm less pleased by this solution -- I'm sure it could use further refinement.

Day 4 (Go)

I wrote Day 4 in Go. I didn't feel like dealing with Maven/IntelliJ or LiteEdit or VSCode Kotlin extensions, so I may be done with Kotlin for this year's AoC. If I use it again, I'll probably go back to building a whole Maven/IntelliJ project as the least objectionable, but does really feel like significant overkill.

There are things I like about Go, although I would say that I haven't gotten to the point where I look forward to using it. I'm hoping with more exposure, the things I like about Go will eventually overwhelm the things I dislike, so I built Day 4 with Go. It was painful.

Go keeps the langauge and libraries pretty simple. I see the appeal of that, but I often find that pushes a bunch of work back into your hands that feel pretty easy in other languages. Go is fast to compile and run, has some nice libraries and features, but sometimes I feel like i'm writing a lot of code to do fairly simple work. Day 4 did not disappoint in that regard.

I feel like I spent as much time doing mechanical work (like writing an atoi that runs across a slice). This kind of work is incredibly trivial in Ruby, JavaScript, Python, Kotlin, but in Go, you gotta write it. It's not hard, and there may be a library out there that offers common operations, but ... feels like I'm being punished for using Go. And Go error handling still feels like a Java project that makes heavy use of checked exceptions. There ends up being a lot of code dedicated to errors that shouldn't come up, but ... could.

In the end, I felt like I could have done this program significantly faster and with significantly fewer lines in many other languages. I'm sure I could still use more Go practice, and maybe with more practice, it'd come more naturally to me, but it did not make me want to use it for the next day's puzzle.

Looking around at other solutions:

  • I should obviously use Log.fatal and Log.fatalf:
    • Logs an error and then exits.
    • Might not be the right choice for production code, but totally fine for this sort of thing.
  • Most of the other Go solutions for Day 4 look pretty long as well
  • Apparently I ought to learn NumPy

More to Come ...

I've already done Days 6 & 7, but I'll write them up in the next post.