From fe5ef51e903da5e42b2efb8a94f5523828e77afc Mon Sep 17 00:00:00 2001 From: Shanti Chellaram Date: Sun, 25 Dec 2022 23:40:31 +0900 Subject: [PATCH] Day 23-1 --- day23.erl | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 day23.erl diff --git a/day23.erl b/day23.erl new file mode 100644 index 0000000..dd16794 --- /dev/null +++ b/day23.erl @@ -0,0 +1,151 @@ +-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, <>}. + +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, <>) end, + WithRow = MkRow(W, Acc), + mkboard(W, H-1, <>). + +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).