Clojure solution to Advent of Code 2016 day 1

2022-02-11

I saw someone on Twitter solving old Advent of Code challenges with Clojure and I got inspired to try it myself. So here's my solution to Advent of Code 2016 day 1 using Clojure.

The code is on GitLab but I'll include the main function here:

(defn start [document]
  (loop [distq [0 0] facing 0 orders (parse-doc document)]
    (if (empty? orders)
      (apply + (map #(Math/abs %) distq))
      (let [[RL nblocks] (first orders)
            delta (* nblocks ([1 1 -1 -1] facing) RL)]
        (recur
         [(last distq) (+ (first distq) delta)]
         (mod (+ RL facing) 4)
         (rest orders))))))

I was able to find a few shortcuts which reduced the amount of code I had to write.

First, I chose to represent "right" as the number 1 and "left" as the number -1. This makes it easy to move and change direction, as we'll see.

The compass rose directions are represented with increasing numbers "clock-wise". North is 0, east is 1, south is 2, and west is 3. To change direction after a move I add either 1 or -1 depending on if we went right (1) or left (-1), and finally take the modulus 4 of the result to get back into range [0 3].

A key point is that for each iteration through the loop, the current position only moves along either the X- or the Y-axis. We start facing north and then turn either right or left, thus only affecting our position on the X axis. If we turned right, we start the next loop facing east and can only move on the Y-axis.

Because we alternate between X and Y this way, each iteration need only be concerned with our position on that axis. This is where the variable distq comes into play. Its first element is our position on the current axis. That's why in the call to recur their positions are swapped.

On computing delta. The variable delta represents how much the position on the current axis will change due to our move. If the move is "R5" then nblocks will be 5. If we are facing north (0) or east (1) and go right, then the axis value will increase, represented by 1. If we are facing south (2) or west (3), then the axis value will decrease if we go right, represented by -1. Finally we multiply by RL, which is 1 if we went right, and -1 if we went left.