-module(day9). -export([solve/1]). solve(Input) -> %#{visited := Visited} = EndState = fold_moves(Input, fun simulate/2, State), {simulate_rope(Input, 1), simulate_rope(Input, 9)}. make_rope(Length) -> Rope0 = make_head(), Rope1 = add_knots(Rope0, Length), add_trace(Rope1). simulate_rope(Input, Length) -> {RopeFn, RopeState} = make_rope(Length), {Visited, _RopeState} = fold_moves(Input, RopeFn, RopeState), map_size(Visited). make_head() -> { fun(Move, [Pos]) -> [vec_add(Move, Pos)] end, [{0, 0}] }. add_knot({HeadFn, Head0}) -> { fun(Move, [TailPos|HeadState]) -> [HeadPosNew|_] = HeadStateNew = HeadFn(Move, HeadState), [chase_tail(HeadPosNew, TailPos) | HeadStateNew] end, [{0,0}|Head0] }. add_knots(Stream, 0) -> Stream; add_knots(Stream, N) -> add_knots(add_knot(Stream), N-1). add_trace({SimFn, Sim0}) -> { fun(Move, {Visited, SimAccIn}) -> [Pos | _] = SimAccOut = SimFn(Move, SimAccIn), {Visited#{Pos => []}, SimAccOut} end, {#{}, Sim0} }. simulate(Move, #{head := Head, tail := Tail, visited := Visited}) -> NewHead = vec_add(Head, Move), NewTail = chase_tail(NewHead, Tail), NewVisited = Visited#{NewTail => []}, #{head => NewHead, tail => NewTail, visited => NewVisited}. chase_tail(Head, Tail) -> Diff = vec_sub(Head, Tail), case vec_norm(Diff) of Diff -> Tail; Norm -> vec_add(Norm, Tail) end. fold_instructions(<>, Fun, Acc) -> {Count, RestOut} = parse_int(RestIn, 0), Direction = case Letter of $U -> up; $D -> down; $L -> left; $R -> right end, fold_instructions(RestOut, Fun, Fun({Direction, Count}, Acc)); fold_instructions(<<$\n, Rest/binary>>, Fun, Acc) -> fold_instructions(Rest, Fun, Acc); fold_instructions(<<>>, _Fun, Acc) -> Acc. fold_moves(Stream, Fun, Acc) -> element(2, fold_instructions(Stream, fun per_move/2, {Fun, Acc})). per_move({Direction, Count}, {Fun, Acc}) -> {Fun, per_move0(direction_vec(Direction), Count, Fun, Acc)}. per_move0(_Move, 0, _Fun, Acc) -> Acc; per_move0(Move, Count, Fun, Acc) -> per_move0(Move, Count-1, Fun, Fun(Move, Acc)). direction_vec(up) -> {0, 1}; direction_vec(down) -> {0, -1}; direction_vec(left) -> {-1, 0}; direction_vec(right) -> {1, 0}. vec_add({X1, Y1}, {X2, Y2}) -> {X1+X2, Y1+Y2}. vec_sub({X1, Y1}, {X2, Y2}) -> {X1-X2, Y1-Y2}. vec_norm({X, Y}) -> {norm(X), norm(Y)}. norm(X) when X > 0 -> 1; norm(X) when X < 0 -> -1; norm(0) -> 0. parse_int(<>, Acc) when Char >= $0 andalso Char =< $9 -> parse_int(Rest, Acc*10+(Char - $0)); parse_int(Rest, Acc) -> {Acc, Rest}.