This commit is contained in:
Ingy döt Net 2013-10-27 22:24:23 +00:00
parent 6f050a029e
commit 776bba907c
3887 changed files with 59894 additions and 7280 deletions

View file

@ -11,7 +11,7 @@ void main() {
vis[y][x] = true;
static struct P { immutable uint x, y; } // Will wrap-around.
auto d = [P(x-1, y), P(x, y+1), P(x+1, y), P(x, y-1)];
foreach (p; d.randomCover(unpredictableSeed.Random)) {
foreach (p; d.randomCover) {
if (p.x >= w || p.y >= h || vis[p.y][p.x]) continue;
if (p.x == x) hor[max(y, p.y)][x] = "+ ";
if (p.y == y) ver[y][max(x, p.x)] = " ";

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

@ -1,36 +1,36 @@
function maze(x,y) {
var n=x*y-1;
if (n<0) {alert("illegal maze dimensions");return;}
var horiz=[]; for (var j= 0; j<x+1; j++) horiz[j]= [];
var verti=[]; for (var j= 0; j<y+1; j++) verti[j]= [];
var here= [Math.floor(Math.random()*x), Math.floor(Math.random()*y)];
var path= [here];
var unvisited= [];
for (var j= 0; j<x+2; j++) {
unvisited[j]= [];
var horiz =[]; for (var j= 0; j<x+1; j++) horiz[j]= [],
verti =[]; for (var j= 0; j<y+1; j++) verti[j]= [],
here = [Math.floor(Math.random()*x), Math.floor(Math.random()*y)],
path = [here],
unvisited = [];
for (var j = 0; j<x+2; j++) {
unvisited[j] = [];
for (var k= 0; k<y+1; k++)
unvisited[j].push(j>0 && j<x+1 && k>0 && (j != here[0]+1 || k != here[1]+1));
}
while (0<n) {
var potential= [[here[0]+1, here[1]], [here[0],here[1]+1],
var potential = [[here[0]+1, here[1]], [here[0],here[1]+1],
[here[0]-1, here[1]], [here[0],here[1]-1]];
var neighbors= [];
for (var j= 0; j < 4; j++)
var neighbors = [];
for (var j = 0; j < 4; j++)
if (unvisited[potential[j][0]+1][potential[j][1]+1])
neighbors.push(potential[j]);
if (neighbors.length) {
n= n-1;
n = n-1;
next= neighbors[Math.floor(Math.random()*neighbors.length)];
unvisited[next[0]+1][next[1]+1]= false;
if (next[0] == here[0])
horiz[next[0]][(next[1]+here[1]-1)/2]= true;
else
verti[(next[0]+here[0]-1)/2][next[1]]= true;
path.push(here= next);
path.push(here = next);
} else
here= path.pop();
here = path.pop();
}
return ({x: x, y: y, horiz: horiz, verti: verti});
return {x: x, y: y, horiz: horiz, verti: verti};
}
function display(m) {

View file

@ -0,0 +1,26 @@
function walk(maze, cell, visited = {})
push!(visited, cell)
for neigh in shuffle(neighbors(cell, size(maze)))
if !(neigh in visited)
maze[int((cell+neigh)/2)...] = 0
walk(maze, neigh, visited)
end
end
maze
end
neighbors(c,b,d=2) = filter(check(b),map(m->c+d*m, {[0,1],[-1,0],[0,-1],[1,0]}))
check(bound) = cell -> all([1,1] .<= cell .<= [bound...])
maze(w, h) = walk([i%2|j%2 for i=1:2w+1,j=1:2h+1], 2*[rand(1:w),rand(1:h)])
pprint(maze) = print(mapslices(x-> [join(x)], maze, [2]))
function mprint(maze, wall = CharString("╹╸┛╺┗━┻╻┃┓┫┏┣┳╋"...))
pprint([ maze[i,j] == 0 ? ' ' :
wall[sum(c-> 2.0^.5(3c[1]+c[2]+3),
filter(x -> maze[x...] != 0,
neighbors([i,j],[size(maze)...],1)) .- {[i,j]})]
for i = 1:2:size(maze,1), j = 1:size(maze,2)])
end

View file

@ -1,11 +1,11 @@
MazeGraph[m_, n_] :=
Block[{$RecursionLimit = Infinity, grid = GridGraph[{m, n}],
visited = {}},
Graph[Range[m n], Reap[{AppendTo[visited, #];
Do[
If[FreeQ[visited, neighbor],
Sow[# <-> neighbor]; #0@neighbor], {neighbor,
RandomSample@AdjacencyList[grid, #]}]} &@
RandomChoice@VertexList@grid][[2, 1]],
GraphLayout -> {"GridEmbedding", "Dimension" -> {m, n}}]];
Block[{$RecursionLimit = Infinity, grid = GridGraph[{m, n}],
unvisitedQ}, unvisitedQ[_] := True;
Graph[Range[m n], Reap[{unvisitedQ[#] = False;
Do[
If[unvisitedQ[neighbor],
Sow[# <-> neighbor]; #0@neighbor], {neighbor,
RandomSample@AdjacencyList[grid, #]}]} &@
RandomChoice@VertexList@grid][[2, 1]],
GraphLayout -> {"GridEmbedding", "Dimension" -> {m, n}}]];
maze = MazeGraph[13, 21]

View file

@ -0,0 +1,217 @@
*process source attributes xref or(!);
mgg: Proc Options(main);
/* REXX ***************************************************************
* 04.09.2013 Walter Pachl translated from REXX version 2
**********************************************************************/
Dcl (MIN,MOD,RANDOM,REPEAT,SUBSTR) Builtin;
Dcl SYSIN STREAM INPUT;
Dcl print Print;
Dcl imax Bin Fixed(31) init(10);
Dcl jmax Bin Fixed(31) init(15);
Dcl seed Bin Fixed(31) init(4711);
Get File(sysin) Data(imax,jmax,seed);
Dcl ii Bin Fixed(31);
Dcl jj Bin Fixed(31);
Dcl id Bin Fixed(31);
Dcl jd Bin Fixed(31);
id=2*imax+1; /* vertical dimension of a.i.j */
jd=2*jmax+1; /* horizontal dimension of a.i.j */
Dcl c Char(2000) Var;
c=repeat('123456789'!!'abcdefghijklmnopqrstuvwxyz'!!
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',20);
Dcl x Bin Float(53);
x=random(seed);
Dcl ps Bin Fixed(31) Init(1); /* first position */
Dcl na Bin Fixed(31) Init(1); /* number of points used */
Dcl si Bin Fixed(31); /* loop to compute paths */
Begin;
Dcl a(id,jd) Bin Fixed(15);
Dcl p(imax,jmax) Char(1);
Dcl 1 pl(imax*jmax),
2 ic Bin Fixed(15),
2 jc Bin Fixed(15);
Dcl 1 np(imax*jmax),
2 ic Bin Fixed(15),
2 jc Bin Fixed(15);
Dcl 1 pos(imax*jmax),
2 ic Bin Fixed(15),
2 jc Bin Fixed(15);
Dcl npl Bin Fixed(31) Init(0);
a=1; /* mark all borders present */
p='.'; /* Initialize all grid points */
ii=rnd(imax); /* find a start position */
jj=rnd(jmax);
Do si=1 To 1000; /* Do Forever - see Leave */
Call path(ii,jj); /* compute a path starting at ii/jj */
If na=imax*jmax Then /* all points used */
Leave; /* we are done */
Call select_next(ii,jj); /* get a new start from a path*/
End;
Call show;
Return;
path: Procedure(ii,jj);
/**********************************************************************
* compute a path starting from point (ii,jj)
**********************************************************************/
Dcl ii Bin Fixed(31);
Dcl jj Bin Fixed(31);
Dcl nb Bin Fixed(31);
Dcl ch Bin Fixed(31);
Dcl pp Bin Fixed(31);
p(ii,jj)='1';
pos.ic(ps)=ii;
pos.jc(ps)=jj;
Do pp=1 to 50; /* compute a path of maximum length 50*/
nb=neighbors(ii,jj); /* number of free neighbors */
Select;
When(nb=1) /* just one */
Call advance((1),ii,jj); /* go for it */
When(nb>0) Do; /* more Than 1 */
ch=rnd(nb); /* choose one possibility */
Call advance(ch,ii,jj); /* and go for that */
End;
Otherwise /* none available */
Leave;
End;
End;
End;
neighbors: Procedure(i,j) Returns(Bin Fixed(31));
/**********************************************************************
* count the number of free neighbors of point (i,j)
**********************************************************************/
Dcl i Bin Fixed(31);
Dcl j Bin Fixed(31);
Dcl in Bin Fixed(31);
Dcl jn Bin Fixed(31);
Dcl nb Bin Fixed(31) Init(0);
in=i-1; If in>0 Then Call check(in,j,nb);
in=i+1; If in<=imax Then Call check(in,j,nb);
jn=j-1; If jn>0 Then Call check(i,jn,nb);
jn=j+1; If jn<=jmax Then Call check(i,jn,nb);
Return(nb);
End;
check: Procedure(i,j,n);
/**********************************************************************
* check if point (i,j) is free and note it as possible successor
**********************************************************************/
Dcl i Bin Fixed(31);
Dcl j Bin Fixed(31);
Dcl n Bin Fixed(31);
If p(i,j)='.' Then Do; /* point is free */
n+=1; /* number of free neighbors */
np.ic(n)=i; /* note it as possible choice */
np.jc(n)=j;
End;
End;
advance: Procedure(ch,ii,jj);
/**********************************************************************
* move to the next point of the current path
**********************************************************************/
Dcl ch Bin Fixed(31);
Dcl ii Bin Fixed(31);
Dcl jj Bin Fixed(31);
Dcl ai Bin Fixed(31);
Dcl aj Bin Fixed(31);
Dcl pii Bin Fixed(31) Init((ii));
Dcl pjj Bin Fixed(31) Init((jj));
Dcl z Bin Fixed(31);
ii=np.ic(ch);
jj=np.jc(ch);
ps+=1; /* position number */
pos.ic(ps)=ii; /* note its coordinates */
pos.jc(ps)=jj;
p(ii,jj)=substr(c,ps,1); /* mark the point as used */
ai=pii+ii; /* vertical border position */
aj=pjj+jj; /* horizontal border position */
a(ai,aj)=0; /* tear the border down */
na+=1; /* number of used positions */
z=npl+1; /* add the point to the list */
pl.ic(z)=ii; /* of follow-up start pos. */
pl.jc(z)=jj;
npl=z;
End;
show: Procedure;
/*********************************************************************
* Show the resulting maze
*********************************************************************/
Dcl i Bin Fixed(31);
Dcl j Bin Fixed(31);
Dcl ol Char(300) Var;
Put File(print) Edit('mgg',imax,jmax,seed)(Skip,a,3(f(4)));
Put File(print) Skip Data(na);
Do i=1 To id;
ol='';
Do j=1 To jd;
If mod(i,2)=1 Then Do; /* odd lines */
If a(i,j)=1 Then Do; /* border to be drawn */
If mod(j,2)=0 Then
ol=ol!!'---'; /* draw the border */
Else
ol=ol!!'+';
End;
Else Do; /* border was torn down */
If mod(j,2)=0 Then
ol=ol!!' '; /* blanks instead of border */
Else
ol=ol!!'+';
End;
End;
Else Do; /* even line */
If a(i,j)=1 Then Do;
If mod(j,2)=0 Then /* even column */
ol=ol!!' '; /* moving space */
Else /* odd column */
ol=ol!!'!'; /* draw the border */
End;
Else /* border was torn down */
ol=ol!!' '; /* blank instead of border */
End;
End;
Select;
When(i=6) substr(ol,11,1)='A';
When(i=8) substr(ol, 3,1)='B';
Otherwise;
End;
Put File(print) Edit(ol,i)(Skip,a,f(3));
End;
End;
select_next: Procedure(is,js);
/**********************************************************************
* look for a point to start the nnext path
**********************************************************************/
Dcl is Bin Fixed(31);
Dcl js Bin Fixed(31);
Dcl n Bin Fixed(31);
Dcl nb Bin Fixed(31);
Dcl s Bin Fixed(31);
Do Until(nb>0); /* loop until one is found */
n=npl; /* number of points recorded */
s=rnd(n); /* pick a random index */
is=pl.ic(s); /* its coordinates */
js=pl.jc(s);
nb=neighbors(is,js); /* count free neighbors */
If nb=0 Then Do; /* if there is none */
pl.ic(s)=pl.ic(n); /* remove this point */
pl.jc(s)=pl.jc(n);
npl-=1;
End;
End;
End;
rnd: Proc(n) Returns(Bin Fixed(31));
/*********************************************************************
* return a pseudo-random integer between 1 and n
*********************************************************************/
dcl (r,n) Bin Fixed(31);
r=min(random()*n+1,n);
Return(r);
End;
End;
End;

View file

@ -0,0 +1,102 @@
/*REXX program generates and displays a (rectangular) solvable maze. */
height=0; @.=0 /*default for all cells visited.*/
parse arg rows cols seed . /*allow user to specify maze size*/
if rows='' | rows==',' then rows=19 /*No rows given? Use the default*/
if cols='' | cols==',' then cols=19 /*No cols given? Use the default*/
if seed\=='' then call random ,,seed /*use a seed for repeatability. */
call buildRow ''copies('~',cols-1)'~' /*build the top edge of maze.*/
/*(below) build the maze's grid.*/
do r=1 for rows; _=; __=; hp= '|'; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj'~'; hj=''; hp=''
end /*c*/
call buildRow _'' /*build right edge of cells.*/
if r\==rows then call buildRow __'' /* " " " " maze.*/
end /*r*/
call buildRow ''copies('~',cols-1)'~' /*build the bottom maze edge.*/
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose 1st cell*/
/* [↓] traipse through the maze.*/
do forever; n=hood(r!,c!); if n==0 then if \fcell() then leave
call ?; @._r._c=0 /*get the (next) direction to go.*/
ro=r!; co=c!; r!=_r; c!=_c /*save original cell coordinates.*/
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the row and cell directions*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next row and col.*/
@.rw.cw='·' /*mark the cell as being visited.*/
end /*forever*/
do r=1 for height; _= /*display the maze. */
do c=1 for cols*2 + 1; _=_ || @.r.c; end /*c*/
if \(r//2) then _=translate(_, '\', "·") /*trans to backslash*/
@.r=_ /*save the row in @.*/
end /*r*/
do #=1 for height; _=@.# /*display maze to the terminal. */
call makeNice /*make some cell corners prettier*/
_=changestr(1,_,111) /*these four ────────────────────*/
_=changestr(0,_,000) /*─── statements are ────────────*/
_=changestr('·',_," ") /*──────── used for preserving ──*/
_=changestr('~',_,"───") /*──────────── the aspect ratio. */
say translate(_, '', "═|\10") /*make it presentable for screen.*/
end /*#*/
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────@ subroutine────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a cell.*/
/*──────────────────────────────────? subroutine────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr=+2 /* east*/
if ?==3 then ?.zc=+2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*──────────────────────────────────BUILDROW subroutine─────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*──────────────────────────────────FCELL subroutine────────────────────*/
fcell: do r=1 for rows; r2=r+r
do c=1 for cols; c2=c+c
if hood(r2,c2)==1 then do; r!=r2; c!=c2; @.r!.c!=0; return 1;end
end /*c*/
end /*r*/
return 0
/*──────────────────────────────────HOOD subroutine─────────────────────*/
hood: parse arg rh,ch; return @(rh+2,ch)+@(rh-2,ch)+@(rh,ch-2)+@(rh,ch+2)
/*──────────────────────────────────MAKENICE subroutine─────────────────*/
makeNice: width=length(_); old=#-1; new=#+1; old_=@.old; new_=@.new
if left(_,2) =='·' then _=translate(_, '|', "")
if right(_,2)=='·' then _=translate(_, '|', "")
/* [↓] handle the top grid row.*/
do k=1 for width while #==1; z=substr(_,k,1) /*maze top row.*/
if z\=='' then iterate
if substr(new_,k,1)=='\' then _=overlay('',_,k)
end /*k*/
do k=1 for width while #==height; z=substr(_,k,1) /*maze bot row.*/
if z\=='' then iterate
if substr(old_,k,1)=='\' then _=overlay('',_,k)
end /*k*/
/* [↓] handle the mid grid rows*/
do k=3 to width-2 by 2 while #//2; z=substr(_,k,1) /*maze mid rows*/
if z\=='' then iterate
le=substr(_,k-1,1)
ri=substr(_,k+1,1)
up=substr(old_,k,1)
dw=substr(new_,k,1)
select
when le=='·' & ri=='·' & up=='' & dw=='' then _=overlay('|',_,k)
when le=='~' & ri=='~' & up=='\' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri=='~' & up=='\' & dw=='' then _=overlay('',_,k)
when le=='~' & ri=='~' & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri=='·' & up=='\' & dw=='\' then _=overlay('',_,k)
when le=='·' & ri=='~' & up=='\' & dw=='\' then _=overlay('',_,k)
when le=='·' & ri=='·' & up=='' & dw=='\' then _=overlay('|',_,k)
when le=='·' & ri=='·' & up=='\' & dw=='' then _=overlay('|',_,k)
when le=='·' & ri=='~' & up=='\' & dw=='' then _=overlay('',_,k)
when le=='·' & ri=='~' & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri=='·' & up=='\' & dw=='' then _=overlay('',_,k)
when le=='~' & ri=='·' & up=='' & dw=='\' then _=overlay('',_,k)
when le=='~' & ri=='·' & up=='' & dw=='' then _=overlay('',_,k)
when le=='·' & ri=='~' & up=='' & dw=='' then _=overlay('',_,k)
otherwise nop
end /*select*/
end /*k*/
return

View file

@ -0,0 +1,57 @@
/*REXX program generates and displays a (rectangular) solvable maze. */
height=0; @.=0 /*default for all cells visited.*/
parse arg rows cols seed . /*allow user to specify maze size*/
if rows='' | rows==',' then rows=19 /*No rows given? Use the default*/
if cols='' | cols==',' then cols=19 /*No cols given? Use the default*/
if seed\=='' then call random ,,seed /*use a seed for repeatability. */
call buildRow ''copies('',cols-1)'' /*build the top edge of maze.*/
/*(below) build the maze's grid.*/
do r=1 for rows; _=; __=; hp= '|'; hj=''
do c=1 for cols; _= _||hp'1'; __=__||hj''; hj=''; hp=''
end /*c*/
call buildRow _'' /*build right edge of cells.*/
if r\==rows then call buildRow __'' /* " " " " maze.*/
end /*r*/
call buildRow ''copies('',cols-1)'' /*build the bottom maze edge.*/
r!=random(1,rows)*2; c!=random(1,cols)*2; @.r!.c!=0 /*choose 1st cell*/
/* [↓] traipse through the maze.*/
do forever; n=hood(r!,c!); if n==0 then if \fcell() then leave
call ?; @._r._c=0 /*get the (next) direction to go.*/
ro=r!; co=c!; r!=_r; c!=_c /*save original cell coordinates.*/
?.zr=?.zr%2; ?.zc=?.zc%2 /*get the row and cell directions*/
rw=ro+?.zr; cw=co+?.zc /*calculate the next row and col.*/
@.rw.cw='·' /*mark the cell as being visited.*/
end /*forever*/
do r=1 for height; _= /*display the maze. */
do c=1 for cols*2 + 1; _=_ || @.r.c; end /*c*/
if \(r//2) then _=translate(_, '\', "·") /*trans to backslash*/
_=changestr(1,_,111) /*these four ────────────────────*/
_=changestr(0,_,000) /*─── statements are ────────────*/
_=changestr('·',_," ") /*──────── used for preserving ──*/
_=changestr('',_,"───") /*──────────── the aspect ratio. */
say translate(_,'',"|\10") /*make it presentable for screen.*/
end /*r*/
exit /*stick a fork in it, we're done.*/
/*──────────────────────────────────@ subroutine────────────────────────*/
@: parse arg _r,_c; return @._r._c /*a fast way to reference a cell.*/
/*──────────────────────────────────? subroutine────────────────────────*/
?: do forever; ?.=0; ?=random(1,4); if ?==1 then ?.zc=-2 /*north*/
if ?==2 then ?.zr=+2 /* east*/
if ?==3 then ?.zc=+2 /*south*/
if ?==4 then ?.zr=-2 /* west*/
_r=r!+?.zr; _c=c!+?.zc; if @._r._c==1 then return
end /*forever*/
/*──────────────────────────────────BUILDROW subroutine─────────────────*/
buildRow: parse arg z; height=height+1; width=length(z)
do c=1 for width; @.height.c=substr(z,c,1); end; return
/*──────────────────────────────────FCELL subroutine────────────────────*/
fcell: do r=1 for rows; r2=r+r
do c=1 for cols; c2=c+c
if hood(r2,c2)==1 then do; r!=r2; c!=c2; @.r!.c!=0; return 1;end
end /*c*/
end /*r*/
return 0
/*──────────────────────────────────HOOD subroutine─────────────────────*/
hood: parse arg rh,ch; return @(rh+2,ch)+@(rh-2,ch)+@(rh,ch-2)+@(rh,ch+2)

View file

@ -0,0 +1,168 @@
/* REXX ***************************************************************
* 04.09.2013 Walter Pachl
**********************************************************************/
Parse Arg imax jmax seed
If imax='' Then imax=10
If jmax='' Then jmax=15
If seed='' Then seed=4711
c='123456789'||,
'abcdefghijklmnopqrstuvwxyz'||,
translate('abcdefghijklmnopqrstuvwxyz')
c=copies(c,10)
call random 1,10,seed
id=2*imax+1 /* vertical dimension of a.i.j */
jd=2*jmax+1 /* horizontal dimension of a.i.j */
a.=1 /* mark all borders present */
p.='.' /* Initialize all grid points */
pl.=0 /* path list */
ii=random(1,imax) /* find a start position */
jj=random(1,jmax)
p=1 /* first position */
na=1 /* number of points used */
Do si=1 To 1000 /* Do Forever - see Leave */
/* Say 'loop' si na show progress */
Call path ii,jj /* compute a path starting at ii/jj */
If na=imax*jmax Then /* all points used */
Leave /* we are done */
Parse Value select_next() With ii jj /* get a new start from a path*/
End
/***************
Do i=1 To imax
ol=''
Do j=1 To jmax
ol=ol||p.i.j
End
Say ol
End
Say ' '
***************/
Call show
/***********************
Do pi=1 To imax*jmax
Say right(pi,3) pos.pi
End
***********************/
Exit
path: Procedure Expose p. np. p pl. c a. na imax jmax id jd pos.
/**********************************************************************
* compute a path starting from point (ii,jj)
**********************************************************************/
Parse Arg ii,jj
p.ii.jj='1'
pos.p=ii jj
Do pp=1 to 50 /* compute a path of maximum length 50*/
neighbors=neighbors(ii,jj) /* number of free neighbors */
Select
When neighbors=1 Then /* just one */
Call advance 1,ii,jj /* go for it */
When neighbors>0 Then Do /* more Than 1 */
ch=random(1,neighbors) /* choose one possibility */
Call advance ch,ii,jj /* and go for that */
End
Otherwise /* none available */
Leave
End
End
Return
neighbors: Procedure Expose p. np. imax jmax neighbors pl.
/**********************************************************************
* count the number of free neighbors of point (i,j)
**********************************************************************/
Parse Arg i,j
neighbors=0
in=i-1; If in>0 Then Call check in,j
in=i+1; If in<=imax Then Call check in,j
jn=j-1; If jn>0 Then Call check i,jn
jn=j+1; If jn<=jmax Then Call check i,jn
Return neighbors
check: Procedure Expose p. imax jmax np. neighbors pl.
/**********************************************************************
* check if point (i,j) is free and note it as possible successor
**********************************************************************/
Parse Arg i,j
If p.i.j='.' Then Do /* point is free */
neighbors=neighbors+1 /* number of free neighbors */
np.neighbors=i j /* note it as possible choice */
End
Return
advance: Procedure Expose p pos. np. p. c ii jj a. na pl. pos.
/**********************************************************************
* move to the next point of the current path
**********************************************************************/
Parse Arg ch,pii,pjj
Parse Var np.ch ii jj
p=p+1 /* position number */
pos.p=ii jj /* note its coordinates */
p.ii.jj=substr(c,p,1) /* mark the point as used */
ai=pii+ii /* vertical border position */
aj=pjj+jj /* horizontal border position */
a.ai.aj=0 /* tear the border down */
na=na+1 /* number of used positions */
z=pl.0+1 /* add the point to the list */
pl.z=ii jj /* of follow-up start pos. */
pl.0=z
Return
show: Procedure Expose id jd a. na
/*********************************************************************
* Show the resulting maze
*********************************************************************/
say 'mgg 6 18 4711'
say 'show na='na
Do i=1 To id
ol=''
Do j=1 To jd
If i//2=1 Then Do /* odd lines */
If a.i.j=1 Then Do /* border to be drawn */
If j//2=0 Then
ol=ol||'---' /* draw the border */
Else
ol=ol'+'
End
Else Do /* border was torn down */
If j//2=0 Then
ol=ol||' ' /* blanks instead of border */
Else
ol=ol||'+'
End
End
Else Do /* even line */
If a.i.j=1 Then Do
If j//2=0 Then /* even column */
ol=ol||' ' /* moving space */
Else /* odd column */
ol=ol||'|' /* draw the border */
End
Else /* border was torn down */
ol=ol||' ' /* blank instead of border */
End
End
Select
When i=6 Then ol=overlay('A',ol,11)
When i=8 Then ol=overlay('B',ol, 3)
Otherwise Nop
End
Say ol format(i,2)
End
Return
select_next: Procedure Expose p. pl. imax jmax
/*********************************************************************
* look for a point to start the nnext path
*********************************************************************/
Do Until neighbors>0 /* loop until one is found */
n=pl.0 /* number of points recorded */
s=random(1,n) /* pick a random index */
Parse Var pl.s is js /* its coordinates */
neighbors=neighbors(is,js) /* count free neighbors */
If neighbors=0 Then Do /* if there is none */
pl.s=pl.n /* remove this point */
pl.0=pl.0-1
End
End
Return is js /* return the new start point*/

View file

@ -2,27 +2,24 @@ class Maze
DIRECTIONS = [ [1, 0], [-1, 0], [0, 1], [0, -1] ]
def initialize(width, height)
@width = width
@height = height
@width = width
@height = height
@start_x = rand(width)
@start_y = 0
@end_x = rand(width)
@end_y = height - 1
@end_x = rand(width)
@end_y = height - 1
# Which walls do exist? Default to "true". Both arrays are
# one element bigger than they need to be. For example, the
# @vertical_walls[y][x] is true if there is a wall between
# (x,y) and (x+1,y). The additional entry makes printing
# easier.
@vertical_walls = Array.new(height) { Array.new(width, true) }
@horizontal_walls = Array.new(height) { Array.new(width, true) }
# @vertical_walls[x][y] is true if there is a wall between
# (x,y) and (x+1,y). The additional entry makes printing easier.
@vertical_walls = Array.new(width) { Array.new(height, true) }
@horizontal_walls = Array.new(width) { Array.new(height, true) }
# Path for the solved maze.
@path = Array.new(height) { Array.new(width) }
@path = Array.new(width) { Array.new(height) }
# "Hack" to print the exit.
@horizontal_walls[@end_y][@end_x] = false
reset_visiting_state
@horizontal_walls[@end_x][@end_y] = false
# Generate the maze.
generate
@ -31,26 +28,16 @@ class Maze
# Print a nice ASCII maze.
def print
# Special handling: print the top line.
line = "+"
for x in (0...@width)
line.concat(x == @start_x ? " +" : "---+")
end
puts line
puts @width.times.inject("+") {|str, x| str << (x == @start_x ? " +" : "---+")}
# For each cell, print the right and bottom wall, if it exists.
for y in (0...@height)
line = "|"
for x in (0...@width)
line.concat(@path[y][x] ? " o " : " ")
line.concat(@vertical_walls[y][x] ? "|" : " ")
@height.times do |y|
line = @width.times.inject("|") do |str, x|
str << (@path[x][y] ? " * " : " ") << (@vertical_walls[x][y] ? "|" : " ")
end
puts line
line = "+"
for x in (0...@width)
line.concat(@horizontal_walls[y][x] ? "---+" : " +")
end
puts line
puts @width.times.inject("+") {|str, x| str << (@horizontal_walls[x][y] ? "---+" : " +")}
end
end
@ -58,58 +45,47 @@ class Maze
# Reset the VISITED state of all cells.
def reset_visiting_state
@visited = Array.new(@height) { Array.new(@width) }
end
# Check whether the given coordinate is within the valid range.
def coordinate_valid?(x, y)
(x >= 0) && (y >= 0) && (x < @width) && (y < @height)
@visited = Array.new(@width) { Array.new(@height) }
end
# Is the given coordinate valid and the cell not yet visited?
def move_valid?(x, y)
coordinate_valid?(x, y) && !@visited[y][x]
(0...@width).cover?(x) && (0...@height).cover?(y) && !@visited[x][y]
end
# Generate the maze.
def generate
generate_visit_cell @start_x, @start_y
reset_visiting_state
generate_visit_cell(@start_x, @start_y)
end
# Depth-first maze generation.
def generate_visit_cell(x, y)
# Mark cell as visited.
@visited[y][x] = true
@visited[x][y] = true
# Randomly get coordinates of surrounding cells (may be outside
# of the maze range, will be sorted out later).
coordinates = []
for dir in DIRECTIONS.shuffle
coordinates << [ x + dir[0], y + dir[1] ]
end
coordinates = DIRECTIONS.shuffle.map { |dx, dy| [x + dx, y + dy] }
for new_x, new_y in coordinates
next unless move_valid?(new_x, new_y)
# Recurse if it was possible to connect the current
# and the the cell (this recursion is the "depth-first"
# part).
# Recurse if it was possible to connect the current and
# the cell (this recursion is the "depth-first" part).
connect_cells(x, y, new_x, new_y)
generate_visit_cell new_x, new_y
generate_visit_cell(new_x, new_y)
end
end
# Try to connect two cells. Returns whether it was valid to do so.
def connect_cells(x1, y1, x2, y2)
if x1 == x2
# Cells must be above each other, remove a horizontal
# wall.
@horizontal_walls[ [y1, y2].min ][x1] = false
# Cells must be above each other, remove a horizontal wall.
@horizontal_walls[x1][ [y1, y2].min ] = false
else
# Cells must be next to each other, remove a vertical
# wall.
@vertical_walls[y1][ [x1, x2].min ] = false
# Cells must be next to each other, remove a vertical wall.
@vertical_walls[ [x1, x2].min ][y1] = false
end
end
end