I am calling it! I declare my Advent of Code (AOC) for 2024 done! Finally!
Clarification: This does not mean that I did all of it, but I did what I wanted to do and was able to do to make the tree look like this …
The last star that was missing was part1 of Day21 (more on this later).
This year my main focus was on refreshing my scala3 skills and to learn a lot about how AI-enabled IDEs can support a scala-engineer to write better code and to solve problems faster. And (ooohhh boy) … it was a wild ride.
On the Scala3
side I …
- … used mill (instead of
sbt
) - … used
extensions
,given
andusing
(extensively) - … experimented with curly-brackets vs. indentation (the verdict is out on that one)
- … used scalafmt and scalafix (extensively)
- … used breeze (to solve linear equation systems and scala-graph (to find the shortest path in a graph (or 2-dim grid)) and scala-corner (to count the number of corners of regions in a 2-dim grid)
… and …
- … (last, but not least) used
VsCode/Copilot
and Windsurf to write code
Using AI to help me solve the problems was an interesting
experience. Here are my main take-aways …
- Sometimes the help I got was very effective and efficient. The approach was good and the code was good (and idomatic)
- But also (sometimes) less so. Which (sometimes) created problems when I tried to solve part2, because I either did not fully understand the code that was generated for part1 and was therefore not able to make good suggestions how to change the code to make part2 work or there was a small subtle bug in part1 that I did not see/find until I started to put some serious time into debugging part1
- I think for me to become more effective and efficient with using AI will require that I get much better at writing better prompts and providing better context(s) for the prompts (e.g. get better at tunning the windsurfrules file) and (first of all) develop a better feeling for when not to use AI or when to stop using it when it gets stuck in a loop or goes down a rabbit-hole (rule number one, when you are in a hole: Stop digging!)
- The new/next generation of AI-enabled IDEs (like Windsurf) are a quantum-leap forward. Mainly because they not only support chat, but also tools that allow you to run tools locally (e.g. run your tests and make changes to the code until all tests pass
- I tried and tested a lot of different IDEs, extensions and stand-alone tools. The one that worked/works best for me (right now) is Windsurf. It also features (now; since February 2025) a capability to configure and use MCP-servers (e.g. you can use the GitHub MCP-server to enable prompts that will manage your GitHub repo for you (creating issues, merging PRs, …))
- Also experimented with Warp. Warp is an AI-enabled terminal that make solving problems much more effective. Means my workflow is to use the IDE to develop code, but when it comes to all the other tasks (creatig tickets, building and deploying (docker) services, writing bash/python command-line tools, …) I am using Warp (which is much better than using the terminal in the IDE)
The work on AOC 2024 also inspired my to create a pod-cast series and a meetup about Augmented Software Engineering
. Feel free to subscribe. Feel free to join.
With all of this I was able to do solve part1 and part2 until we hit half-time (Day 13). Then I had to scale back to just solving part1. Hence the shape/look of the tree …
And even to get to this point took me a while (because of Day 21 (that I only/finally cracked over the weekend); see below).
So here are some of the high-lights from this years implementation …
- Day01 - Warmup and setup. Configure
scalafmt
- Day02 - Using
extensions
on standard types. Improves the readability of the code - Day03 - Using AI to do some good
regex
-ing to parse the input file - Day04 - Using
extensions
again/more - Day05 - Using
extensions
again/more - Day06 - Using
classes/methods
instead oftypes/functions
- Day07 - Sucessfully avoid the premature optimization trap. Found a minimal implementation for part1. And part2 was just a small change from there. Happy with that one.
- Day08 - More of the same. Nothing special about this one
- Day09 - The opposite experience of Day07. Found a solution for part1, but had to completely refactor/rewrite it for part2
- Day10 - The opposite experience of Day09. Guessed part2 right. Using an
array-of-array
instead of a set-of-positions - Day11 - Going back to using a
set-of-positions
for modeling the grid. And start using acache
to make part2 work (duh!) - Day12 - Used the scala-corner library to solve the problems. With the lib it was easy. Oh wait … ;)
- Day13 - LOL. Good one. Did part1 (the hard way). Overlooked that it is a system of linear equations. Changed gears for part2. And then I found breeze. Definetely one of these cases, where 20 mins of reading, thinking and searching (for the right library) can save you 2 hours of work (going into the wrong direction).
- Day14 - From now on I am just focusing on solving part1. Running a simulation on a grid (again; going with my trusted set-of-positions/-robots approach)
- Day15 - Another grid. Using sets-of-positions to model walls and also free-spaces (for elegance and speed). Having a wall around the grid also helps with the boundary-checking (you need none)
- Day16 - There must be a better way to solve grid-walking/shortest-path problems, right? And yes, there is: Let’s use scala-graph. The lesson learned is (big surprise): After really understanding the problem and after learning the library, the actual solution is short and elegant
- Day17 - A little bit of a breather. (Simple enough) State-Machine running instructions simulation problem. Good usage of
case classes
- Day18 - Another dfs-/bfs-grid-traversal problem. My ambition got the better of me. I first implememented a solution myself (worked, but slow and ugly). Then I refactored/reimplemented the entire thing. Switched to using scala-graph. Now it is fast and elegant
- Day19 - Nice. Read the problem description. Realize that you can solve it with
regex
pattern-mattching. This is the solutionraw"(${towels.mkString("|")})+".r
- Day20 - Another grid-traversal. Another usage of scala-graph. Decided to wrap scala-graph in my own wrapper (
util.GridGraph
) and (re)use it multiple times (Day16, Day18, Day20) - Day21 - Ooohhh boy. That one was my waterloo (or sea-monster, if you get my drift). Took me 3 month to crack it. First I (rightfully) wanted to use scala-graph again to find the shortest-path on the numeric keypad. Then I realized that I can build the following key-sequences on the directional keypads just by (string-)replacing the key-strokes (that works well). But then I failed to realize that my cost function for finding the shortest-path on the numeric key-pad was wrong. The fix was to calculate the cost dynamically and make the cost of paths that are straigt (e.g. »>) cheap
- Day22 -
LazyList
to the rescue. That one took less than 30 mins - Day23 - Used that one to experiment with a lot of different language models. The best solution was generated by
claude-3.5-sonnet
- Day24 - Almost done. Loved this one. It’s a boolean expression evaluator. Do we have one? Sure we do! It is called Scala!!! Means we just need to generate the right scala code (on the fly) and feed it to the toolbox
evaluate()
function. Done! - Day25 - Just the intersection of two sets (set-of-keys and set-of-locks)
That’s it. That’s a wrap. Really enjoyed it (again; maybe Day21 not so much ;)). Learned a ton. About Scala3 (again). And about using AI and Windsurf (specifically). And about using scala-graph. And about reading problem statements carefully. And about building stuff with some flexibility in mind, but not too much.
See you in December for AOC 2025.