In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

迷路で眺める探索アルゴリズム(Search algorithm visualize)

8種類の基本的な探索アルゴリズム
【深さ優先、幅優先、反復深化深さ優先、最良優先、間違い制限探索、幅優先ビームサーチ、最良優先ビームサーチ】
と、
2種類のオリジナル(?)の探索アルゴリズム
【優良優先探索、二重制限探索】(仮称)
を迷路を使って可視化しました。

☆迷路の見方
■ 青色: Sがスタート、Gがゴール
■ 白色: 未探索のマス
■ 灰色: 探索済みのマス
■ 赤色: 探索により発見されて、リストに保持されているの分岐点
■ 橙色: 枝刈りにより、リストから削除された分岐点

赤マスに書いてある数字は、リスト内のマスにゴールに近い順に順位を付けたものです。ただし、間違い制限探索のみ間違い数(discrepancy)です。

詳しい解説は以下を見てください
http://spheresofa.net/blog/?p=1044
Get Adobe Flash player
by shohei909 02 Mar 2013

    Talk

    makc3d at 27 Feb 2013 14:09
    what about good old "walk along the wall" ?
    poulet at 27 Feb 2013 14:23
    i am impressed by the "good first" method !
    shohei909 at 28 Feb 2013 08:42
    >>makc3d One of the depth-first search works very similar to ''walk along the wall'', and depth-first search is more versatile than it. So I omitted it. >>poulet Thank you! The good-first search is a generalization of "best-first". When its width is 1, It is equals to best-first search. So I named it "good-fist".

    Tags

    Embed
/**
 * Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/hq8p
 */

package {
    import com.bit101.components.CheckBox;
    import com.bit101.components.ComboBox;
    import com.bit101.components.Component;
    import com.bit101.components.HBox;
    import com.bit101.components.HUISlider;
    import com.bit101.components.Label;
    import com.bit101.components.NumericStepper;
    import com.bit101.components.PushButton;
    import com.bit101.components.Slider;
    import com.bit101.components.Style;
    import com.bit101.components.VBox;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    
    [SWF(frameRate="60")]
    public class Main extends Sprite {
        private const SEARCH_LIST:Array = [DepthFirst, BreadthFirst, IDDepthFirst, HillClimbing, BestFirst, LimitedDiscrepancy, BreadthFirstBeam, BestFirstBeam, GoodFirst, DoubleLimit ];
        private const SPEED_LIST:Array = [ 120, 60, 35, 15, 10, 7, 5, 3, 2, 1 ]
        public var bitmap:Bitmap;
        public var maze:Maze;
        public var log:Log;
        public var running:Boolean;
        private var speed:int = 5;
        private var count:int = 0;
        private var stepItems:Array;
        private var speedSlider:HUISlider;
        private var stepSlider:Slider;
        private var stepLabel:Label;
        private var output:Label;
        private var startBtn:PushButton;
        private var combo:ComboBox;
        private var mapCheck:CheckBox;
        private var mapBtn:PushButton;
        private var steppers:Array  = [];
        private var labels:Array    = [];
        private var changedCount:int = 0;
        private var params:Array;
        private var footer:HBox;
        
        public function Main():void {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            addEventListener(Event.ENTER_FRAME, onFrame);
            
            //UI
            var back:BitmapData = new BitmapData( 2, 2, false, 0xF6F6F6 ); 
            back.setPixel(0, 1, 0xFFFFFF);
            var g:Graphics = graphics;
            g.beginFill( 0xFAFAFA, 1 )
            g.beginBitmapFill( back );
            g.drawRect( 0, 0, 465, 465 );
            
            //ボタン
            Style.embedFonts     = false;
            Style.fontSize        = 10;
            Style.fontName         = "MS ゴシック,MS Gothic,Meiryo UI,Osaka";
            var vbox:VBox = new VBox( this, 0, 4 );
            var hbox:HBox = new HBox( vbox, 5, 0 );
            hbox.height = 20;            
            hbox.spacing = 4;
            //hbox.alignment = "middle";
            function getNames( item:*, ...args ):String { return item.name; }
            combo = new ComboBox( hbox, 0, 0, null, SEARCH_LIST.map( getNames ) );
            combo.selectedIndex = 0;
            combo.width = 240;
            combo.height = 20
            combo.addEventListener( Event.SELECT, onSearchChange );
            combo.numVisibleItems = SEARCH_LIST.length;
            mapCheck = new CheckBox( hbox, 0, 5, "", remap );
            mapCheck.width = 10;
            mapBtn = new PushButton( hbox, 0, 0, "new", remap );
            mapBtn.width = 35;
            var slider:HUISlider = speedSlider = new HUISlider( hbox, 0, 2, "| speed" );
            slider.width     = 160;
            slider.maximum     = SPEED_LIST.length;
            slider.minimum     = 1;
            slider.value    = 3;
            slider.tick     = 1;
            slider.labelPrecision = 0;
            //--
            hbox = new HBox( vbox, 5, 0 );
            hbox.height = 20;
            hbox.spacing = 3;
            //hbox.alignment = "middle";
            stepItems = [];
            startBtn = new PushButton( hbox, 0, 0, "start", onStart )
            startBtn.width = 45;
            new Label( hbox, 0, 0, "|" );
            var btn:PushButton;
            btn = new PushButton( hbox, 0, 0, "<<", top );
            btn.width = 30; stepItems.push( btn );
            btn = new PushButton( hbox, 0, 0, "<", prev );
            btn.width = 30; stepItems.push( btn );
            stepSlider = new Slider( "horizontal", hbox, 0, 5, slide );
            stepSlider.width     = 190;
            stepSlider.tick        = 1;
            stepSlider.minimum    = 0;
            stepItems.push( stepSlider );
            stepLabel  = new Label( hbox, 0, 2, "10000/10000" );
            btn = new PushButton( hbox, 0, 0, ">", next );
            btn.width = 30; stepItems.push( btn );
            btn = new PushButton( hbox, 0, 0, ">>", end );
            btn.width = 30; stepItems.push( btn );
            
            //--
            footer = hbox = new HBox( vbox, 5, 0 );
            hbox.height = 20;
            hbox.spacing = 3;
            //hbox.alignment = "middle";
            for ( var i:int = 0; i < 2; i++ ) {
                labels.push( new Label( hbox, 0, 0, "ビーム幅(beam width)" ) );
                var stepper:NumericStepper = new NumericStepper( hbox, 0, 0, onStepperChange );
                stepper.maximum = 99;
                stepper.minimum = 1;
                stepper.width = 60;
                steppers.push( stepper );
            }
            //--
            output = new Label( this, 5, 450, "" );
            
            //迷路初期化
            maze     = new Maze( 41, 33 );
            maze.make();
            addChild( bitmap = new Bitmap( maze.bitmapData() ) );
            bitmap.x = (stage.stageWidth - bitmap.width) / 2;
            bitmap.y = 80;
            
            onSearchChange( null );
            onStart( null );
        }
        
        private function onFrame( e:Event ):void {
            stepLabel.text = (log.position + 1) + "/" + log.length;
            var p:int = stepSlider.value = log.position;
            stepSlider.maximum = log.length - 1;
            
            if ( changedCount > 0 ) {
                changedCount--;
                if ( changedCount == 0 ) { search(); update(); }
            }
            if (! running || count++ % SPEED_LIST[speedSlider.value - 1] ) return;
            if(! log.isLast() ){
                log.next();
                update();
            }
        }
        
        private function update():void {
            log.apply( maze );
            maze.draw( bitmap.bitmapData, log.isLast() ? log.goal : null );
            output.text = "";
            for ( var key:String in maze.data ) {
                var d:int = maze.data[key];
                output.text += Maze.NAMES[key] + ":" + d + " ";
            }
            footer.draw();
        }
        private function onStepperChange( e:Event ):void {
            changedCount = 30;
            for ( var i:int = 0, l:int = params.length; i < l; i++ ) {
                params[i] = steppers[i].value;
            }
        }
        private function onSearchChange( e:Event ):void {
            setupSearchCtrl();
            search();
            update();
        }
        
        private function onStart( e:Event ):void {
            if ( running ) {
                running = false;
                startBtn.label = "start";
                //setStepCtrl( true );
            }else {
                running = true;
                startBtn.label = "pause";
                //setStepCtrl( false );
            }
            update();
        }
        
        private function search():void {
            var algorithm:Object = SEARCH_LIST[ combo.selectedIndex ];
            searchId++;
            maze.reset();
            if ( params ) {
                log = algorithm.search.apply( null, [maze].concat( params ) );
            }else{
                log = algorithm.search( maze );
            }
            stepSlider.value     = 0;
            update();
        }
        
        private function top( e:Event ):void {
            log.position = 0;
            update();
        }
        private function prev( e:Event ):void {
            log.prev();
            update();
        }
        private function next( e:Event ):void {
            log.next();
            update();
        }
        private function end( e:Event ):void {
            log.position = log.length - 1;
            update();
        }
        private function slide( e:Event ):void {
            if (log.position == stepSlider.value) return;
            log.position = stepSlider.value;
            update();
        }
        private function remap( e:Event ):void {
            maze.make( mapCheck.selected ? 0 : 1 );
            search();
        }
        
        private function setupSearchCtrl():void {
            var s:Object = SEARCH_LIST[ combo.selectedIndex ];
            params = s.params;
            var num:int = params ? params.length : 0;
            for ( var i:int = 0, l:int = steppers.length; i < l; i++ ) {
                if ( i < num ) {
                    labels[i].visible         = true;
                    labels[i].text             = s.paramNames[i];
                    steppers[i].visible     = true;
                    steppers[i].value        = params[i];
                    if ( s.mins )    steppers[i].minimum        = s.mins[i];
                    else            steppers[i].minimum        = 1;
                }else {
                    labels[i].visible         = false;
                    steppers[i].visible     = false;
                }
            }
        }
        private function setStepCtrl( enabled:Boolean ):void {
            for each(var c:Component in stepItems ) {
                c.enabled = enabled;
            }
        }
    }
}

import adobe.utils.CustomActions;
import flash.display.BitmapData;
import flash.events.StatusEvent;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.text.AntiAliasType;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.utils.ByteArray;

class Maze {
    public static const CELL:int         = 9;
    public static const WALL:int         = 2;
    public static const COLORS:Array     = [
        0x333333,
        0xf0f0f0,
        0xB5B5B5,
        0xF82018,
        0xFF9822,
        0x777777,
        0x2233F4,
        0x2233F4
    ];
    
    public static const DIRS:Array = [
        [0, -1],
        [-1, 0],
        [0, 1],
        [1, 0]
    ];
    
    //0:壁, 
    //1:未開拓の道
    //2:探索済みの道
    //3:探索中の道
    //4:探索保留中の道
    //5:枝刈り済みの道
    //6:スタート
    //7:ゴール
    public var map:Array;
    public var start:Position;
    public var end:Position;
    public var width:int;
    public var height:int;
    public var branches:Array/*Position*/;
    public var informed:Boolean;
    
    public var data:Object;
    static public const NAMES:Object = {
        memory:         "分岐数(mem)",
        maxMemory:         "最大分岐数(max mem)",
        time:             "探索回数(time)",
        depthLimit:        "深さ制限(depth limit)",
        discrepancy:    "間違い制限(discrepancy)"
    }
    
    function Maze( width:int, height:int ) {
        this.width         = width;
        this.height     = height;
    }
    
    public function make( type:int = 1 ):void {
        data = { memory:1, maxMemory:1, time:1 };
        switch( type ) {
            case 0: _makeDigMaze();            break;
            case 1: _makeClusterMaze();     break;
        }
        
        start = new Position( 1, 1 );
        do end   = new Position( int(Math.random() * width) * 2 + 1, int(Math.random() * height) * 2 + 1 );
        while ( end.x == start.x && end.y == start.y );
        branches = [start];
        start.score = evalute( start );
        map[start.x][start.y] = 3;
    }
    
    public function reset():void {
        data = { memory:1, maxMemory:1, time:1 };
        for ( var x:int = 0; x < (width * 2); x++ ) {
            for ( var y:int = 0; y < (height * 2); y++ ) {
                switch( map[x][y] ) {
                    case 0:     map[x][y] = 0;    break;
                    default:     map[x][y] = 1;     break;
                }
            }
        }
        branches = [start];
        map[start.x][start.y] = 3;
    }
    
    //穴掘り法で迷路作成。
    private function _makeDigMaze():void {
        _initMap( 0 );
        var joints:Vector.<Position> = new Vector.<Position>();
        var p:Position = new Position( int(Math.random() * width) * 2 + 1, int(Math.random() * height) * 2 + 1 );
        var w:int = width * 2 + 1;
        var h:int = height * 2 + 1;
        
        do {
            map[ p.x ][ p.y ] = 1;
            var arr:Array = [];
            for each( var d:Array in DIRS ) {
                var nx:int = p.x + 2 * d[0];
                var ny:int = p.y + 2 * d[1];
                if ( 0 <= nx && nx < w && 0 <= ny && ny < h && map[nx][ny] == 0 ) {
                    arr.push( d );
                }
            }
            
            var l:int = arr.length
            
            if ( l == 0 ) {
                l = joints.length;
                if ( l == 0 ) break;
                var r:int = l - 1;//int(l * Math.random());
                p = joints[ r ];
                joints[ r ] = joints[l - 1];
                joints.pop();
                continue;
            }
            
            joints.push( p );
            d = arr[ int(l * Math.random()) ];
            map[ p.x + d[0] ][ p.y + d[1] ] = 1;
            p = new Position( p.x + d[0] * 2, p.y + d[1] * 2 );
        }while ( true );
    }
    
    //クラスタリングアルゴリズムで迷路作成。
    private function _makeClusterMaze():void {
        var cluster:Vector.<Vector.<int>> = _initCluster();
        var walls:Vector.<Position> = new Vector.<Position>();
        _initMap( 1 );
        for ( var x:int = 0; x < width; x++ ) {
            for ( var y:int = 0; y < height; y++ ) {
                if( y + 1 != height ) walls.push( new Position(x, y, 2) );
                if( x + 1 != width  ) walls.push( new Position(x, y, 3) );
            }
        }
        
        //1つづつ壁を壊す
        var l:int;
        while ( (l = walls.length) > 0 ) {
            var rand:int     = Math.random() * walls.length;
            var p:Position  = walls[rand];
            walls[rand]     = walls[l - 1] ;
            walls.length--;
            
            var d:Array        = DIRS[ p.dir ];
            var num:int        = cluster[p.x + d[0]][p.y + d[1]];
            if ( num != cluster[p.x][p.y] ) {
                map[p.x * 2 + d[0] + 1][p.y * 2 + d[1] + 1] = 1;
                connectCluster( p.x, p.y, num, cluster );
            }
        }
    }
    
    //
    private function connectCluster( x:int, y:int, num:int, cluster:Vector.<Vector.<int>> ):void {
        var old:int = cluster[x][y];
        cluster[x][y] = num;
        for each( var d:Array in DIRS ) {
            var nx:int = x + d[0];
            var ny:int = y + d[1];
            if ( 0 <= nx && nx < width && 0 <= ny && ny < height && old == cluster[nx][ny] ) connectCluster( nx, ny, num, cluster );
        }
    }
    
    private function _initCluster():Vector.<Vector.<int>> {
        var cluster:Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
        for ( var x:int = 0, i:int = 0; x < width; x++ ) {
            cluster[x] = new Vector.<int>();
            for( var y:int = 0; y < height; y++, i++ ) {
                cluster[x][y] = i;
            }
        }
        return cluster;
    }
    
    private function _initMap(type:int):void {
        map = [];
        for ( var x:int = 0, w:int = width * 2 + 1; x < w; x++ ) {
            map[x] = [];
            for( var y:int = 0, h:int = height * 2 + 1; y < h; y++ ) {
                map[x][y] = int(type > 0 && (x % 2 == 1) && (y % 2 == 1));
            }
        }
    }
    public function bitmapData():BitmapData {
        return new BitmapData( CELL * width + WALL * (width + 1), CELL * height + WALL * (height + 1), false );
    }
    public function draw( b:BitmapData, goal:Position = null ):void {
        b.fillRect( b.rect, 0xFFFF );
        var r:Rectangle = new Rectangle();
        var bx:int = 0, by:int = 0;
        
        for ( var x:int = 0, w:int = width * 2 + 1; x < w; x++, r.x = (bx += bw), r.y = by = 0 ) {
            for ( var y:int = 0, h:int = height * 2 + 1; y < h; y++ ) {
                var bw:int = r.width  = (x % 2 == 0) ? WALL : CELL;
                var bh:int = r.height = (y % 2 == 0) ? WALL : CELL;
                
                b.fillRect( r, COLORS[ map[x][y] ] );
                r.x = bx;
                r.y = (by += bh);
            }
        }
        
        if ( informed )
            for ( var i:int = 0, l:int = branches.length; i < l; i++ ) {
                var p:Position = branches[i];
                drawText( "" + i, p.x, p.y, b );
            }
        
        if ( goal ) {
            var p0:Position = goal, p1:Position;
            while ( (p1 = p0.prev) ) {
                var sx:Number = (p0.x + 1) / 2 * (WALL + CELL) - CELL / 2;
                var sy:Number = (p0.y + 1) / 2 * (WALL + CELL) - CELL / 2;
                var ex:Number = (p1.x + 1) / 2 * (WALL + CELL) - CELL / 2;
                var ey:Number = (p1.y + 1) / 2 * (WALL + CELL) - CELL / 2;
                var tmp:Number;
                if ( sx > ex ) { tmp = sx; sx = ex; ex = tmp; }
                if ( sy > ey ) { tmp = sy; sy = ey; ey = tmp; }
                b.fillRect( new Rectangle( sx - 0.5, sy - 0.5, (ex - sx) + 1, (ey - sy) + 1 ), 0xFFFFFF );
                p0 = p1;
            }
        }
        
        
        b.fillRect( cellRect( start.x, start.y, CELL - 2), COLORS[6] );
        b.fillRect( cellRect( end.x, end.y, CELL - 2), COLORS[6] );
        drawText( "S", start.x, start.y, b );
        drawText( "G", end.x, end.y, b );
        
    }
    
    private function drawText( str:String, x:int, y:int, b:BitmapData, color:uint = 0xFFFFFF ):void {
        var text:TextField = new TextField();
        text.autoSize     = TextFieldAutoSize.LEFT;
        text.embedFonts = true;
        text.antiAliasType = AntiAliasType.NORMAL;
        text.defaultTextFormat = new TextFormat("PF Ronda Seven", 8, color);
        text.text = str;
        var px:int = (x + 1) / 2 * (WALL + CELL) - CELL / 2 - text.width / 2 + 0.5;
        var py:int = (y + 1) / 2 * (WALL + CELL) - CELL / 2 - 9.5;
        b.draw( text, new Matrix( 1, 0, 0, 1, px, py) ); 
    }
    private function cellRect( x:int, y:int, size:Number ):Rectangle {
        var rx:Number = (x + 1) / 2 * (WALL + CELL) - (CELL + size) / 2;
        var ry:Number = (y + 1) / 2 * (WALL + CELL) - (CELL + size) / 2;
        return new Rectangle( rx, ry, size, size )
    }
    
    public function clone():Maze {
        var c:Maze = new Maze( width, height );
        c.map = [];
        for ( var x:int = 0, w:int = map.length; x < w; x++ ) {
            c.map.push([]);
            for ( var y:int = 0, h:int = map[x].length; y < h; y++ )
                c.map[x].push( map[x][y] );
        }
        c.branches = [];
        for ( var i:int = 0, l:int = branches.length; i < l; i++ )
            c.branches.push( branches[i] );
        c.data = { };
        for ( var k:String in data ) c.data[k] = data[k];
        c.informed = informed;
        c.start = start;
        c.end = end;
        return c;
    }
    
    //ゴールまでのマンハッタン距離。低いほど良い。
    public function evalute( pos:Position ):int {
        return (Math.abs( pos.x - end.x ) + Math.abs( pos.y - end.y )) >> 1;
    }
    
    public function searchAt( index:int ):void {
        var p:Position = branches[ index ];
        var count:int    = 0;
        
        for( var i:int = 0; i < 4; i++ ){
            var d:Array/*int*/ = DIRS[i];
            var x:int = d[0] + p.x;
            var y:int = d[1] + p.y;
            if ( x < 0 || (width * 2) <= x || y < 0 || (height * 2) <= y ) continue;
            if ( map[x][y] == 0 || map[x][y] == 2 ) continue;
            if ( ++count == 2 ) { break; }
            
            map[x][y] = 2;
            x += d[0]; y += d[1];
            map[x][y] = 3;
            var next:Position = new Position( x, y, i );
            next.prev = p;
            next.depth = p.depth + 1;
            branches.push( next );
            next.score = evalute( next );
            data.time++;
        }
        
        if ( count < 2 ) { 
            branches.splice( index, 1 );
            map[p.x][p.y] = 2;
        }
        
        data.memory = branches.length;
        if ( data.maxMemory < branches.length ) data.maxMemory = branches.length;
    }
    
    public function searchAllAt( index:int ):Array {
        var arr:Array = [];
        var p:Position = branches[ index ];
        branches.splice( index, 1 );
        map[p.x][p.y] = 2;
        
        for( var i:int = 0; i < 4; i++ ){
            var d:Array/*int*/ = DIRS[i];
            var x:int = d[0] + p.x;
            var y:int = d[1] + p.y;
            if ( x < 0 || (width * 2) <= x || y < 0 || (height * 2) <= y ) continue;
            if ( map[x][y] == 0 || map[x][y] == 2 ) continue;
            
            map[x][y] = 2;
            x += d[0]; y += d[1];
            map[x][y] = 3;
            
            var next:Position = new Position( x, y, i );
            next.prev = p;
            next.depth = p.depth + 1;
            branches.push( next );
            arr.push( next );
            next.score = evalute( next );
            data.time++;
        }
        
        data.memory = branches.length;
        if ( data.maxMemory < branches.length ) data.maxMemory = branches.length;
        return arr;
    }
    
    public function sort():void {
        branches.sortOn( "score", Array.NUMERIC );
    }
    
    public function cut( p:Position ):void {
        map[p.x][p.y] = 4;
        branches.splice( branches.indexOf( p ), 1 );
    }
    
    public function cutRange( start:int, end:int = int.MAX_VALUE ):void {
        if ( end > branches.length ) end = branches.length;
        for ( var i:int = start; i < end; i++ ) {
            var p:Position = branches[i];
            map[p.x][p.y] = 4;
        }
        branches.splice( start, end - start );
    }
    
    public function isFinish():Position {
        for each (var p:Position in branches)
            if (p.score == 0) return p;
        return null;
    }
}

//
class Position {
    //0:上, 1:左, 2:下, 3:右
    public var dir:int;
    public var x:int;
    public var y:int;
    public var score:int;
    public var discrepancy:int;
    public var prev:Position;
    public var depth:int = 0;
    
    function Position(x:int, y:int, dir:int = -1) {
        this.x = x; this.y = y; this.dir = dir;
    }
}

class Log {
    public var goal:Position;
    public var map:Vector.<Array> = new Vector.<Array>();
    public var branches:Vector.<Array> = new Vector.<Array>();
    public var data:Array = new Array();
    public var position:int = 0;
    public var informed:Boolean;
    public function get length():int { return map.length; }    
    function Log( maze:Maze, informed:Boolean = false ) { 
        this.informed = informed;
        add( maze );
    }
    public function add( maze:Maze ):void {
        maze = maze.clone();
        this.data.push( maze.data );
        this.map.push( maze.map );
        this.branches.push( maze.branches );
    }
    public function isFirst():Boolean { return position == 0; }
    public function isLast():Boolean { return position == map.length - 1; }
    public function next():int {
        return isLast() ? position : ++position;
    }
    public function prev():int {
        return isFirst() ? position : --position;
    }
    
    public function apply( maze:Maze, pos:int = -1 ):void {
        if( pos == -1 ) pos = position
        maze.map         = map[ pos ];
        maze.branches     = branches[ pos ];
        maze.data        = data[pos];
        maze.informed    = informed;
    }
}

class Node {
    public var cost:Number;
    public var position:Position;
}


class BreadthFirst {
    static public const name:String = "幅優先(breadth-first)"; 
    static public function search(maze:Maze):Log {
        var log:Log = new Log(maze);
        do {
            for (var i:int = 0, l:int = maze.branches.length; i < l; i++ )
                maze.searchAllAt( 0 );
            log.add( maze );
            if ( maze.branches.length == 0 || (log.goal = maze.isFinish()) ) { break; }
        }while ( true );
        return log;
    }
}

class DepthFirst{
    static public const name:String = "深さ優先(depth-first)"; 
    static public function search(maze:Maze):Log {
        maze = maze.clone();
        var log:Log = new Log(maze);
        do {
            maze.searchAt( maze.branches.length - 1 );
            log.add( maze );
            log.goal = null;
            if ( maze.branches.length == 0 || (log.goal = maze.isFinish()) ) { 
                trace( log.goal );
                trace( maze.end.x, maze.end.y );
                trace( log.goal.x, log.goal.y );
                break; 
            }
        }while ( true );
        return log;
    }
}

import flash.utils.setTimeout;
import flash.utils.getTimer;
var searchId:int = 0;
class IDDepthFirst{
    static public const name:String                     = "反復深化深さ優先(iterative deepening)"; 
    static public var best:int;
    static public const paramNames:Array/*String*/         = ["margin"]; 
    static public const params:Array/*int*/             = [4];
    static public const mins:Array/*int*/                 = [0];
    
    static public function search(maze:Maze, margin:int ):Log {
        best = int.MAX_VALUE;
        maze = maze.clone();
        var limit:int = maze.evalute( maze.start ) + margin;
        maze.data.depthLimit = limit;
        var log:Log = new Log( maze );
        limitSearch( maze, limit, margin, log, searchId );
        return log;
    }
    
    static private function limitSearch( maze:Maze, limit:int, margin:int, log:Log, id:int ):void {
        if ( id != searchId ) return;
        var startTime:int = getTimer();
        var cut:Boolean = false;
        
        do {
            var end:int = maze.branches.length - 1;
            var p:Position = maze.branches[ end ];
            if( p.depth < limit ){
                if ( cut ) log.add( maze );
                maze.searchAt( end );
                log.add( maze );
                cut = false;
            }else {
                var d:int = p.depth + maze.evalute( p );
                if ( best > d ) { best = d; }
                maze.cutRange( end-- );
                cut = true;
            }
            
            if ( maze.branches.length == 0 ) {
                maze.reset();
                limit = best + margin;
                maze.data.depthLimit = limit;
                log.add( maze );
                best = int.MAX_VALUE;
                limitSearch( maze, limit, margin, log, id );
                break;
            }
            if ( (log.goal = maze.isFinish()) ) { break; }
            
            //フリーズ回避
            if ( cut == false && (getTimer() - startTime) > 30 ) { 
                setTimeout( limitSearch, 10, maze, limit, margin, log, id );
                break;
            }
        }while ( true );
    }
}

class HillClimbing {
    static public const name:String = "山登り法(hill climbing)"; 
    static public function search(maze:Maze):Log {
        return DoubleLimit.search( maze, 1, 1 );
    }
}

class BestFirst {
    static public const name:String = "最良優先(best-first)"; 
    static public function search(maze:Maze):Log {
        return DoubleLimit.search( maze, 1, int.MAX_VALUE );
    }
}

class LimitedDiscrepancy {
    static public const name:String = "間違い制限(limited discrepancy)"; 
    static public function search(maze:Maze):Log {
        maze = maze.clone();
        maze.data.discrepancy = "0";
        var log:Log = new Log( maze, true );
        limitSearch( maze, 0, log, searchId );
        return log;
    }
    
    static public function limitSearch( maze:Maze, limit:int, log:Log, id:int ):void {
        if ( id != searchId ) return;
        var startTime:int = getTimer();
        do {
            var d:int = limit;
            var l:int = maze.branches.length;
            if ( d >= l ) d = l - 1;
            var arr:Array = maze.searchAllAt( d );    
            
            arr.sortOn( "score" );
            for (var i:int = 0, al:int = arr.length; i < al; i++ ) {
                var p:Position = arr[ i ];
                p.discrepancy = d + i;
            }
            maze.branches.sortOn( "discrepancy" );
            log.add( maze );
            
            l = maze.branches.length;
            if ( l == 0 ) { 
                maze.reset();
                limit++;
                maze.data.discrepancy = limit;
                log.add( maze );
                continue;
            }
            
            if( (log.goal = maze.isFinish()) ) { break; }
            if ( l > limit + 1 ) {
                maze.cutRange( limit + 1 );
                log.add( maze );
            }
            
            if( (getTimer() - startTime) > 30 ) { 
                setTimeout( limitSearch, 10, maze, limit, log, id );
                break;
            }
        }while( true );
    }
}

class BreadthFirstBeam {
    static public const name:String = "幅優先ビーム(breadth-first beam)"; 
    static public const paramNames:Array/*String*/     = ["ビーム幅(beam width)"]; 
    static public const params:Array/*int*/     = [5];
    static public function search(maze:Maze, beamWidth:int):Log {
        return DoubleLimit.search( maze, beamWidth, beamWidth );
    }
}

class BestFirstBeam {
    static public const name:String = "最良優先ビーム(best-first beam)"; 
    static public const paramNames:Array/*String*/     = ["ビーム幅(beam width)"]; 
    static public const params:Array/*int*/     = [5];
    static public function search(maze:Maze, beamWidth:int):Log {
        return DoubleLimit.search( maze, 1, beamWidth );
    }
}

class GoodFirst {
    static public const name:String = "優良優先(good-first)"; 
    static public const paramNames:Array/*String*/         = ["優先幅(good-first width)"]; 
    static public const params:Array/*int*/     = [3];
    static public function search(maze:Maze, goodWidth:int):Log {
        return DoubleLimit.search( maze, goodWidth, int.MAX_VALUE );
    }
}

class DoubleLimit{
    static public const name:String                     = "二重制限(double limit)"; 
    static public const paramNames:Array/*String*/         = ["優先幅(good width)", "ビーム幅(beam width)"]; 
    static public const params:Array/*int*/             = [3, 7]; 
    static public function search(maze:Maze, goodWidth:int, beamWidth:int ):Log {
        var log:Log = new Log( maze, true );
        do {
            var l:int = maze.branches.length;
            if ( l > goodWidth ) l = goodWidth;
            for ( var i:int = 0; i < l; i++ ) {
                maze.searchAllAt( 0 );
            }
            maze.sort();
            log.add( maze );
            l = maze.branches.length;
            if ( l == 0 || (log.goal = maze.isFinish()) ) { break; }
            if( beamWidth < l ) {
                maze.cutRange( beamWidth );
                log.add( maze );
            }
        }while ( true );
        return log;
    }
}