About Monkey 2 › Forums › Monkey 2 Code Library › Perfect Maze Generation
Tagged: algorithms
This topic contains 3 replies, has 3 voices, and was last updated by
impixi
1 year, 10 months ago.
-
AuthorPosts
-
June 10, 2017 at 2:38 am #8561
The following example generates a “perfect maze” within a 2d bool array using an iterative, depth-first technique. (I sometimes use the approach as a basis for some types of procedural dungeons.)
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233'Perfect Maze Generation Example#Import "<std>"#Import "<mojo>"Using std..Using mojo..Function Main()New AppInstanceNew MyWindowApp.Run()EndClass MyWindow Extends WindowField _maze:Bool[,]Field _cols:Int, _rows:IntField _cellWidth:Int, _cellHeight:IntMethod New(title:String = "Perfect Maze Generation Example", width:Int = 640, height:Int = 480, flags:WindowFlags = Null)Super.New(title, width, height, flags)_cellWidth = 16_cellHeight = 16_cols = Width / _cellWidth_rows = Height / _cellHeight_maze = New Bool[_cols, _rows]GenerateMaze()EndMethod GenerateMaze:Void()GenerateMaze2d(_maze)EndMethod RenderMaze:Void(canvas:Canvas)canvas.Clear(Color.Blue)canvas.Color = Color.YellowFor Local c := 0 Until _colsFor Local r := 0 Until _rowsIf _maze[c, r] Then canvas.DrawRect(c * _cellWidth, r * _cellHeight, _cellWidth, _cellHeight)NextNextEndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()RenderMaze(canvas)canvas.Color = Color.Blackcanvas.DrawText("Left Click to generate new maze.", _cellWidth, 0)canvas.Flush()EndMethod OnKeyEvent(event:KeyEvent) OverrideIf event.Type = EventType.KeyDown And event.Key = Key.Escape Then App.Terminate()EndMethod OnMouseEvent(event:MouseEvent) OverrideIf event.Type = EventType.MouseClick Then GenerateMaze()EndEnd'==========================Struct CellPublicField North:Bool = TrueField East:Bool = TrueField South:Bool = TrueField West:Bool = TrueEnd'Generate a perfect maze within a 2d Bool array using an iterative, depth-first technique.'(Overwrites existing array data.)Function GenerateMaze2d:Void(arr2d:Bool[,])If Not arr2d Then Return'Expecting columns x rows grid format. One wall per cell.Local cols:Int = arr2d.GetSize(0)Local rows:Int = arr2d.GetSize(1)'--- Create a grid of Cell structures with dimensions ~half those of the passed in arr2d.'(Because the maze generation code uses "NESW wall-surrounded" cells rather than the'"block per cell" format of the passed arr2d.)Local cellCols:IntIf (cols Mod 2 <> 0) Then cellCols = Floor(cols / 2) Else cellCols = Floor((cols - 1) / 2)Local cellRows:IntIf (rows Mod 2 <> 0) Then cellRows = Floor(rows / 2) Else cellRows = Floor((rows - 1) / 2)Local cellMaze:= New Cell[cellCols, cellRows]'---'Store the path coordinates in a 1d array.'(Could use a Stack<Vec2i>.)Local totalCells:Int = cellRows * cellColsLocal track := New Vec2i[totalCells] 'track[i].X is column, track[i].Y is rowLocal tri:Int = 0 'track indexLocal curCoord:Vec2iLocal visited:int, i:Int, mazei:Int, nbi:Int, r:Int, c:Int'Initialize the maze-related data structures.For r = 0 Until cellRowsFor c = 0 Until cellColscellMaze[c, r] = New Cell()i = (r * cellCols) + ctrack[i] = New Vec2iNextNext'4 directionsLocal dir:= New Vec2i[4]Local nextCoord := New Vec2i[4]For i = 0 Until dir.Lengthdir[i] = New Vec2inextCoord[i] = New Vec2iNextdir[0].Y = -1 'northdir[1].X = 1 'eastdir[2].Y = 1 'southdir[3].X = -1 'westcurCoord.Y = Floor(Rnd(0, cellRows))curCoord.X = Floor(Rnd(0, cellCols))visited = 1 'counterWhile (visited < totalCells)nbi = 0For i = 0 Until dir.Lengthr = curCoord.Y + dir[i].Yc = curCoord.X + dir[i].XIf (r >= 0) And (r < cellRows) And (c >= 0) And (c < cellCols)If (cellMaze[c, r].North) And (cellMaze[c, r].East) And (cellMaze[c, r].South) And (cellMaze[c, r].West)nextCoord[nbi].Y = rnextCoord[nbi].X = cnbi += 1EndifEndifNextif (nbi >= 1)i = Floor(Rnd(0, nbi))If (nextCoord[i].Y - curCoord.Y) = 0 Thenr = nextCoord[i].YIf nextCoord[i].X > curCoord.X Thenc = curCoord.XcellMaze[c, r].East = Falsec = nextCoord[i].XcellMaze[c, r].West = Falseelsec = curCoord.XcellMaze[c, r].West = Falsec = nextCoord[i].XcellMaze[c, r].East = Falseendifelsec = nextCoord[i].XIf nextCoord[i].Y > curCoord.Y Thenr = curCoord.YcellMaze[c, r].South = Falser = nextCoord[i].YcellMaze[c, r].North = FalseElser = curCoord.YcellMaze[c, r].North = Falser = nextCoord[i].YcellMaze[c, r].South = falseendifEndiftri += 1track[tri].Y = curCoord.Ytrack[tri].X = curCoord.XcurCoord.Y = nextCoord[i].YcurCoord.X = nextCoord[i].Xvisited += 1ElsecurCoord.Y = track[tri].YcurCoord.X = track[tri].Xtri -= 1EndifWend'--- Remap maze cells to 2d Bool array.'True = Wall/Block cell.'False = Empty cell.'Initialise all cells as blocked.For Local r := 0 Until rowsFor Local c := 0 Until colsarr2d[c, r] = TrueNextNextLocal mazer:Int, mazec:IntFor Local r := 0 Until cellRowsmazer = (r * 2) + 1For Local c := 0 Until cellColsmazec = (c * 2) + 1arr2d[mazec, mazer] = False'West/East and North/South overlap, so only need to check one of each.If (Not cellMaze[c, r].West) Then arr2d[mazec - 1, mazer] = FalseIf (Not cellMaze[c, r].North) Then arr2d[mazec, mazer - 1] = FalseNextNext'---EndTheory:
https://en.wikipedia.org/wiki/Maze_generation_algorithm
June 10, 2017 at 3:37 am #8563Yay, code snippets! Thanks for sharing.
June 18, 2017 at 6:18 pm #8809Thanks Impixi, this is the first piece of Monky 2 code I was ever able to compile!
The mazes it makes are not quite complete- they have numerous “dead” ends and there is no single complete path from one side to the other- in other words, there is not one entrance and one exit.
A shame there is only 10 examples of code and over 1800 posts looking for help with Monkey 2.
June 19, 2017 at 12:01 am #8810The mazes it makes are not quite complete- they have numerous “dead” ends and there is no single complete path from one side to the other- in other words, there is not one entrance and one exit.
The code generates “perfect” mazes, meaning every coordinate is reachable from every other coordinate within the maze. There are many types of mazes. See here:
http://www.astrolog.org/labyrnth/algrithm.htm
A shame there is only 10 examples of code and over 1800 posts looking for help with Monkey 2.
The “Monkey 2 Code Library” forum is only a week old. More examples will be posted over time. Personally I have more examples to post when I tidy up my code, but at the moment I’m too busy working on other projects.
-
AuthorPosts
You must be logged in to reply to this topic.