Advent of Code, in Erlang: Day 2

Published Monday, December 13, 2021 by Bryan

While I wait for it to get a little later into Day 13, before posting spoilers for those that haven't solved it yet, let's talk about Day 2.

Part 1 described three commands. Two modify one value (the depth of a submarine), and the third modifies a second value (the submarine's distance across the ocean floor, a.k.a. "X"). My solution is a very typical Erlang list iteration:

solveA(Commands) ->
    reportA(Commands, #stateA{x=0, depth=0}).

reportA([], Position) ->
reportA([<<"forward ", Move/binary>>|Rest], State=#stateA{x=X}) ->
    reportA(Rest, State#stateA{x=X + binary_to_integer(Move)});
reportA([<<"down ", Move/binary>>|Rest], State=#stateA{depth=Depth}) ->
    reportA(Rest, State#stateA{depth=Depth + binary_to_integer(Move)});
reportA([<<"up ", Move/binary>>|Rest], State=#stateA{depth=Depth}) ->
    reportA(Rest, State#stateA{depth=Depth - binary_to_integer(Move)}).

After breaking the input into a list of lines, I recurse through the lines, updating the depth and x values of my state record, as directed. This could have been written in the "fold" syntax I waxed lyrical about yesterday, but this time I wrote it out longhand. Sometimes that's the way it works out.

{stateA,15,10} = puzzle02:solveA(Example).
150 = 15 * 10.

Part 2 requires exactly the same parsing, but different interpretation. This time, one command (forward) modifies both x and depth, while the other two commands update a completely different bit of state (aim). The twist is that forward's modification also depends on the latest value of that other bit of state.

solveB(Commands) ->
    lists:foldl(fun(<<"forward ", Move/binary>>,
                    State=#stateB{x=X, depth=Depth, aim=Aim}) ->
                        M = binary_to_integer(Move),
                        State#stateB{x=X + M, depth=Depth + Aim * M};
                   (<<"down ", Move/binary>>, State=#stateB{aim=Aim}) ->
                        State#stateB{aim=Aim + binary_to_integer(Move)};
                   (<<"up ", Move/binary>>, State=#stateB{aim=Aim}) ->
                        State#stateB{aim=Aim - binary_to_integer(Move)};
                #stateB{x=0, depth=0, aim=0},

Surprise! I thought it would be fun to have a semi-direct comparison of manual recursion versus fold. If this comparison isn't direct enough, you can also view the git diff where I changed solveB just now to demonstrate. Which do you like better? I think each has its own noise and clutter, but feel free to send me your opinion on Twitter (@hobbyist) or Github (beerriot).

{stateB,15,60,10} = puzzle02:solveB(Example).
900 = 15 * 60.

The ending state shows what we expected: x is the same as last time, and aim took the same value that depth had previously, but now our submarine has ventured much deeper.

Good luck to those solving Day 13 right now! Come back later to read my solution.