Talking about Maze game, Actionscript 3, Flash and Users contributions.
If you are following the blog for a long time, you should know I love mazes and pathfinding algorithms.
Just to show you a couple of examples, look at the perfect maze generation with AS3, perfect maze generation – tile based version and the basics of pathfinding – animated example.
Today Chevy Ray Johnston shares with us a simple breadth-first search pathfinding system (on a randomly generated maze).
In graph theory, breadth-first search (BFS) is a strategy for searching in a graph when search is limited to essentially two operations: (a) visit and inspect a node of a graph; (b) gain access to visit the nodes that neighbor the currently visited node. The BFS begins at a root node and inspects all the neighboring nodes. Then for each of those neighbor nodes in turn, it inspects their neighbor nodes which were unvisited, and so on. (source: Wikipedia)
Click on 2 black tiles to pathfind between them.
And here is the source code for you to study it:
package { import flash.display.Shape; import flash.geom.Point; import flash.events.MouseEvent; import flash.display.BitmapData; import flash.display.Bitmap; import flash.display.Sprite; //CLICK ON 2 BLACK TILES TO PATHFIND BETWEEN THEM! public class Main extends Sprite { //Maze should always be an odd-number of tiles! (eg. 9x9, 33x15, etc.) public const MAZE_WIDTH:int=31; public const MAZE_HEIGHT:int=23; public const MAZE_SCALE:Number=20; //Directional constants public const ILLEGAL:int=0; public const OPEN:int=1; public const RIGHT:uint=2; public const LEFT:uint=3; public const DOWN:uint=4; public const UP:uint=5; public var closed:BitmapData=new BitmapData(MAZE_WIDTH,MAZE_HEIGHT,false,0); public var data:BitmapData=new BitmapData(MAZE_WIDTH,MAZE_HEIGHT,false,0xFFFFFF); public var bitmap:Bitmap=new Bitmap(data); public var start:Point; public var end:Point; public var pathShape:Shape = new Shape(); public function Main() { x=10; y=10; bitmap.scaleX=bitmap.scaleY=MAZE_SCALE; addChild(bitmap); pathShape.scaleX=pathShape.scaleY=MAZE_SCALE; pathShape.x=pathShape.y=MAZE_SCALE/2; addChild(pathShape); generateMaze(); stage.addEventListener(MouseEvent.CLICK, onClick); } public function onClick(e:MouseEvent):void { start=end; end = new Point(int(mouseX / MAZE_SCALE), int(mouseY / MAZE_SCALE)); if (start != null) { pathShape.graphics.clear(); pathShape.graphics.beginFill(0xFF0000); pathShape.graphics.drawCircle(start.x, start.y, 0.25); pathShape.graphics.drawCircle(end.x, end.y, 0.25); pathShape.graphics.endFill(); pathShape.graphics.lineStyle(0, 0xFF0000); var path:Array=pathfind(start,end); if (path != null && path.length > 1) { pathShape.graphics.moveTo(path[0].x, path[0].y); for (var i:int = 1; i < path.length; i++) { pathShape.graphics.lineTo(path[i].x, path[i].y); } } } } public function pathfind(start:Point, end:Point):Array { if (! getTile(start.x,start.y)&&! getTile(end.x,end.y)) { closed.fillRect(closed.rect, 0xFF000000 | OPEN); setClosed(start.x, start.y, ILLEGAL); var x:int; var y:int; var xQueue:Array=[]; var yQueue:Array=[]; xQueue.push(start.x); yQueue.push(start.y); while (xQueue.length > 0) { x=xQueue.shift(); y=yQueue.shift(); if (x == end.x && y == end.y) { var path:Array=[]; var tile:int=getClosed(x,y); while (tile != ILLEGAL) { path.push(new Point(x, y)); switch (tile) { case RIGHT : x++; break; case LEFT : x--; break; case DOWN : y++; break; case UP : y--; break; } tile=getClosed(x,y); } path.push(new Point(x, y)); return path.reverse(); } else { if (getClosed(x + 1, y) == OPEN && !getTile(x + 1, y)) { setClosed(x + 1, y, LEFT); xQueue.push(x + 1); yQueue.push(y); } if (getClosed(x - 1, y) == OPEN && !getTile(x - 1, y)) { setClosed(x - 1, y, RIGHT); xQueue.push(x - 1); yQueue.push(y); } if (getClosed(x, y + 1) == OPEN && !getTile(x, y + 1)) { setClosed(x, y + 1, UP); xQueue.push(x); yQueue.push(y + 1); } if (getClosed(x, y - 1) == OPEN && !getTile(x, y - 1)) { setClosed(x, y - 1, DOWN); xQueue.push(x); yQueue.push(y - 1); } } } } return null; } public function generateMaze():void { var xStack:Array=[]; var yStack:Array=[]; var sides:Array=[]; data.fillRect(data.rect, 0xFFFFFFFF); //Pick random start point var x:int=1+int(Math.random()*(MAZE_WIDTH/2))*2; var y:int=1+int(Math.random()*(MAZE_HEIGHT/2))*2; xStack.push(x); yStack.push(y); //Maze generation loop while (xStack.length > 0) { x=xStack[xStack.length-1]; y=yStack[yStack.length-1]; sides.length=0; if (getTile(x + 2, y)) { sides.push(RIGHT); } if (getTile(x - 2, y)) { sides.push(LEFT); } if (getTile(x,y+2)) { sides.push(DOWN); } if (getTile(x,y-2)) { sides.push(UP); } if (sides.length>0) { var side:int=sides[int(Math.random()*sides.length)]; switch (side) { case RIGHT : setTile(x + 1, y, false); setTile(x + 2, y, false); xStack.push(x + 2); yStack.push(y); break; case LEFT : setTile(x - 1, y, false); setTile(x - 2, y, false); xStack.push(x - 2); yStack.push(y); break; case DOWN : setTile(x, y + 1, false); setTile(x, y + 2, false); xStack.push(x); yStack.push(y + 2); break; case UP : setTile(x, y - 1, false); setTile(x, y - 2, false); xStack.push(x); yStack.push(y - 2); break; } } else { xStack.pop(); yStack.pop(); } } } public function getTile(x:int, y:int):Boolean { if (x < 0 || y < 0 || x >= MAZE_WIDTH || y >= MAZE_HEIGHT) { return false; } return data.getPixel(x, y) > 0x000000; } public function setTile(x:int, y:int, solid:Boolean):void { data.setPixel(x, y, solid ? 0xFFFFFF : 0x000000); } public function getClosed(x:int, y:int):int { if (x < 0 || y < 0 || x >= MAZE_WIDTH || y >= MAZE_HEIGHT) { return ILLEGAL; } return closed.getPixel(x, y); } public function setClosed(x:int, y:int, value:int):void { closed.setPixel(x, y, value); } } }
You can also download the entire project.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.