Initial data commit

This commit is contained in:
Ingy döt Net 2023-07-01 11:58:00 -04:00
parent 72d218235f
commit f23f22d71c
199087 changed files with 3378941 additions and 0 deletions

View file

@ -0,0 +1,143 @@
-module( maze ).
-export( [cell_accessible_neighbours/1, cell_content/1, cell_content_set/2, cell_pid/3, cell_position/1, display/1, generation/2, stop/1, task/0] ).
-record( maze, {dict, max_x, max_y, start} ).
-record( state, {content=" ", controller, is_dug=false, max_x, max_y, neighbours=[], position, walls=[north, south, east, west], walk_done} ).
cell_accessible_neighbours( Pid ) -> read( Pid, accessible_neighbours ).
cell_content( Pid ) -> read( Pid, content ).
cell_content_set( Pid, Content ) -> Pid ! {content, Content, erlang:self()}.
cell_pid( X, Y, Maze ) -> dict:fetch( {X, Y}, Maze#maze.dict ).
cell_position( Pid ) -> read( Pid, position ).
display( #maze{dict=Dict, max_x=Max_x, max_y=Max_y} ) ->
Position_pids = dict:to_list( Dict ),
display( Max_x, Max_y, reads(Position_pids, content), reads(Position_pids, walls) ).
generation( Max_x, Max_y ) ->
Controller = erlang:self(),
Position_pids = cells_create( Controller, Max_x, Max_y ),
Pids = [Y || {_X, Y} <- Position_pids],
[X ! {position_pids, Position_pids} || X <- Pids],
{Position, Pid} = lists:nth( random:uniform(Max_x * Max_y), Position_pids ),
Pid ! {dig, Controller},
receive
{dig_done} -> ok
end,
#maze{dict=dict:from_list(Position_pids), max_x=Max_x, max_y=Max_y, start=Position}.
stop( #maze{dict=Dict} ) ->
Controller = erlang:self(),
Pids = [Y || {_X, Y} <- dict:to_list(Dict)],
[X ! {stop, Controller} || X <- Pids],
ok.
task() ->
Maze = generation( 16, 8 ),
io:fwrite( "Starting at ~p~n", [Maze#maze.start] ),
display( Maze ),
stop( Maze ).
cells_create( Controller, Max_x, Max_y ) -> [{{X, Y}, cell_create(Controller, Max_x, Max_y, {X, Y})} || X <- lists:seq(1, Max_x), Y<- lists:seq(1, Max_y)].
cell_create( Controller, Max_x, Max_y, {X, Y} ) -> erlang:spawn_link( fun() -> random:seed( X*1000, Y*1000, (X+Y)*1000 ), loop( #state{controller=Controller, max_x=Max_x, max_y=Max_y, position={X, Y}} ) end ).
display( Max_x, Max_y, Position_contents, Position_walls ) ->
All_rows = [display_row( Max_x, Y, Position_contents, Position_walls ) || Y <- lists:seq(Max_y, 1, -1)],
[io:fwrite("~s+~n~s|~n", [North, West]) || {North, West} <- All_rows],
io:fwrite("~s+~n", [lists:flatten(lists:duplicate(Max_x, display_row_north(true)))] ).
display_row( Max_x, Y, Position_contents, Position_walls ) ->
North_wests = [display_row_walls(proplists:get_value({X,Y}, Position_contents), proplists:get_value({X,Y}, Position_walls)) || X <- lists:seq(1, Max_x)],
North = lists:append( [North || {North, _West} <- North_wests] ),
West = lists:append( [West || {_X, West} <- North_wests] ),
{North, West}.
display_row_walls( Content, Walls ) -> {display_row_north( lists:member(north, Walls) ), display_row_west( lists:member(west, Walls), Content )}.
display_row_north( true ) -> "+---";
display_row_north( false ) -> "+ ".
display_row_west( true, Content ) -> "| " ++ Content ++ " ";
display_row_west( false, Content ) -> " " ++ Content ++ " ".
loop( State ) ->
receive
{accessible_neighbours, Pid} ->
Pid ! {accessible_neighbours, loop_accessible_neighbours( State#state.neighbours, State#state.walls ), erlang:self()},
loop( State );
{content, Pid} ->
Pid ! {content, State#state.content, erlang:self()},
loop( State );
{content, Content, _Pid} ->
loop( State#state{content=Content} );
{dig, Pid} ->
Not_dug_neighbours = loop_not_dug( State#state.neighbours ),
New_walls = loop_dig( Not_dug_neighbours, lists:delete( loop_wall_from_pid(Pid, State#state.neighbours), State#state.walls), Pid ),
loop( State#state{is_dug=true, walls=New_walls, walk_done=Pid} );
{dig_done} ->
Not_dug_neighbours = loop_not_dug( State#state.neighbours ),
New_walls = loop_dig( Not_dug_neighbours, State#state.walls, State#state.walk_done ),
loop( State#state{walls=New_walls} );
{is_dug, Pid} ->
Pid ! {is_dug, State#state.is_dug, erlang:self()},
loop( State );
{position, Pid} ->
Pid ! {position, State#state.position, erlang:self()},
loop( State );
{position_pids, Position_pids} ->
{_My_position, Neighbours} = lists:foldl( fun loop_neighbours/2, {State#state.position, []}, Position_pids ),
erlang:garbage_collect(), % Shrink process after using large Pid_positions. For memory starved systems.
loop( State#state{neighbours=Neighbours} );
{stop, Controller} when Controller =:= State#state.controller ->
ok;
{walls, Pid} ->
Pid ! {walls, State#state.walls, erlang:self()},
loop( State )
end.
loop_accessible_neighbours( Neighbours, Walls ) -> [Pid || {Direction, Pid} <- Neighbours, not lists:member(Direction, Walls)].
loop_dig( [], Walls, Pid ) ->
Pid ! {dig_done},
Walls;
loop_dig( Not_dug_neighbours, Walls, _Pid ) ->
{Dig_pid, Dig_direction} = lists:nth( random:uniform(erlang:length(Not_dug_neighbours)), Not_dug_neighbours ),
Dig_pid ! {dig, erlang:self()},
lists:delete( Dig_direction, Walls ).
loop_neighbours( {{X, Y}, Pid}, {{X, My_y}, Acc} ) when Y =:= My_y + 1 -> {{X, My_y}, [{north, Pid} | Acc]};
loop_neighbours( {{X, Y}, Pid}, {{X, My_y}, Acc} ) when Y =:= My_y - 1 -> {{X, My_y}, [{south, Pid} | Acc]};
loop_neighbours( {{X, Y}, Pid}, {{My_x, Y}, Acc} ) when X =:= My_x + 1 -> {{My_x, Y}, [{east, Pid} | Acc]};
loop_neighbours( {{X, Y}, Pid}, {{My_x, Y}, Acc} ) when X =:= My_x - 1 -> {{My_x, Y}, [{west, Pid} | Acc]};
loop_neighbours( _Position_pid, Acc ) -> Acc.
loop_not_dug( Neighbours ) ->
My_pid = erlang:self(),
[Pid ! {is_dug, My_pid} || {_Direction, Pid} <- Neighbours],
[{Pid, Direction} || {Direction, Pid} <- Neighbours, not read_receive(Pid, is_dug)].
loop_wall_from_pid( Pid, Neighbours ) -> loop_wall_from_pid_result( lists:keyfind(Pid, 2, Neighbours) ).
loop_wall_from_pid_result( {Direction, _Pid} ) -> Direction;
loop_wall_from_pid_result( false ) -> controller.
read( Pid, Key ) ->
Pid ! {Key, erlang:self()},
read_receive( Pid, Key ).
read_receive( Pid, Key ) ->
receive
{Key, Value, Pid} -> Value
end.
reads( Position_pids, Key ) ->
My_pid = erlang:self(),
[Pid ! {Key, My_pid} || {_Position, Pid} <- Position_pids],
[{Position, read_receive(Pid, Key)} || {Position, Pid} <- Position_pids].

View file

@ -0,0 +1,114 @@
-module(maze).
-record(maze, {g, m, n}).
-export([generate_default/0, generate_MxN/2]).
make_maze(M, N) ->
Maze = #maze{g = digraph:new(), m = M, n = N},
lists:foreach(fun(X) -> digraph:add_vertex(Maze#maze.g, X) end, lists:seq(0, M * N - 1)),
Maze.
row_at(V, Maze) -> trunc(V / Maze#maze.n).
col_at(V, Maze) -> V - row_at(V, Maze) * Maze#maze.n.
vertex_at(Row, Col, Maze) -> Cell_Exists = cell_exists(Row, Col, Maze), if Cell_Exists -> Row * Maze#maze.n + Col; true -> -1 end.
cell_exists(Row, Col, Maze) -> (Row >= 0) and (Row < Maze#maze.m) and (Col >= 0) and (Col < Maze#maze.n).
adjacent_cells(V, Maze) -> % ordered: left, up, right, down
adjacent_cell(cell_left, V, Maze)++adjacent_cell(cell_up, V, Maze)++adjacent_cell(cell_right, V, Maze)++adjacent_cell(cell_down, V, Maze).
adjacent_cell(cell_left, V, Maze) -> case (col_at(V, Maze) == 0) of true -> []; _Else -> [V - 1] end;
adjacent_cell(cell_up, V, Maze) -> case (row_at(V, Maze) == 0) of true -> []; _Else -> [V - Maze#maze.n] end;
adjacent_cell(cell_right, V, Maze) -> case (col_at(V, Maze) == Maze#maze.n - 1) of true -> []; _Else -> [V + 1] end;
adjacent_cell(cell_down, V, Maze) -> case (row_at(V, Maze) == Maze#maze.m - 1) of true -> []; _Else -> [V + Maze#maze.n] end.
connect_all(V, Maze) ->
lists:foreach(fun(X) -> digraph:add_edge(Maze#maze.g, V, X) end, adjacent_cells(V, Maze)).
make_maze(M, N, all_connected) ->
Maze = make_maze(M, N),
lists:foreach(fun(X) -> connect_all(X, Maze) end, lists:seq(0, M * N - 1)),
Maze.
maze_parts(Maze) ->
SPR = Maze#maze.n + 1, % slots per row is #columns + 1
NPR = (Maze#maze.m * 2) + 1, % # part rows is #(rows * 2) + 1
[make_part(Maze, trunc(Index/SPR), Index - trunc(Index/SPR) * SPR) || Index <- lists:seq(0, (SPR * NPR) - 1)].
draw_part(Part) ->
case Part of
{pwall, pclosed} -> io:format("+---");
{pwall, popen} -> io:format("+ ");
{pwall, pend} -> io:format("+~n");
{phall, pclosed} -> io:format("| ");
{phall, popen} -> io:format(" ");
{phall, pend} -> io:format("|~n")
end.
has_neighbour(Maze, Row, Col, Direction) ->
V = vertex_at(Row, Col, Maze),
if
V >= 0 ->
Adjacent = adjacent_cell(Direction, V, Maze),
if
length(Adjacent) > 0 ->
Neighbours = digraph:out_neighbours(Maze#maze.g, lists:nth(1, Adjacent)),
lists:member(V, Neighbours);
true -> false
end;
true -> false
end.
make_part(Maze, DoubledRow, Col) ->
if
trunc(DoubledRow/2) * 2 == DoubledRow -> % --- (even row) making a wall above the cell
make_part(Maze, trunc(DoubledRow/2), Col, cell_up, pwall);
true -> % ---otherwise (odd row) making a hall through the cell
make_part(Maze, trunc(DoubledRow/2), Col, cell_left, phall)
end.
make_part(Maze, _, Col, _, Part_Type) when Col == Maze#maze.n -> {Part_Type, pend};
make_part(Maze, Row, Col, Direction, Part_Type) ->
Has_Neighbour = has_neighbour(Maze, Row, Col, Direction),
if
Has_Neighbour -> {Part_Type, popen};
true -> {Part_Type, pclosed}
end.
shuffle([], Acc) -> Acc;
shuffle(List, Acc) ->
Elem = lists:nth(random:uniform(length(List)), List),
shuffle(lists:delete(Elem, List), Acc++[Elem]).
processDepthFirst(Maze) ->
if
Maze#maze.m * Maze#maze.n == 0 -> [{pwall, pend}];
true ->
Visited = array:new([{size, Maze#maze.m * Maze#maze.n},{fixed,true},{default,false}]),
{_, Path} = processDepthFirst(Maze, -1, random:uniform(Maze#maze.m * Maze#maze.n) - 1, {Visited, []}),
Path
end.
processDepthFirst(Maze, Vfrom, V, VandP) ->
{Visited, Path} = VandP,
Was_Visited = array:get(V, Visited),
if
not Was_Visited ->
Walker = fun(X, Acc) -> processDepthFirst(Maze, V, X, Acc) end,
Random_Neighbours = shuffle(digraph:out_neighbours(Maze#maze.g, V), []),
lists:foldl(Walker, {array:set(V, true, Visited), Path++[{Vfrom, V}]}, Random_Neighbours);
true -> VandP
end.
open_wall(_, {-1, _}) -> ok;
open_wall(Maze, {V, V2}) ->
case (V2 > V) of true -> digraph:add_edge(Maze#maze.g, V, V2); _Else -> digraph:add_edge(Maze#maze.g, V2, V) end.
generate_MxN(M, N) ->
Maze = make_maze(M, N),
Matrix = make_maze(M, N, all_connected),
Trail = processDepthFirst(Matrix),
lists:foreach(fun(X) -> open_wall(Maze, X) end, Trail),
Parts = maze_parts(Maze),
lists:foreach(fun(X) -> draw_part(X) end, Parts).
generate_default() ->
generate_MxN(9, 9).