Depth-First Search Maze Generation
by TechLord · 10/19/2004 (6:56 pm) · 17 comments
//Depth-First Search Maze Generation TorqueScript
//by Frankie L. Taylor 2004
//Based on DFS Algorithm found http://www.mazeworks.com/mazegen/mazetut/
//DFS Algorithm works like this:
// 1) Start at a random cell in the grid.
// 2) Look for a random neighbor cell you haven't been to yet.
// 3) If you find one, move there, knocking down the wall between the cells.
// If you don't find one, back up to the previous cell.
// 4) Repeat steps 2 and 3 until you've been to every cell in the grid.
//When the while loop terminates, the algorithm is completed. Every cell has been
//visited and thus no cell is inaccessible. Also, since we test each possible move
//to see if we've already been there, the algorithm prevents the creation of any
//open areas, or paths that circle back on themselves.
//We can put the start and end points wherever we want. This is another advantage
//of a perfect maze. Since, by definition, one and only one path will exist between
//any two points in the maze, we know that given a start/end pair, a unique solution
// to the maze must exist.
//Depth-First Search is the most common algorithm used in maze generation programs:
//it's simple to implement, works quickly, and generates very pretty mazes.
//The algorithm can also be used to solve mazes. This is how MazeGen generates
//solutions for all mazes, no matter which algorithm was used to create them.
//-------------------------------------------------------------------------------------
function mazeGenerate(%fileName,%cellMaxX,%cellMaxY){
//Pararmeters: %filename Name of maze file. The *.maz and *.maz.2 are added.
// %cellMaxX Maximum number of horizontal cells.
// %cellMaxY Maximum number or vertical cells.
//Return: nothing
//Description: Generates maze data in two formats. Data used to assemble random
// maze from independent DIF Interiors, DTS Shapes, and Images.
%filename = "starter.fps/data/missions/" @ %filename @ ".maz"; //change directory path as needed.
%cellBaseData = "0000 1000 0100 0010 0001 1100 0110 0011 1001 1110 0111 1011 1101 1010 0101 1111";
//cellBaseID: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
%cellBaseDataPTR = 0;
%cellNeighborData = "0 -1 -1 0 0 1 1 0";
%cellNeighborDataPTR = 0;
%cellBaseWallKnockoutData = "0 2 1 3 2 0 3 1"; //current,neighbor
%cellBaseWallKnockoutDataPTR = 0;
//create a CellStack (LIFO) to hold a list of cell locations
%cellStackX[0] = 0;
%cellStackY[0] = 0;
%cellStackPTR = 0;
//set TotalCells = number of cells in grid
%cellTotal = %cellMaxX*%cellMaxY;
//choose a cell at random and call it CurrentCell
%cellCurrentX = getRandom(%cellMaxX);
%cellCurrentY = getRandom(%cellMaxY);
//create cells: cellBaseWall, cellBaseWallVariance (future use), cellBase, cellBaseVariance(future use)
//Note: cellbase describes the the cell pattern (See Diagram 1). A cellbase contains upto 4 walls.
//Each wall can be independent in shape/texture (variance).
for (%cellX = 0;%cellX < %cellMaxX;%cellX++){
for(%cellY = 0;%cellY < %cellMaxY;%cellY++){
%cellBase[%cellX,%cellY] = 0;
%cellBaseVariance[%cellX,%cellY] = getRandom(15);
for(%loop = 0;%loop < 4;%loop++){
%cellBaseWall[%cellX,%cellY,%loop] = 1; //cellwalls = 0:top, 1:left, 2:bottom, 3:right
//make walls border by variance
if ( %cellX == 0 || %cellX == %cellMaxX-1 || %cellY == 0 || %cellY == %cellMaxY-1){//borders
%cellBaseWallVariance[%cellX,%cellY,%loop] = getRandom(16,24); //Borders are BaseWallVariety 16-24
}
else{//standard walls
%cellBaseWallVariance[%cellX,%cellY,%loop] = getRandom(15); //BaseWallVariety 0-15
}
}
}
}
//set VisitedCells = 1
%cellVisited = 1;
//while VisitedCells < TotalCells
while(%cellVisited < %cellTotal){
//find all neighbors of CurrentCell with all walls intact
%cellNeighborDataPTR = 0;//restore cellNeighborData
%cellNeighborCount = 0;//reset neighborCount (list)
for(%loop = 0;%loop<4;%loop++){
//calculate neighbors
%cellX = getWord(%cellNeighborData,%cellNeighborDataPTR);
%cellY = getWord(%cellNeighborData,%cellNeighborDataPTR+1);
%cellNeighborDataPTR += 2;//increment cellNeighborData
%cellX += %cellCurrentX;
%cellY += %cellCurrentY;
//check if base is within borders
//if so, check if walls are intact add to neighbor list
if( %cellX >= 0 && %cellX < %cellMaxX && %cellY >= 0 && %cellY < %cellMaxY ){
%cellBaseWallsIntact = %cellBaseWall[%cellX,%cellY,0] + %cellBaseWall[%cellX,%cellY,1] + %cellBaseWall[%cellX,%cellY,2] + %cellBaseWall[%cellX,%cellY,3];
if (%cellBaseWallsIntact == 4){
//add to neighbor list
%cellNeighborX[%cellNeighborCount] = %cellX;
%cellNeighborY[%cellNeighborCount] = %cellY;
%cellNeighborPos[%cellNeighborCount] = %loop;
%cellNeighborCount++;
}
}
}
//if one or more neighbor found
if (%cellNeighborCount > 0){
//choose one of the neighbors at random
%cellNeighborRandom = getRandom(%cellNeighborCount-1);
//knock down the wall between it and CurrentCell
%cellBaseWallKnockoutDataPTR = 0;//restore cellBaseWallKnockoutData
for(%loop = 0;%loop<4;%loop++){//check walls
%cellBaseWallKnockoutCurrent = getWord(%cellBaseWallKnockoutData,%cellBaseWallKnockoutDataPTR);
%cellBaseWallKnockoutNeighbor = getWord(%cellBaseWallKnockoutData,%cellBaseWallKnockoutDataPTR+1);
%cellBaseWallKnockoutDataPTR += 2;//increment cellBaseWallKnockoutData
if(%cellNeighborPos[%cellNeighborRandom] == %loop){
%cellBaseWall[%cellCurrentX,%cellCurrentY, %cellBaseWallKnockoutCurrent] = 0;
%cellBaseWall[%cellNeighborX[%cellNeighborRandom],%cellNeighborY[%cellNeighborRandom],%cellBaseWallKnockoutNeighbor] = 0;
%loop = 4;//break for loop
}
}
//push CurrentCell location on the CellStack
%cellStackX[%cellStackPTR] = %cellCurrentX;
%cellStackY[%cellStackPTR] = %cellCurrentY;
%cellStackPTR++;
//make the new cell CurrentCell
%cellCurrentX = %cellNeighborX[%cellNeighborRandom];
%cellCurrentY = %cellNeighborY[%cellNeighborRandom];
//add 1 to VisitedCells
%cellVisited++;
}
else{
//pop the most recent cell entry off the CellStack and make it CurrentCell
%cellStackPTR--;
%cellCurrentX = %cellStackX[%cellStackPTR];
%cellCurrentY = %cellStackY[%cellStackPTR];
}//endIf
} //endWhile
//maze completed, id cellbases
for (%cellX = 0;%cellX < %cellMaxX;%cellX++){
for(%cellY = 0;%cellY < %cellMaxY;%cellY++){
%cellBaseWallSet1 = %cellBaseWall[%cellX,%cellY,0] @ %cellBaseWall[%cellX,%cellY,1] @ %cellBaseWall[%cellX,%cellY,2] @ %cellBaseWall[%cellX,%cellY,3];
%cellBaseDataPTR = 0;//restore cellBaseData
for(%loop = 0;%loop < 16;%loop++){
%cellBaseWallSet2 = getWord(%cellBaseData,%cellBaseDataPTR);
%cellBaseDataPTR++;//increment cellBaseData
if(%cellBaseWallSet2 == %cellBaseWallSet1){
%cellBase[%cellX,%cellY] = %loop;
%loop = 16;//break
}
}
}
}
//write maze data format I
//Note: if variance is not required omit the lines.
%file = new fileObject();
%file.openforWrite(%fileName);
for (%cellX = 0;%cellX < %cellMaxX;%cellX++){
for(%cellY = 0;%cellY < %cellMaxY;%cellY++){
%file.writeLine(%cellBase[%cellX,%cellY]);
%file.writeLine(%cellBaseVariance[%cellX,%cellY]);
for(%loop = 0;%loop < 4;%loop++){
%file.writeLine(%cellBaseWall[%cellX,%cellY,%loop]);
%file.writeLine(%cellBaseWallVariance[%cellX,%cellY,%loop]);
}
}
}
%file.close();
%file.delete();
//write maze data format II (Optional)
%file = new fileObject();
%file.openforWrite(%fileName@".2");
for(%cellY = 0;%cellY < %cellMaxY;%cellY++){
%writetext="";
for (%cellX = 0;%cellX < %cellMaxX;%cellX++){
%writetext = %writetext @ %cellBase[%cellX,%cellY] @ "\t";
}
%file.writeLine(%writetext);
}
%file.close();
%file.delete();
}
Diagram 1. Cellbaseid ( in green ), Format II.
The Result: A Perfect Maze
#2
10/20/2004 (5:23 am)
looks cool, but i dont get it, you dont give much of an explanation, what are the uses for this, it would be really cool if it worked for the terrain in torque
#3
10/22/2004 (4:21 pm)
This is slick brutha. Good job. Very useful info. Thank you.
#4
filebox.vt.edu/users/ehartman/maze1.jpg
filebox.vt.edu/users/ehartman/maze2.jpg
Although, I am having a problem where sometimes a cell will have an id of 15, which I dont think is supposed to happen.
10/23/2004 (6:54 pm)
Hey, This is pretty sweet!filebox.vt.edu/users/ehartman/maze1.jpg
filebox.vt.edu/users/ehartman/maze2.jpg
Although, I am having a problem where sometimes a cell will have an id of 15, which I dont think is supposed to happen.
#5
10/24/2004 (5:03 am)
ok, now i see, that is cool :)
#6
AWESOME WORK!!! The code is not to supposed to generate a cell id of 15.Well, at least it didnt in my test. I reckoned it is possible.
10/26/2004 (5:31 pm)
Eric,
AWESOME WORK!!! The code is not to supposed to generate a cell id of 15.Well, at least it didnt in my test. I reckoned it is possible.
#7
10/29/2004 (8:24 am)
That would be well cool in an FPS shooter game!
#8
I only have one question...just exactly how awesome is this? eh?
10/29/2004 (9:59 am)
we HAVE to use this in the next Game In a Day.I only have one question...just exactly how awesome is this? eh?
#9
10/30/2004 (4:21 am)
Anyone willing to post their dif or dts loading script? I'm trying to utilize this and accomplish the result of the screenshots, but its not quite working. Help?
#10
%cellVisited++;
}
else{
//pop the most recent cell entry off the CellStack and make it CurrentCell
starter.fps/server/scripts/maze1.cs Line: 150 - Syntax error.
>>> Advanced script error report. Line 299.
>>> Some error context, with ## on sides of error halt:
%cellCurrentY = %cellNeighborY[%cellNeighborRandom];
//add 1 to VisitedCells
%cellVisited++;
}
else{##
##
//pop the most recent cell entry off the CellStack and make it CurrentCell
Thanks
11/03/2004 (6:25 pm)
i have gotten all the errors except this one, any idea?%cellVisited++;
}
else{
//pop the most recent cell entry off the CellStack and make it CurrentCell
starter.fps/server/scripts/maze1.cs Line: 150 - Syntax error.
>>> Advanced script error report. Line 299.
>>> Some error context, with ## on sides of error halt:
%cellCurrentY = %cellNeighborY[%cellNeighborRandom];
//add 1 to VisitedCells
%cellVisited++;
}
else{##
##
//pop the most recent cell entry off the CellStack and make it CurrentCell
Thanks
#11
11/04/2004 (1:39 am)
Your error is probably above that, most likely a missing bracket. The code had no errors, other than fixing up the word wrap.
#12
//push CurrentCell location on the CellStack
%cellStackX[%cellStackPTR] = %cellCurrentX;
%cellStackY[%cellStackPTR] = %cellCurrentY;
%cellStackPTR++;
//make the new cell CurrentCell
%cellCurrentX = %cellNeighborX[%cellNeighborRandom];
%cellCurrentY = %cellNeighborY[%cellNeighborRandom];
//add 1 to VisitedCells
%cellVisited++;
}
else
{
11/04/2004 (10:03 am)
Here is the code above that//push CurrentCell location on the CellStack
%cellStackX[%cellStackPTR] = %cellCurrentX;
%cellStackY[%cellStackPTR] = %cellCurrentY;
%cellStackPTR++;
//make the new cell CurrentCell
%cellCurrentX = %cellNeighborX[%cellNeighborRandom];
%cellCurrentY = %cellNeighborY[%cellNeighborRandom];
//add 1 to VisitedCells
%cellVisited++;
}
else
{
#13
I'm still looking for the method of loading dif/dts with this. Anyone? :)
11/04/2004 (11:13 am)
@Wayne Emailing you a compilable file....I'm still looking for the method of loading dif/dts with this. Anyone? :)
#14
Yea it would be great to have the dif/dts shape to try this out with.
11/04/2004 (3:57 pm)
Thanks man ErikYea it would be great to have the dif/dts shape to try this out with.
#15
03/07/2005 (4:25 pm)
Can someone pls post pics and examples of uses for this resource? I think it could be used to generate dungeons with a little work, I'm just not exactly sure how.
#16
Any resources appreciated.
07/11/2005 (6:27 pm)
I would be interested in doing more work in this area for 3d maze generation if I could get more info on what was used in the dif/dts shapes. I tried google on site and found very little information.Any resources appreciated.
#17
07/24/2009 (9:53 am)
Anyone, know how to load them? 
Torque Owner Martin Schultz
Best,
Martin
indies@work