; --Then for each neighbor...Loop Parse, neighbors, `n{ pC :=InStr(A_LoopField,","), x2 :=SubStr(A_LoopField,1, pC-1), y2 :=SubStr(A_LoopField, pC+1); If it has not been visited...If GetChar(S,2*x2,2*y2)="0"{; Mark it as visited... S := ChangeChar(s,2*x2,2*y2," "); Remove the wall between this cell and the neighbor... S := ChangeChar(S, x+x2, y+y2," "); Then recurse with the neighbor as the current cell S := Walk(S, x2, y2)}}return S}

Dimensions are specified by the first two values pushed onto the stack - currently 20 (45*) by 16 (28*). Note, however, that the upper limit in a standard Befunge-93 implementation will be around 38 by 40 (1520 cells) due to the constrained page size.

Also note that this requires an interpreter with working read-write memory support, which is suprisingly rare in online implementations. Padding the code page with extra blank lines or spaces can sometimes help. Using smaller dimensions might also be preferable, especially on slower implementations.

The remove-wall function has been written so as to be as close as possible to the specification. The walls are made from a single unicode character, specified by the block keyword, e. g. (maze 20 6 :block #\X). The BOX_DRAWINGS_LIGHT_DIAGONAL_CROSS character is used by default.

Erlang is single assignment. To get mutability I use processes. The code is over-enginered for this task, but the extra is used for Maze_solving. Also, Erlang starts counting at 1, not 0, so the co-ordinate of the lower left corner is 1,1.

This algorithm allows almost no parallelism. So, while it might be "simple", generating very large mazes this way will not be necessarily efficient to implement on future (highly parallel) systems. That said, perhaps mazes with millions of cells are not very likely to be needed to be generated quickly.

The result of maze is a pair of arrays: one for open "doors" in the horizontal direction and the other for open "doors" in the vertical direction. The entry and exit doors are not represented by maze -- they are implicitly defined and are implemented in display. (The sequences of coordinates in display are the relative coordinates for the doors. For example, 2 1;2 2;2 3 are where we put spaces for each vertical door. The variable text is an ascii representation of the maze grid before the doors are placed.)

unvisited — two dimensional array of locations that have not been visited, padded to avoid need for boundary tests (true means location needs to be visited)

potential — locations adjacent to here

neighbors — unvisited locations adjacent to here

Variable meanings in function display:

m — maze to be drawn

text — lines of text representing maze

line — characters of current line

Note that this implementation relies on javascript arrays being treatable as infinite in size with false (null) values springing into existence as needed, to support referenced array locations. (This significantly reduces the bulk of the necessary initialization code.)

For an animated presentation of the progress of this maze creation process, you can use display in each iteration of the main loop. You would also need to take steps to make sure you could see each intermediate result.

check:Procedure(i,j,n);/********************************************************************** * check if point (i,j) is free and note it as possible successor **********************************************************************/Dcl i BinFixed(31);Dcl j BinFixed(31);Dcl n BinFixed(31);If p(i,j)='.'ThenDo;/* 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 BinFixed(31);Dcl ii BinFixed(31);Dcl jj BinFixed(31);Dcl ai BinFixed(31);Dcl aj BinFixed(31);Dcl pii BinFixed(31)Init((ii));Dcl pjj BinFixed(31)Init((jj));Dcl z BinFixed(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;

Procedure generateMaze(Array maze(2), mazeWidth, mazeHeight)Dim maze(mazeWidth, mazeHeight);Each cell specifies walls present above and to the left of it,;array includes an extra row and column for the right and bottom wallsDim visited(mazeWidth +1, mazeHeight +1);Each cell represents a cell of the maze, an extra line of cells are included;as padding around the representative cells for easy border detection

The maze is represented by an array of cells where each cell indicates the walls present above (#dir_N) and to its left (#dir_W). Maze generation is done with a additional array marking the visited cells. Neither an entry nor an exit are created, these were not part of the task. A simple means of doing so is included but has been commented out.

select_next: ProcedureExpose p. pl. imax jmax/********************************************************************** look for a point to start the nnext path*********************************************************************/DoUntil neighbors>0/* loop until one is found */ n=pl.0 /* number of points recorded */ s=random(1,n)/* pick a random index */ParseVar pl.s is js /* its coordinates */ neighbors=neighbors(is,js)/* count free neighbors */If neighbors=0ThenDo/* if there is none */ pl.s=pl.n /* remove this point */ pl.0=pl.0-1EndEndReturn is js /* return the new start point*/

# Which walls do exist? Default to "true". Both arrays are# one element bigger than they need to be. For example, the# @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(width){Array.new(height)}

# 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)endend

# 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[x1][[y1, y2].min] = falseelse# Cells must be next to each other, remove a vertical wall.@vertical_walls[[x1, x2].min][y1] = falseendendend

The following is a complete, self-contained command line utility. We also drop use of the TXR pattern extraction language and work purely in TXR Lisp.

The algorithm is quite different from the previous. This version is not recursive. This algorithm divides the maze cells into visited cells, frontier cells and unvisited cells. As in the DFS version, border cells outside of the maze area are pre-initialized as visited, for convenience. The frontier set initially contains the upper left hand corner.

The algorithm's main loop iterates while there are frontier cells. As the generation progresses, unvisited cells adjacent to frontier cells added to the frontier set. Frontier cells that are only surrounded by other frontier cells or visited cells are removed from the frontier set and become visited cells. Eventually, all unvisited cells become frontier cells and then visited cells, at which point the frontier set becomes empty and the algorithm terminates.

At every step, the algorithm picks the first cell in the frontier list. In the code, the frontier cells are kept in a hash called fr and also in a queue q. The algorithm tries to extend the frontier around the frontier cell which is at the head of the queue q by randomly choosing an adjacent unvisited cell. (If there is no such cell, the node is not a frontier node any more and is popped from the queue and fr set). If an unvisited node is picked, then a two-way path is broken from the given frontier cell to that cell, and that cell is added to the frontier set. Important: the new frontier cell is added to the head of the queue, rather than the tail.

The algorithm is modified by a "straightness" parameter, which is used to initialize a counter. Every time a new frontier node is added to the front of the queue, the counter decrements. When it reaches zero, the frontier queue is scrambled, and the counter is reset. As long as the count is nonzero, the maze growth proceeds from the previously traversed node, because the new node is placed at the head of the queue. This behavior mimics the DFS algorithm, resulting in long corridors without a lot of branching.

At the user interface level, the straightness parameter is represented as a percentage. This percentage is converted to a number of cells based on the width and height of the maze. For instance if the straightness parameter is 15, and the maze size is 20x20, it means that 15% out of 400 cells, or 60 cells will be traversed before the queue is scrambled. Then another 60 will be traversed and the queue will be scrambled, and so forth.