Zoomed Labyrinth
トレモー法で迷路探索
/**
* Copyright peko ( http://wonderfl.net/user/peko )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/trQg
*/
// 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.x)/15;
zoomSpr.y += (cursor.y-zoomSpr.y)/15;
filters = [new DisplacementMapFilter(zoomBmd, new Point(zoomSpr.x-(zoomBmd.width>>1), zoomSpr.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));
}
}
}