You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
6.1 KiB

-module(day23).
-export([solve/1]).
-compile(export_all).
solve(InputData) ->
{part1(InputData), none}.
part1(Input) ->
State = parse_input(Input),
EndState = step(10, State),
{XMin, XMax, YMin, YMax} = Bounds = get_bounds(EndState),
%io:format("~p~n", [Bounds]),
(XMax - XMin + 1) * (YMax - YMin + 1) - num_elves(State).
parse_input(Input) -> parse_input(Input, 0, 0, 1, {#{}, #{}}).
parse_input(<<>>, _X, _Y, _NumElves, State) ->
State;
parse_input(<<$., Rest/binary>>, X, Y, ElfId, State) ->
parse_input(Rest, X+1, Y, ElfId, State);
parse_input(<<$\n, Rest/binary>>, X, Y, ElfId, State) ->
parse_input(Rest, 0, Y+1, ElfId, State);
parse_input(<<$#, Rest/binary>>, X, Y, ElfId, {ActiveElves, Positions}) ->
parse_input(Rest, X+1, Y, ElfId+1, {ActiveElves#{ElfId => {X, Y}}, Positions#{{X, Y} => ElfId}}).
num_elves({_ActiveElves, Positions}) -> map_size(Positions).
board_put(X, Y, Char, {W, Data}) ->
Index = Y * (W+1) + X,
Before = binary_part(Data, {0, Index}),
After = binary_part(Data, {Index + 1, byte_size(Data) - Index - 1}),
{W, <<Before/bytes, Char, After/bytes>>}.
mkboard(W, H) ->
mkboard(W, H, <<>>).
mkboard(W, 0, Acc) -> {W, Acc};
mkboard(W, H, Acc) ->
MkRow = fun MkRow(0, R) -> R;
MkRow(N, R) -> MkRow(N-1, <<R/binary, $.>>) end,
WithRow = MkRow(W, Acc),
mkboard(W, H-1, <<WithRow/binary, $\n>>).
render({_Active, Positions} = State) ->
{MinX, MaxX, MinY, MaxY} = get_bounds(State),
{_, Data} = maps:fold(fun ({X, Y}, Id, Board) -> board_put(X - MinX, Y - MinY, (Id rem 10) + $0, Board) end,
mkboard(MaxX - MinX + 1, MaxY - MinY + 1),
Positions),
Data.
step(N, State) -> step(0, N, State).
step(N, N, State) -> State;
step(I, N, State) when N - I > 0 -> step(I+1, N, do_step(State, I)).
do_step({ActiveElves, PositionsIn}, Round) ->
%io:format(" Stepping ~n", []),
%io:format("~s~n", [render({ActiveElves, PositionsIn})]),
{PlannedMoves, _Inactive} = plan_moves(ActiveElves, PositionsIn, Round),
maps:fold(fun (NewPos, ElfId, {Active, Positions}) ->
#{ElfId := OldPos} = Active,
Updooted = maps:remove(OldPos, Positions),
{Active#{ElfId := NewPos}, Updooted#{NewPos => ElfId}}
end,
{ActiveElves, PositionsIn},
PlannedMoves).
plan_moves(ActiveElves, Positions, Round) ->
{_Collisions, Moves, Inactive} =
maps:fold(
fun (ElfId, Position, {Collisions, MoveSet, Inactive}) ->
Move = get_move(Position, Positions, Round),
%io:format(" ~p ~p ~n", [ElfId, Position]),
%io:format(" ~p ~p ~n", [ElfId, Move]),
% Todo: only mark this unit as inactive if it exited because it had no neighbors (update the signature of get_move/2)
if
Position == Move ->
{Collisions, MoveSet, Inactive#{ElfId => Position}};
is_map_key(Move, Collisions) ->
#{Move := Elves} = Collisions,
{Collisions#{Move:=[ElfId|Elves]}, MoveSet, Inactive};
is_map_key(Move, MoveSet) ->
#{Move := CollidingElf} = MoveSet,
{Collisions#{Move => [ElfId, CollidingElf]}, maps:remove(Move, MoveSet), Inactive};
true ->
{Collisions, MoveSet#{Move => ElfId}, Inactive}
end
end,
{#{}, #{}, #{}},
ActiveElves),
{Moves, Inactive}.
open(Position, Positions) when is_map_key(Position, Positions) -> 0;
open(_Position, _Positions) -> 1.
% N S W NW SW E NE SE
get_move({ElfX, ElfY}, Positions, Round) ->
[_|Adjacent] = [{X, Y} || X <- [ElfX, ElfX-1, ElfX+1], Y <- [ElfY, ElfY-1, ElfY+1]],
OpenByte= lists:foldl(fun (Pos, Acc) -> (Acc bsl 1) + open(Pos, Positions) end, 0, Adjacent),
%io:format("~.2B~n", [OpenByte]),
case Round rem 4 of
0 ->
if
2#11111111 == OpenByte -> {ElfX, ElfY};
2#10010010 == (OpenByte band 2#10010010) -> {ElfX, ElfY-1};
2#01001001 == (OpenByte band 2#01001001) -> {ElfX, ElfY+1};
2#00111000 == (OpenByte band 2#00111000) -> {ElfX-1, ElfY};
2#00000111 == (OpenByte band 2#00000111) -> {ElfX+1, ElfY};
true -> {ElfX, ElfY}
end;
1 ->
if
2#11111111 == OpenByte -> {ElfX, ElfY};
2#01001001 == (OpenByte band 2#01001001) -> {ElfX, ElfY+1};
2#00111000 == (OpenByte band 2#00111000) -> {ElfX-1, ElfY};
2#00000111 == (OpenByte band 2#00000111) -> {ElfX+1, ElfY};
2#10010010 == (OpenByte band 2#10010010) -> {ElfX, ElfY-1};
true -> {ElfX, ElfY}
end;
2 ->
if
2#11111111 == OpenByte -> {ElfX, ElfY};
2#00111000 == (OpenByte band 2#00111000) -> {ElfX-1, ElfY};
2#00000111 == (OpenByte band 2#00000111) -> {ElfX+1, ElfY};
2#10010010 == (OpenByte band 2#10010010) -> {ElfX, ElfY-1};
2#01001001 == (OpenByte band 2#01001001) -> {ElfX, ElfY+1};
true -> {ElfX, ElfY}
end;
3 ->
if
2#11111111 == OpenByte -> {ElfX, ElfY};
2#00000111 == (OpenByte band 2#00000111) -> {ElfX+1, ElfY};
2#10010010 == (OpenByte band 2#10010010) -> {ElfX, ElfY-1};
2#01001001 == (OpenByte band 2#01001001) -> {ElfX, ElfY+1};
2#00111000 == (OpenByte band 2#00111000) -> {ElfX-1, ElfY};
true -> {ElfX, ElfY}
end
end.
mark_inactive(ElfId, {ActiveElves, Positions}) -> maps:remove(ElfId, ActiveElves).
mark_active(ElfId, ElfPosition, {ActiveElves, Positions}) -> ActiveElves#{ElfId => ElfPosition}.
get_bounds({_ActiveElves, Positions}) ->
maps:fold(fun ({X, Y}, _Value, {MinX, MaxX, MinY, MaxY}) ->
{min(X, MinX), max(X, MaxX), min(Y, MinY), max(Y, MaxY)}
end,
{50000000, 0, 50000000, 0},
Positions).