Generative labyrinth

// forked from kenbu's 迷路探索 トレモー法
/**
    トレモー法で迷路探索
*/

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.PixelSnapping;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filters.DisplacementMapFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	[SWF(width="465", height="465")]
	public class Labirinth extends Sprite
	{

		private var maze:Maze;
		private var searchMaze:TremauxSearch;
		private var bitmap:Bitmap;

		private const WIDTH:Number = 231;
		private const HEIGHT:Number = 231;
		private const SCALE:Number = 2;

		private var cursor:Sprite;

		private var zoomSpr:Sprite = new Sprite;
		private var zoomBmd:BitmapData = new BitmapData(256,256,false,0x808080);
		private var zoomBmp:Bitmap = new Bitmap(zoomBmd, PixelSnapping.NEVER, true);

		public function Labirinth() {

			stage.frameRate = 60;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			var progressArray:Array = [];
			maze = new DigMaze(WIDTH,HEIGHT,progressArray);

			searchMaze = new TremauxSearch(maze, new Point(1,1), new Point(1, HEIGHT-2), new Array());

			var bitmapData:BitmapData = new BitmapData(WIDTH*SCALE, HEIGHT*SCALE);
			bitmap = new Bitmap(bitmapData);
			addChild(bitmap);

			cursor = new Sprite();
			addChild(cursor);
			var g:Graphics = cursor.graphics;
			g.beginFill(0x5500ff00ff);
			g.drawRect(0,0,SCALE,SCALE);
			g.endFill();

			//地図を描画
			drawMaze();

			this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);

			zoomBmp.x = -zoomBmp.height>>1;
			zoomBmp.y = -zoomBmp.width >>1;
			zoomSpr.addChild(zoomBmp);
			zoomSpr.alpha = 0.1;
			//addChild(zoomSpr);

			for (var yy:uint=0; yy<zoomBmd.height; yy++) {
				for (var xx:uint=0; xx<zoomBmd.width; xx++) {
					var dx:Number = xx-128;
					var dy:Number = yy-128;
					var rr:Number = Math.sqrt(dx*dx+dy*dy);

					if(rr<127) {
						var R:uint = 128-(128-rr)/128*dx;
						var G:uint = 128-(128-rr)/128*dy;
						var B:uint = 128;
						zoomBmd.setPixel(xx, yy,(R<<16) + (G<<8) + (B<<0));
					}

				}
			}

		}

		private var cnt:int = 0;
		private function enterFrameHandler(event:Event):void
		{
			var l:int = 1;
			for(var i:int = 0; i<l; i++) {
				drawSearch();
			}
			zoomSpr.x = cursor.x;
			zoomSpr.y = cursor.y;
			filters = [new DisplacementMapFilter(zoomBmd, new Point(cursor.x-(zoomBmd.width>>1), cursor.y-(zoomBmd.height>>1)),1, 2, 256, 256)]

			if(cnt == maze.buildProgressArray.length)
			{
				this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
			}
		}

		private function drawSearch():void
		{
			if(cnt == searchMaze.buildProgressArray.length)
			{
				return;
			}

			var point:MazePoint = searchMaze.buildProgressArray[cnt];
			var color:Number;
			if(point.value == 2)
			{
				//一回通った
				color = 0x550000ff;
			}
			else
			{
				//2回通った。
				color = 0xff0000ff;
			}
			var scale:Number = 3;

			var bitmapData:BitmapData = bitmap.bitmapData;

			rect.x = point.x * SCALE;
			rect.y = point.y * SCALE;
			rect.width = SCALE;
			rect.height = SCALE;

			bitmapData.fillRect(rect, color);

			cursor.x = point.x * SCALE;
			cursor.y = point.y * SCALE;

			cnt++;

		}

		private var rect:Rectangle = new Rectangle(0,0,0,0);

		private function drawMaze():void
		{
			var bitmapData:BitmapData = bitmap.bitmapData;

			maze.data.forEach(function(p:uint, i:int, arr:Array):void{

				var x:int = i%maze.width;
				var y:int = int(i/maze.height);

				var color:Number;
				if(p == 0)
				{
					color = 0xffffffff;
				}
				else
				{
					color = 0xff000000;
				}
				var scale:Number = 3;

				rect.x = x*SCALE;
				rect.y = y*SCALE;
				rect.width = SCALE;
				rect.height = SCALE;

				bitmapData.fillRect(rect, color);
			});
                           if(!searchMaze)
                            return;

			//ゴールを描画
			rect.x = searchMaze.goalPoint.x * SCALE;
			rect.y = searchMaze.goalPoint.y * SCALE;
			rect.width = SCALE;
			rect.height = SCALE;
			bitmapData.fillRect(rect, 0xffff0000);

		}
	}
}

    class Maze
    { 

        public var width:uint;
        public var height:uint;
        public var data:Array;
        public var buildProgressArray:Array; 

        public function Maze(width:uint, height:uint, buildProgressArray:Array = null)
        {
            this.width = width;
            this.height = height;
            this.buildProgressArray = buildProgressArray;
            initialize();
        } 

        protected function initialize():void
        {
            data = [];
        } 

        protected function setData(x:uint, y:uint, value:uint):void
        {
            //value : 1(壁) , 0(道)
            data[x + width*y] = value; 

            if(buildProgressArray)
            {
                buildProgressArray.push(new MazePoint(x, y, value));
            }
        } 

        public function getData(x:uint, y:uint):uint
        {
            return data[x + width*y];
        } 

    } 

    import flash.geom.Point; 

    class DigMaze extends Maze
    { 

        public function DigMaze(width:uint, height:uint, buildProgressArray:Array = null,density:Number = 1)
        {
            super(width, height, buildProgressArray); 

        } 

        override protected function initialize():void
        {
            super.initialize(); 

            setDefWall();
            setWall(); 

        } 

        private var startPointArray:Array = []; 

        private function setDefWall():void
        {
            //まずすべてを壁でうめる
            for(var y:int = 0; y< height; y++)
            {
                for(var x:int = 0; x< width; x++)
                {
                    setData(x,y,1); 

                    //起点になれる座標配列を作る。
                    if(y==0 || y == height-1 || x==0 || x == width-1)
                    {
                        continue;
                    }
                    if(x%2 == 1 && y%2 == 1)
                    {
                        startPointArray.push(new MazePoint(x,y,1));
                    } 

                }
            } 

            //↓シャッフル・・をさせる。
            //startPointArray. 

        } 

        private function setWall():void
        { 

            startPointArray.forEach(function(p:MazePoint,i:int, arr:Array):void
            { 

                if(p.value != 0)
                {
                    var loop:Boolean = true; 

                    var x:uint = p.x;
                    var y:uint = p.y; 

                    while(loop)
                    {
                        var dir:String = checkRandomDicrectionCreate2Panel(x,y); 

                        if(dir != "")
                        {
                            //塗りつぶし処理をする。と。
                            //setData(x,y,0);
                            switch(dir)
                            {
                                case TOP:
                                    setData(x,y-1,0);
                                    setData(x,y-2,0);
                                    y -= 2;
                                    break;
                                case RIGHT:
                                    setData(x+1,y,0);
                                    setData(x+2,y,0);
                                    x+=2; 

                                    break;
                                case BOTTOM:
                                    setData(x,y+1,0);
                                    setData(x,y+2,0);
                                    y +=2;
                                    break;
                                case LEFT:
                                    setData(x-1,y,0);
                                    setData(x-2,y,0);
                                    x -=2;
                                    break;
                            }
                        }
                        else
                        {
                            loop = false;
                        }
                    }
                } 

            });
        } 

        private static const TOP:String = "top";
        private static const RIGHT:String = "right";
        private static const BOTTOM:String = "bottom";
        private static const LEFT:String = "left"; 

        private function checkRandomDicrectionCreate2Panel(x:uint, y:uint):String
        {
            //生成できる方向をかえす
            var enabledArray:Array = [];
            if(getData(x-1,y) == 1 && getData(x-2,y) == 1 && x!=2)
            {
                //左は動ける。
                enabledArray.push(LEFT);
            }
            if(getData(x,y-1) == 1 && getData(x,y-2) == 1 && y!=2)
            {
                //上に動ける。
                enabledArray.push(TOP);
            }
            if(getData(x+1,y) == 1 && getData(x+2,y) == 1 && x!=width-2)
            {
                //右に動ける。
                enabledArray.push(RIGHT);
            }
            if(getData(x,y+1) == 1 && getData(x,y+2) == 1 && y!=height-2)
            {
                //下に動ける。
                enabledArray.push(BOTTOM);
            } 

            if(enabledArray.length > 0)
            {
                return enabledArray[int(enabledArray.length*Math.random())];
            }
            return "";
        } 

    } 

     class Direction
	{
		public static const TOP:String = "top";
		public static const RIGHT:String = "right";
		public static const BOTTOM:String = "bottom";
		public static const LEFT:String = "left";

	}

    import flash.geom.Point;
    class MazePoint extends Point
    {
        public var value:int;
        public function MazePoint(x:Number,y:Number,value:int)
        {
            super(x,y);
            this.value = value;
        } 

    } 

    //サーチャー
	class TremauxSearcher
	{

		private static const DIRECTIONS:Array = [Direction.TOP, Direction.RIGHT, Direction.BOTTOM, Direction.LEFT];

		public var direction:int;
		private var maze:Maze;

		public function TremauxSearcher(direction:uint, maze:Maze)
		{
			this.direction = direction;
			this.maze = maze;
			x = 1;
			y = 1;
		}

		//セッタ
		private var _x:uint;
		public function set x(value:uint):void
		{
			_oldX = _x;
			_x = value;
		}

		public function get x():uint
		{
			return _x;
		}

		private var _oldX:uint;
		public function get oldX():uint
		{
			return _oldX;
		}

		private var _y:uint;
		public function set y(value:uint):void
		{
			_oldY = _y;
			_y = value;
		}

		private var _oldY:uint;
		public function get oldY():uint
		{
			return _oldY;
		}

		public function get y():uint
		{
			return _y;
		}

		public function forword():void
		{
			//前方が道か調べる。

			if(isForwordWall(x,y))
			{
				//前が壁の場合。
				//右を向く
				turnRight();
				if(isForwordWall(x,y))
				{
					//左を向く
					turnLeft();
					turnLeft();
					if(isForwordWall(x,y))
					{
						//後を向く
						turnLeft();
					}
				}
			}
			//一歩進む。
			move(1);

		}

		public function trunBack():void
		{
			turnRight();
			turnRight();
		}

		public function turnRight():void
		{
			direction++;
			if(direction == 4)
			{
				direction = 0
			}
		}

		public function turnLeft():void
		{
			direction--;
			if(direction == -1)
			{
				direction = 3;
			}
		}

		private function move(value:int):void
		{
			switch(direction)
			{
				case 0:
					//上向き
					x = x;
					y -= value;
					break;
				case 1:
					//右向き
					x += value;
					y=y;
					break;
				case 2:
					//下向き
					x = x;
					y += value;
					break;
				case 3:
					//左向き
					x -= value;
					y = y;
					break;
			}
		}

		private function isForwordWall(posX:uint, posY:uint):Boolean
		{
			//正面が壁か否か
			switch(direction)
			{
				case 0:
					//上向き
					posY -= 1;
					break;
				case 1:
					//右向き
					posX += 1;
					break;
				case 2:
					//下向き
					posY += 1;
					break;
				case 3:
					//左向き
					posX -= 1;
					break;
			}
			//自分の前が壁か調べる。

			return (maze.data[posX + maze.width*posY] == 1);

		}

	}

//サーチ

	class TremauxSearch
	{

		//検索結果をまるごと格納。
		public var data:Array;

		private var searchData:Array;

		private var maze:Maze;
		public var startPoint:Point;
		public var goalPoint:Point;

		private var searcher:TremauxSearcher;

		//捜索過程を格納
		public var buildProgressArray:Array;

		public function TremauxSearch(maze:Maze, startPoint:Point, goalPoint:Point, buildProgressArray:Array = null)
		{
			this.maze = maze;
			this.startPoint = startPoint;
			this.goalPoint = goalPoint;
			this.buildProgressArray = buildProgressArray;

			this.initialize();
		}

		protected function initialize():void
		{
			searcher = new TremauxSearcher(0, maze);
			searcher.x = 1;
			searcher.y = 1;

			searchData = [].concat(maze.data);

			move();
		}

		protected function move():void
		{

			var loop:Boolean = true;
			var cnt:int = 0;
			var cntMax:int = 50000;	//たどり着けない疑いあり・・・のため。。
			//while(loop)
			while(cnt++ < cntMax )
			{
				if(!loop)
				{
					cnt = cntMax;
					return;
				}

				//移動する
				searcher.forword();
				setMark(searcher.x, searcher.y);

				if(isGoal(searcher.x, searcher.y))
				{
					//ゴール。
					loop = false;
					//trace("ゴール");
				}
				else if(this.isDive(searcher.x, searcher.y))
				{
					//分岐点に来た。
					var mark:uint = getData(searcher.x, searcher.y);
					if(mark == 2)
					{
						//初めて来た分岐点
						//道を一本選んで進む。
						loop = selectRoadAndTurn();
					}
					else if(mark == 3 && getData(searcher.oldX, searcher.oldY)==3)
					{
						//行き止まりで戻ってきた場合
						loop = selectRoadAndTurn();
					}
					else
					{
						//来たことのある分岐点の場合は引き返す。
						searcher.trunBack();
					}

				}
				else if(this.isDeadEnd(searcher.x, searcher.y))
				{
					//行き止まりの場合は引き返す。
					searcher.trunBack();
					setMark(searcher.x, searcher.y);
				}

			}

		}

		private function isGoal(x:uint, y:uint):Boolean
		{
			return (goalPoint.x == x && goalPoint.y == y);
		}

		private function isDeadEnd(x:uint, y:uint):Boolean
		{
			//行き止まり
			var top:uint = getData(x,y-1);
			var right:uint = getData(x+1,y);
			var bottom:uint = getData(x,y+1);
			var left:uint = getData(x-1,y);
			var deadEndCnt:uint=0;
			if(top == 1)deadEndCnt++;
			if(right == 1)deadEndCnt++;
			if(bottom == 1)deadEndCnt++;
			if(left == 1)deadEndCnt++;
			return (deadEndCnt >= 3)
		}

		private function isDive(x:uint, y:uint):Boolean
		{
			//分岐点か否か

			var top:uint = getData(x,y-1);
			var right:uint = getData(x+1,y);
			var bottom:uint = getData(x,y+1);
			var left:uint = getData(x-1,y);
			var diveCnt:uint=0;
			if(top != 1)diveCnt++;
			if(right != 1)diveCnt++;
			if(bottom != 1)diveCnt++;
			if(left != 1)diveCnt++;
			return (diveCnt >= 3)
		}

		private function selectRoadAndTurn():Boolean
		{
			//道を選んで、方向を合わせておく。
			var x:uint = searcher.x;
			var y:uint = searcher.y;

			var top:uint = getData(x,y-1);
			var right:uint = getData(x+1,y);
			var bottom:uint = getData(x,y+1);
			var left:uint = getData(x-1,y);

			//通ったことのない道を優先させる。
			if(top == 0 && !isOldPos(x,y-1))
			{
				//上に進める
				searcher.direction = 0;
				return true;
			}

			if(right == 0 && !isOldPos(x+1,y))
			{
				//右に進める
				searcher.direction = 1;
				return true;
			}

			if(bottom == 0 && !isOldPos(x,y+1))
			{
				//下に進める
				searcher.direction = 2;
				return true;
			}

			if(left == 0 && !isOldPos(x-1,y))
			{
				//左に進める
				searcher.direction = 3;
				return true;
			}

			if(top == 2 && !isOldPos(x,y-1))
			{
				//上に進める
				searcher.direction = 0;
				return true;
			}

			if(right == 2 && !isOldPos(x+1,y))
			{
				//右に進める
				searcher.direction = 1;
				return true;
			}

			if(bottom == 2 && !isOldPos(x,y+1))
			{
				//下に進める
				searcher.direction = 2;
				return true;
			}

			if(left == 2 && !isOldPos(x-1,y))
			{
				//左に進める
				searcher.direction = 3;
				return true;
			}			

			return false;

		}

		private function isOldPos(x:uint, y:uint):Boolean
		{
			return(searcher.oldX == x && searcher.oldY == y);
		}

		public function getData(x:uint, y:uint):uint
		{
			return searchData[x + maze.width*y];
		}

		protected function setMark(x:uint, y:uint):void
		{
			var d:uint = searchData[x + maze.width*y];
			var value:uint;
			if(d == 0)
			{
				value = 2;
				searchData[x + maze.width*y] = value;
			}
			else if(d == 2)
			{
				value = 3;
				searchData[x + maze.width*y] = value;
			}

			if(buildProgressArray)
			{
				buildProgressArray.push(new MazePoint(x,y,value));
			}
		}
	}

0 Responses to “Generative labyrinth”


  • No Comments

Leave a Reply