Advent of Code 2022 Day 9 - Discussions

This topic is about Day 9 of the Advent of Code 2022 .

Link to our leaderboard:

https://adventofcode.com/2022/leaderboard/private/view/370884

The entry code is:
370884-a6a71927

Good luck!

2 Likes

Today’s challenge was quite fun :slight_smile:

main(File) ->
    {ok, RawData} = file:read_file(File),
    Data = [ begin
                 [D, L] = binary:split(Line, <<" ">>, [global, trim]),
                 {D, binary_to_integer(L)}
             end
             || Line <- binary:split(RawData, <<"\n">>, [global, trim]) ],
    io:format("part 1: ~p~n", [solve1(Data)]),
    io:format("part 2: ~p~n", [solve2(Data)]).

solve1(Data) ->
    sets:size(move_head({0, 0}, [{0, 0}], Data, sets:new())).

solve2(Data) ->
    sets:size(move_head({0, 0}, lists:duplicate(9, {0, 0}), Data, sets:new())).

move_head(_HeadPos, _Tails, [],                 SetAcc) -> SetAcc;
move_head( HeadPos,  Tails, [{_Dir, 0}  |Rest], SetAcc) -> move_head(HeadPos, Tails, Rest, SetAcc);
move_head({X0, Y0},  Tails, [{ Dir, Len}|Rest], SetAcc) ->
    NewHeadPos = case Dir of
                     <<"R">> -> {X0 + 1, Y0};
                     <<"L">> -> {X0 - 1, Y0};
                     <<"U">> -> {X0, Y0 - 1};
                     <<"D">> -> {X0, Y0 + 1}
                 end,
    NewTails = move_tails(NewHeadPos, Tails),
    move_head(NewHeadPos, NewTails, [{Dir, Len - 1}|Rest], sets:add_element(lists:last(NewTails), SetAcc)).

move_tail({X0, Y0}, {X1, Y1}) when X0 == X1,          abs(Y0 - Y1) == 2 -> {X0,              (Y0 + Y1) div 2};
move_tail({X0, Y0}, {X1, Y1}) when X0 == X1,          abs(Y0 - Y1) == 2 -> {X0,              (Y0 + Y1) div 2};
move_tail({X0, Y0}, {X1, Y1}) when Y0 == Y1,          abs(X0 - X1) == 2 -> {(X0 + X1) div 2, Y0};
move_tail({X0, Y0}, {X1, Y1}) when abs(X0 - X1) == 2, abs(Y0 - Y1) == 1 -> {(X0 + X1) div 2, Y0};
move_tail({X0, Y0}, {X1, Y1}) when abs(X0 - X1) == 1, abs(Y0 - Y1) == 2 -> {X0,              (Y0 + Y1) div 2};
move_tail({X0, Y0}, {X1, Y1}) when abs(X0 - X1) == 2, abs(Y0 - Y1) == 2 -> {(X0 + X1) div 2, (Y0 + Y1) div 2};
move_tail(_HeadPos, TailPos) ->
    TailPos.

move_tails(_Head, [])          -> [];
move_tails( Head, [Tail|Rest]) ->
    NewTail = move_tail(Head, Tail),
    [NewTail|move_tails(NewTail, Rest)].
2 Likes

Wow, this is fun! I just finished Part 1. On to Part 2… (EDIT: Done with that!)

-module(day_9).

-export([solve/0, move/4]).

input() ->
  {ok, Data} = file:read_file("data/9.txt"),
  Data.

parse(Data) ->
  Lines = binary:split(Data, <<"\n">>, [global, trim]),
  Instructions = lists:map(fun parse_instruction/1, Lines),
  Instructions.

parse_instruction(Line) ->
  [A, B] = binary:split(Line, <<" ">>),
  {binary_to_list(A), binary_to_integer(B)}.

solve() ->
  Data = parse(input()),
  {solver(Data, [{0, 0}]), solver(Data, lists:duplicate(9, {0, 0}))}.

solver(Data, Tails) ->
  Reducer =
    fun({D, V}, Acc) ->
       lists:foldl(fun(_, Acc2) ->
                      {HeadPos, TailPos, Visits} = Acc2,
                      move(HeadPos, TailPos, D, Visits)
                   end,
                   Acc,
                   lists:seq(1, V))
    end,
  {_, _, Visits} = lists:foldl(Reducer, {{0, 0}, Tails, []}, Data),
  sets:size(
    sets:from_list(Visits)).

move({HX, HY}, Tails, D, Visits) ->
  HeadPos =
    case D of
      "U" ->
        {HX, HY + 1};
      "D" ->
        {HX, HY - 1};
      "L" ->
        {HX - 1, HY};
      "R" ->
        {HX + 1, HY}
    end,
  NewTails = move_tails(HeadPos, Tails),
  {HeadPos, NewTails, [lists:last(NewTails) | Visits]}.

move_tail({HX, HY}, {TX, TY}) when HX == TX, HY - TY == 2 ->
  {TX, TY + 1};
move_tail({HX, HY}, {TX, TY}) when HX == TX, TY - HY == 2 ->
  {TX, TY - 1};
move_tail({HX, HY}, {TX, TY}) when HY == TY, HX - TX == 2 ->
  {TX + 1, TY};
move_tail({HX, HY}, {TX, TY}) when HY == TY, TX - HX == 2 ->
  {TX - 1, TY};
move_tail({HX, HY}, {TX, TY}) ->
  case {HX - TX, HY - TY} of
    {1, 1} ->
      {TX, TY};
    {-1, -1} ->
      {TX, TY};
    {1, -1} ->
      {TX, TY};
    {-1, 1} ->
      {TX, TY};
    {DX, DY} when DX > 0, DY > 0 ->
      {TX + 1, TY + 1};
    {DX, DY} when DX > 0, DY < 0 ->
      {TX + 1, TY - 1};
    {DX, DY} when DX < 0, DY > 0 ->
      {TX - 1, TY + 1};
    {DX, DY} when DX < 0, DY < 0 ->
      {TX - 1, TY - 1};
    {_, _} ->
      {TX, TY}
  end.

move_tails(_, []) ->
  [];
move_tails(Head, [Tail | Rest]) ->
  NewTail = move_tail(Head, Tail),
  [NewTail | move_tails(NewTail, Rest)].
2 Likes

Hi! I used a similar approach @danilagamma , but used a cleaner tail calculation function (calculate_tail) and used tail recursion in update_tail

-module(solver).

-export([main/0]).
-export([test/0]).

-define(START, {0,0}).

test() -> 
    {0,0} = calculate_tail(?START, {0,0}),
    {0,0} = calculate_tail(?START, {1,0}),
    {1,0} = calculate_tail(?START, {2,0}),
    {0,0} = calculate_tail(?START, {1,1}),
    {1,1} = calculate_tail(?START, {2,1}),
    {-1,-1} = calculate_tail(?START, {-2,-1}),
    TestInput = <<"R 4\nU 4\nL 3\nD 1\nR 4\nD 1\nL 5\nR 2">>,
    Moves = load(TestInput),
    13 = solve1(Moves),
    1 = solve2(Moves),
    ok.

main() ->
    {ok, Data} = file:read_file(input),
    Moves = load(Data),
    {solve1(Moves), solve2(Moves)}.

load(Data0) ->
    [
     begin
         [Motion, Times] = binary:split(Row, <<" ">>, [global, trim]),
         {Motion, list_to_integer(binary:bin_to_list(Times))}
     end
     || Row <- binary:split(Data0, <<"\n">>, [global, trim])
    ].

solve1(Moves) -> 
    Visited = visited(2, Moves),
    sets:size(Visited).

solve2(Moves) ->
    Visited = visited(10, Moves),
    sets:size(Visited).

visited(KnotsNum, Moves) ->
    {_, Visited} = 
    lists:foldl(fun(Move, {Knots, VisitedAcc}) ->
                        move_knots(Move, {Knots, VisitedAcc})
                end, 
                {lists:duplicate(KnotsNum, ?START), sets:add_element({0,0}, sets:new())},
                Moves),
    Visited.

move_knots({_, 0}, Acc) -> Acc;
move_knots({Dir, Times}, {[Head | Tail] = _Knots, VisitedAcc}) ->
    NewFirst = move_head(Dir, Head),
    NewKnots = update_tail(Tail, [NewFirst]),
    NewLastPos = lists:last(NewKnots),
    NewVisitedAcc = sets:add_element(NewLastPos, VisitedAcc),
    move_knots({Dir, Times - 1}, {NewKnots, NewVisitedAcc}).

update_tail([], Acc) -> lists:reverse(Acc);
update_tail([TailHead | Tail], [Head | _] = Acc) ->
    NewTailHead = calculate_tail(TailHead, Head),
    update_tail(Tail, [NewTailHead | Acc]).

move_head(<<"U">>, {X, Y}) -> {X, Y + 1};
move_head(<<"D">>, {X, Y}) -> {X, Y - 1};
move_head(<<"L">>, {X, Y}) -> {X - 1, Y};
move_head(<<"R">>, {X, Y}) -> {X + 1, Y}.

calculate_tail({TX, Same}, {HX, Same}) when abs(HX - TX) > 1 -> {TX + dir(HX, TX), Same};
calculate_tail({Same, TY}, {Same, HY}) when abs(HY - TY) > 1 -> {Same, TY + dir(HY, TY)};
calculate_tail({TX, TY}, {HX, HY}) when abs(HX - TX) + abs(HY - TY) > 2 -> {TX + dir(HX, TX), TY + dir(HY, TY)};
calculate_tail(TPos, _) -> TPos.

dir(0, 0) -> 0;
dir(H, T) -> (H - T) div abs(H - T).
2 Likes