Advent of Code: Day 25 - Sea Cucumber
aoc2021 programming clojure ~6 minutes read

This is it. The last puzzle of the event. We’ve almost made it through. The only thing that stands between us, and the sleigh keys are two herds of sea cucumbers.

Cucumbers are constantly in the move, and our task is to simulate their behavior. There are two herds, one that always moves east, and another one that always moves south. They’re represented with > and v respectively:

....v.....
.>v....v..
.......>..
....v....>

On each step, first, the south herd moves one step forward, then the south herd moves one step downward. Cucumbers can only go into the empty place, marked with a dot ., and will wait if there’s no room. For the example above, the south herd moves first:

....v.....
.>v....v..
........>.
>...v.....

As can be seen the leftmost > couldn’t move, because there’s v standing in its way. The second and third > cucumbers moved freely. The third one actually crossed the gap of the field and moved to the other side, almost as if we’re in a cylindrical world. Then the south herd moves:

..........
.>..v.....
..v....v>.
>...v.....

All three topmost v cucumbers were able to move. However, because the moves are executed simultaneously, the bottom v could not move, as the other cucumber was still in the starting position, from its perspective.

So we basically need to implement these rules and run the simulation. I hope the second part won’t require us to run the simulation one million times on a much bigger field, otherwise, I will be very disappointed. Looking at the leaderboard, it seems that the second part took approximately six seconds to solve, so it’s either some very little derivation in the algorithm, or the requirements for the task are just bigger, but the code is so optimal that it can handle it in no time.

Ok, we actually need to write the code today, so let’s do it quickly:

user> (ns day25 (:require [aoc-commons :refer [slurp-lines transpose]]))
nil
day25> (defn read-input []
         (->> "inputs/day25"
              slurp-lines
              (mapv vec)))
#'day25/read-input

Since there are no diagonal moves, we can restrict the problem to a single row at a time. As we need to move a cell in the row, I think reduce-kv can do:

day25> (defn move [kind row]
         (let [len (count row)]
           (reduce-kv
            (fn [row' i c]
              (if (= c kind)
                (let [pos (mod (inc i) len)]
                  (if (= (nth row pos) \.)
                    (assoc row' i \. pos kind)
                    row'))
                row'))
            row row)))
#'day25/move
day25> (move \> [\. \> \> \. \>])
[\> \> \. \> \.]

This function accepts kind of cucumber we’re moving, so we can reuse it for vertical movement as well. And for vertical movement we will just transpose our world, and repeat the process:

day25> (defn step [world]
         (->> world
              (mapv (partial move \>))
              transpose
              (mapv (partial move \v))
              transpose))
#'day25/step

The only thing left is to write a function, that will run our simulation until the world stops changing:

day25> (defn part-1 [input]
         (reduce (fn [world i]
                   (let [world' (step world)]
                     (if (= world' world)
                       (reduced i)
                       world')))
                 input (drop 1 (range))))
#'day25/part-1
day25> (part-1 (read-input))
367

And part one is done. It was quite easy, compared to the two previous puzzles. Let’s see what awaits us in part two? I guess not.

Day 25 and event ending thoughts

Unfortunately, by the time I’ve finished the first part, I only had 44 stars. And part two unlocks only if you have 49 stars. So perhaps the last star isn’t a variation of the first part at all, but some kind of congratulations or something like that. However, I will not see what’s there unless I’ll go back and solve day 19, part two of day 22, and day 24. I’m not sure if I will, though.

The puzzle for day 25 was a joke. I’ve thought, that since this is a weekend, the task will be brutal, as was on both previous weekends. I guess it’s a holiday, and the task is not hard, so everyone could hang out with their families, friends, and so on. Well, I couldn’t - I was on a train solving and writing this on a phone yet again. It’s a bit sad that there was no part two, but I guess it’s my own fault. I couldn’t keep up with the difficulty spikes, and lost motivation over the last few days, because the variety of tasks was (IMHO) very low. Looking back at it, there definitively were interesting tasks.

As I said, my problem with this year’s event is that the variety wasn’t very big. There were three tasks with exponential growth1, that required finding a way to tame this by grouping the data in some way. Six tasks about 2D data structures2, not including two cellular automaton-like tasks3, which also were 2D. Two tasks about binary data processing4. One task for navigating trees5, and two for navigating graphs6. Two tasks could have been solved without any code fully on paper7. The rest are hard to categorize.

Probably, my favorite ones were on days 8, 11, 13, 15, 17, 18, and 20.

I really like cellular automaton type of puzzles, as they allow you to run the simulation, and even visualize it. I’ve done this the last year, and this year was no different. However, I think there were just too many tasks of this type. The fun ones were on days 11 and 20.

I really liked day 8 because it was a neat task to figure out what wire corresponds to what - kind of reverse engineering, which required you to also write a program that will unmangle the input. Unlike day 24 which was also about reverse engineering, but required no code to solve, as was shown by a lot of people in the community.

I’ve liked days 15 and 18 because they’ve taught me new things - I’ve learned the path-finding algorithm, and a library for navigating trees that I’ve long wanted to try out.

Days 13 and 17 were cool 2D tasks, which I’ve simply liked.

The tasks that I’ve really despised were tasks on days 6, 14, 19, 22, 23, and 24. I simply do not find these types of tasks fun. The challenge is also questionable, especially for the last 4 of these. They were more tedious than hard IMO.

Other than that, the event went mostly smoothly. I had some bad days where events in my life combined with the increased difficulty of tasks made me really angry (and you should never be angry about something like puzzles). Overall, if I was asked to explain my thoughts about this event in a single word, this word would be “meh”. I wanted to try to solve as many puzzles from the event as I can, and also write a post in my blog for every one of them. Maybe this was a mistake, as both solving, and writing in a single day is more exhausting than only solving a puzzle itself. I don’t think I’ll repeat this kind of format the next year if I will even participate. But if I will, I’ll probably do small writeups about days that I’ve really enjoyed instead.

I hope you’ve had fun during this event, and thank you for reading. Merry Christmas and until next year!


  1. days 7, 14 and 21 ↩︎

  2. days 4, 5, 9, 13, 15, and 25 ↩︎

  3. days 11 and 20 (25th was also kinda↩︎

  4. days 3 and 16 ↩︎

  5. day 18 ↩︎

  6. days 12 and 15 (8th was also kinda↩︎

  7. days 23 and 24 ↩︎