[Back to MISC SWAG index] [Back to Main SWAG index] [Original]
{
In the meantime the readers might want to play around with the following
code, that I think I originally picked up in this invaluable conference some
years ago (or it may have been the SWAG -- don't remember really). I've
altered the original code so it can be compiled without any other special
units but my cursorUnit, that comes next.
}
program MazeSolver;
uses Crt, cursorUnit;
{$R-,S-,M 16384, 16384, 16384
Program draws and solves a 23x78 maze.
The algorithm used by Maze is adapted from one given in Chapter 4 of
"The Elements of Programming Style" by B. Kernighan and P.J. Plauger
(McGraw-Hill, 1978)
This version for the IBM PC: Wilbert van Leijen
Written: 16 Sept. 1987
Revised: 19 March 1989
Revised: Jan 15th 1995 by Bj”rn Felten @ 2:203/208
}
const
Title : string[6] = ' Maze ';
Usage : string[38] = ' F1ÄFull speed F2ÄDelay move EscÄQuit ';
MazeX = 77;
MazeY = 22;
type
MazeSquare = (Wall, Path);
MazeArray = array[0..MazeX, 0..MazeY] of MazeSquare;
Direction = (GoUp, GoDown, GoLeft, GoRight);
ScrBuffer = array [0..1999] of word; (* Screen Buffer *)
var
FullSpeed : boolean;
ImageBuffer : pointer;
Maze : MazeArray;
X, Y : integer;
Screen : array [0..7] of ScrBuffer absolute $B800: 0000;
procedure WriteXY (Page, Attrib, X, Y: word; N: String);
function x80p(Y, X: word): word; assembler;
asm
MOV AX,Y
MOV BX,AX
MOV CL,4
SHL BX,CL
MOV CL,6
SHL AX,CL
ADD AX,BX
ADD AX,X
end;
var I: byte;
begin
if N[0] <> #0 then for I := 1 to length(N) do
Screen[Page][X80p(Y,X+pred(I))]:=(Attrib shl 8) + ord(N[I]);
end;
{ Set up a frame around the activities }
procedure Frame;
begin
WriteXY(0, $1F, 37, 0, Title);
WriteXY(0, $17, 41, 24, Usage);
WriteXY(0, $31, 42, 24, 'F1');
WriteXY(0, $31, 56, 24, 'F2');
WriteXY(0, $31, 70, 24, 'Esc')
end;
procedure ShowMaze(X, Y: integer; Show: char);
begin
WriteXY(0, $1B, X+2, Y+1, Show)
end; { ShowMaze }
{ Set up maze }
procedure CreateMaze;
var
X, Y : integer;
MazeAction : Direction;
{ Set a given maze element to be Path or Wall }
procedure SetSquare(X, Y: integer; Val: MazeSquare);
begin
Maze[X, Y] := Val;
case Val of
Path : ShowMaze(X, Y, ' ');
Wall : WriteXY(0, $0F, X+2, Y+1, 'Û')
end
end; { SetSquare }
{ Return a random value of direction }
function RandomDirection : Direction;
begin
case Random(4) of
0 : RandomDirection := GoUp;
1 : RandomDirection := GoDown;
2 : RandomDirection := GoLeft;
3 : RandomDirection := GoRight;
end;
end; { RandomDirection }
{ Return a random element in the maze }
function RandomDig(max : integer) : integer;
begin
RandomDig := 2 * Random(max shr 1-1)+1
end; { RandomDig }
{ Check wether a legal path can be built }
Function LegalPath(x, y : integer;
MazeAction : Direction) : Boolean;
begin
LegalPath := False;
case MazeAction of
GoUp : if y > 2 then
LegalPath := (Maze[x, y-2] = Wall);
GoDown : if y < MazeY-2 then
LegalPath := (Maze[x, y+2] = Wall);
GoLeft : if x > 2 then
LegalPath := (Maze[x-2, y] = Wall);
GoRight : if x < MazeX-2 then
LegalPath := (Maze[x+2, y] = Wall);
end;
end; { LegalPath }
{ Extend path in given direction }
Procedure Buildpath(X, Y : integer;
MazeAction : Direction);
var
Unused : set of Direction;
begin
case MazeAction of
GoUp : begin
SetSquare(X, Y-1, Path);
SetSquare(X, Y-2, Path);
dec(Y, 2)
end;
GoDown : begin
SetSquare(X, Y+1, Path);
SetSquare(X, Y+2, Path);
inc(Y, 2)
end;
GoLeft : begin
SetSquare(X-1, Y, Path);
SetSquare(X-2, Y, Path);
dec(X, 2)
end;
GoRight : begin
SetSquare(X+1, Y, Path);
SetSquare(X+2, Y, Path);
inc(X, 2)
end
end;
Unused := [GoUp..GoRight];
repeat { Check direction for legality }
MazeAction := RandomDirection;
if MazeAction in Unused then { If so, extend in that direction }
begin
Unused := Unused-[MazeAction];
if LegalPath(x, y, MazeAction) then
BuildPath(x, y, MazeAction)
end
until Unused = [] { All legal moves are exhausted }
end; { BuildPath }
{ CreateMaze initially draws a maze that is 'solid rock'.
Then the maze will be 'excavated' by setting the elements of
the maze to path. It keeps digging until all legal paths are
exhausted and, finally, it digs an 'entrance' and 'exit' path
on the boundaries of the maze }
begin
for y := 0 to MazeY do { Setup 'solid rock' }
for x := 0 to MazeX do
SetSquare(x, y, Wall);
y := RandomDig(MazeY); { Starting point }
x := RandomDig(MazeX);
SetSquare(x, y, Path);
repeat { Dig path in maze }
MazeAction := RandomDirection
until LegalPath(x, y, MazeAction);
BuildPath(x, y, MazeAction);
x := RandomDig(MazeX);
SetSquare(x, 0, Path); { Dig entrance }
ShowMaze(x, 0, #25);
x := RandomDig(MazeX);
SetSquare(x, MazeY, Path) { Dig exit }
end; { CreateMaze }
{ Solve the maze }
procedure SolveMaze;
var
Solved : boolean;
x, y : integer;
Tried : array[0..MazeX, 0..MazeY] of boolean;
{ Attempt Maze solution from point in given direction }
function Try(x, y : integer;
MazeAction : Direction) : boolean;
var
Ok : boolean;
{ Draw attempted move on screen }
procedure MoveMaze(MazeAction : Direction);
begin
if not FullSpeed then
Delay(80);
case MazeAction of
GoUp : ShowMaze(x, y, #24);
GoDown : ShowMaze(x, y, #25);
GoLeft : ShowMaze(x, y, #27);
GoRight : ShowMaze(x, y, #26);
end
end; { MoveMaze }
{ Check whether there is a path to the boundary from a given
point in a given direction. It returns True if there exists
a path; otherwise, the Try is False }
begin
Ok := (Maze[x, y] = Path); { If Wall, no solution exist }
if Ok then begin
Tried[x, y] := True; { Set Tried flag }
case MazeAction of
GoUp : Dec(y);
GoDown : Inc(y);
GoLeft : Dec(x);
GoRight : Inc(x);
end;
Ok := (Maze[x, y] = Path) and not Tried[x, y];
if Ok then begin { Consider neighbouring square }
MoveMaze(MazeAction);
Ok := (y <= 0) or (y >= MazeY) or (x <= 0) or (x >= MazeX);
if not Ok then
Ok := Try(x, y, GoLeft);
if not Ok then
Ok := Try(x, y, GoDown);
if not Ok then
Ok := Try(x, y, GoRight);
if not Ok then
Ok := Try(x, y, GoUp);
if not Ok then
ShowMaze(x, y, ' ');
end;
end;
Try := Ok;
end; { Try }
{ SolveMaze looks for a continuous sequence of Path squares from one
point on the boundary of the maze to another }
begin
FillChar(Tried, SizeOf(Tried), False);
Solved := False;
x := 0;
y := 1;
while not Solved and (y < MazeY) do begin
Solved := Try(x, y, GoRight);
inc(y)
end;
x := MazeX;
y := 1;
while not Solved and (y < MazeY) do begin
Solved := Try(x, y, GoLeft);
inc(y)
end;
x := 1;
y := 0;
while not Solved and (x < MazeX) do begin
Solved := Try(x, y, GoDown);
Inc(x)
end;
x := 1;
y := MazeY;
while not Solved and (x < MazeX) do begin
Solved := Try(x, y, GoUp);
Inc(x)
end;
Solved := True;
repeat until KeyPressed
end; { SolveMaze }
procedure Mainline;
const
F1 = #59;
F2 = #60;
var
Ch : char;
begin
repeat
Ch := ReadKey;
if Ch = #0 then Ch := ReadKey;
case Ch of
F1 : begin
CreateMaze;
FullSpeed := True;
SolveMaze
end;
F2 : begin
CreateMaze;
FullSpeed := False;
SolveMaze
end;
end
until Ch = #27
end; { Mainline }
begin
ClrScr;
Frame;
cursorOff;
Randomize;
Mainline;
cursorOn
end. { MazeSolver }
{
From: Lou Duchez Read: Yes Replied: No
Very nice! My algorithm grows walls, but your algorithm digs corridors.
Your algorithm also seems to generate more complicated mazes than mine.
My only concern is that it relies so heavily on recursion; you risk
running out of stack space. Of course, with my algorithm, you allocate
lots of arrays that take up data segment ...
Thanks for posting it!
As I comprehend it, the maze-generating algorithm is like this:
- Draw a field composed entirely of walls.
- Select a random spot in the field to be your very first corridor spot.
- Here is the maze-digging routine:
- (This routine takes two value parameters: the X and Y coordinates of
your current location.)
- If you can randomly select a valid location two units away from those
X / Y coordinates (where "valid locations" are those that currently
are walls and not corridors):
- "Dig a corridor" from the X / Y location to that randomly-
selected location.
- Recursively call this routine; as parameters, pass the X and Y
coordinates of that randomly-selected location. (On the first
pass, use that randomly-selected first corridor spot as the X and
Y coordinates.)
- When the recursion ends, the maze is done.
}
[Back to MISC SWAG index] [Back to Main SWAG index] [Original]