# Advent of Code 2021 - Day 2

This topic is about Day 2 of the Advent of Code 2021.

We have a private leaderboard (shared with users of the elixir forum ):

The entry code is:
`370884-a6a71927`

4 Likes

Thank you @bjorng for introducing me to AoC Here’s my solution (omitting file read into list):

``````solve1(Data) ->
{X, Y} = lists:foldl(fun (X, Y) -> fold1(X, Y) end, {0, 0}, Data),
X * Y.

fold1(String, {X, Y}) ->
[Direction, ValueB] = binary:split(String, <<" ">>),
Value = binary_to_integer(ValueB),
case Direction of
<<"forward">> -> {X + Value, Y};
<<"down">>    -> {X,         Y + Value};
<<"up">>      -> {X,         Y - Value}
end.

solve2(Data) ->
{X, Y, _} = lists:foldl(fun (X, Y) -> fold2(X, Y) end, {0, 0, 0}, Data),
X * Y.

fold2(String, {X, Y, Z}) ->
[Direction, ValueB] = binary:split(String, <<" ">>),
Value = binary_to_integer(ValueB),
case Direction of
<<"forward">> -> {X + Value, Y + (Value * Z), Z};
<<"down">>    -> {X,         Y,               Z + Value};
<<"up">>      -> {X,         Y,               Z - Value}
end.
``````
5 Likes

Code:

``````-module(day02).
-export([p1/2, p2/2]).

p1([{forward, M} | T], {P, D}) ->
p1(T, {P + M, D});
p1([{up, M} | T], {P, D}) ->
p1(T, {P, D - M});
p1([{down, M} | T], {P, D}) ->
p1(T, {P, D + M});
p1([], {P, D}) ->
P * D.

p2([{forward, M} | T], {P, D, A}) ->
p2(T, {P + M, D + A * M, A});
p2([{up, M} | T], {P, D, A}) ->
p2(T, {P, D, A - M});
p2([{down, M} | T], {P, D, A}) ->
p2(T, {P, D, A + M});
p2([], {P, D, _A}) ->
P * D.
``````

Tests:

``````-module(day02_test).

-include("day02.hrl").
-include_lib("eunit/include/eunit.hrl").

p1_test() ->
?assert(day02:p1(test_data(), {0, 0}) =:= 150),
day02:p1(?INPUT_DATA, {0, 0}).

p2_test() ->
?assert(day02:p2(test_data(), {0, 0, 0}) =:= 900),
day02:p2(?INPUT_DATA, {0, 0, 0}).

test_data() ->
[
{forward, 5},
{down, 5},
{forward, 8},
{up, 3},
{down, 8},
{forward, 2}
].
``````

Edit: I subsequently refactored it to use lists:foldl/3 as I feel that is easier to read for my future self. Only P1 shown for brevity.

``````p1(InputData, State) ->
{P, D} = lists:foldl(fun p1_iter/2, State, InputData),
P * D.

p1_iter({forward, M}, {P, D}) ->
{P + M, D};
p1_iter({up, M}, {P, D}) ->
{P, D - M};
p1_iter({down, M}, {P, D}) ->
{P, D + M}.
``````
4 Likes

My lazier code for day 2 pensandoemelixir/day02_2021.erl at main · adolfont/pensandoemelixir · GitHub

And a command-line version that reads the file from stdin: pensandoemelixir/day02_2021_v2.erl at main · adolfont/pensandoemelixir · GitHub

And a video:

4 Likes

Ok, here is my dirty solution :

``````part1() ->
Input = [{forward, 5}, {down, 5}, {forward, 8}, {up, 3}, {down, 8}, {forward, 2}],
Output = lists:foldl(fun({X, Y}, Acc) -> Acc#{X => maps:get(X, Acc, 0) + Y} end, #{}, Input),
#{up := Up, down := Down, forward := Forward} = Output,
(Down - Up) * Forward.
``````
``````part2() ->
Input = [{forward, 5}, {down, 5}, {forward, 8}, {up, 3}, {down, 8}, {forward, 2}],
Output = lists:foldl(
fun ({forward, Y},  Acc = #{forward := Forward, depth := Depth, aim := Aim}) ->
Acc#{forward => Forward + Y, depth => Y * Aim + Depth, aim => Aim};
({down, Y},  Acc = #{down := Down, aim := Aim}) ->
Acc#{down => Down + Y, aim => Aim + Y};
({up, Y},  Acc = #{up := Down, aim := Aim}) ->
Acc#{down => Down + Y, aim => Aim - Y}
end, #{up => 0, down => 0, forward => 0, depth => 0, aim => 0}, Input),
#{forward := Forward, depth := Depth} = Output,
Forward * Depth.
`````` 4 Likes

I managed to put two days of Awk solutions in a single tweet:

Makes me wonder how easy it’d be to make an Awk-like construct for Erlang in escript… Might have to prototype that at some point. Would be neat to get something similar with parse transforms.

9 Likes

The first part is pretty messy and I am not going to share it here. However I am really proud of the second part:

``````partTwo([[<<"forward">>, Y] | Z], {H, D, A}) -> partTwo(Z, {H + binary_to_integer(Y), D + A * binary_to_integer(Y), A});
partTwo([[<<"up">>, Y] | Z], {H, D, A}) -> partTwo(Z, {H, D, A - binary_to_integer(Y)});
partTwo([[<<"down">>, Y] | Z], {H, D, A}) -> partTwo(Z, {H, D, A + binary_to_integer(Y)});
partTwo([], {H, D, _A}) -> H * D.
``````

I am getting all of the input from a `.txt` file in the same directory so that is why I am using binary strings.

4 Likes
5 Likes

Very OCaml-like there!

5 Likes

My take on day 2…

``````-module(day2).
-export([part1/0, part2/0]).

-record(state, {x = 0, depth = 0}).
-record(state2, {x = 0, depth = 0, aim = 0}).

part1() ->
#state{x = X, depth = Depth} = lists:foldl(fun(Command, Acc) ->
apply_command(Command, Acc)
X * Depth.

part2() ->
#state2{x = X, depth = Depth} = lists:foldl(fun(Command, Acc) ->
apply_command(Command, Acc)
X * Depth.

% For state
apply_command({forward, X}, #state{x = PrevX} = State) ->
State#state{x = PrevX + X};
apply_command({up, Depth}, #state{depth = PrevDepth} = State) ->
State#state{depth = PrevDepth - Depth};
apply_command({down, Depth}, #state{depth = PrevDepth} = State) ->
State#state{depth = PrevDepth + Depth};
% For state2
apply_command({forward, X}, #state2{x = PrevX, depth = PrevDepth, aim = PrevAim} = State) ->
State#state2{x = PrevX + X, depth = PrevDepth + (PrevAim * X)};
apply_command({up, Aim}, #state2{aim = PrevAim} = State) ->
State#state2{aim = PrevAim - Aim};
apply_command({down, Aim}, #state2{aim = PrevAim} = State) ->
State#state2{aim = PrevAim + Aim}.

List = binary:split(Binary, <<"\n">>, [global, trim]),
lists:map(fun(X) -> to_command(X) end, List).

to_command(X) when is_binary(X) ->
to_command(binary:split(X, <<" ">>));
to_command([Key, Value]) ->
case Key of
<<"forward">> -> {forward, to_int(Value)};
<<"up">> -> {up, to_int(Value)};
<<"down">> -> {down, to_int(Value)}
end.

to_int(Binary) ->
list_to_integer(binary_to_list(Binary)).
``````

It was new for me to replace Elixir structs with Erlang records.

And also, I did not find an equivalent to Elixir nested modules ``````defmodule Day2 do
defmodule State do
...
end
defmodule State2 do
...
end
...
end
``````
4 Likes

Indeed, nested modules do not exist, as the Erlang namespace is flat, and one module = one `.erl` file. What is solved by namespaces in other languages is normally solved by convention in Erlang. `http_server` vs `http_client` vs `tcp_client` and so on rather than `HTTP.Client`, `HTTP.Server` and `TCP.Client`.

I appreciate the flat namespace more and more each time I have to fight with all the infernal combinations of `go version`, `GOPATH` and `go.mod`, a chaotic system carefully engineered to ensure you will fail and you will increase Google traffic.

3 Likes