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

flash on 2015-10-26

Get Adobe Flash player
by shallot 26 Oct 2015
    Embed
/**
 * Copyright shallot ( http://wonderfl.net/user/shallot )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/yjEc
 */

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;

    }

}