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

Square Game

締め切り前日に3時間でつくるといって5日かかった結果がこれだよ!
/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/tlE3
 */

// forked from checkmate's Checkmate Vol.6 Sponser
package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.*;
    import flash.net.*;
    import flash.system.*;
    import org.libspark.betweenas3.*;
    import org.libspark.betweenas3.tweens.ITween;
    import org.libspark.betweenas3.easing.*;
    import org.libspark.betweenas3.core.easing.*;
    
    // 締め切り前日に3時間でつくるといって5日かかった結果がこれだよ!
    [SWF(backgroundColor=0xbbffbb)]
    public class SquareGame extends Sprite {
        public static const IMGSRC : String = "http://assets.dev.wonderfl.net/images/related_images/e/ea/ea8a/ea8a6b1d37c2cec12e07893c66f164a9da1e92c0";
        
        private static const N : uint = 6; // 縦の長さ
        private static const M : uint = 2; // 奥行き
        private static const SCORE_WIN : uint = 5; // 勝利するための駒数
        
        private static const COLORS : Array = [0xffffdddd, 0xffddddff];
        private static const COLORS_SELECTED : Array = [0xffff9999, 0xff9999ff];
        private static const COLORS_CELL : Array = [0xffffcccc, 0xffccccff];
        
        private var _showLayer : Shape; // セル表示用のレイヤー
        private var _roboLayer : Sprite; // robo表示用のレイヤー
        
        private var _robos : Array;
        private var _places : Array; // 座標→Robo
        private var _scores : Array; // スコア格納用
        private var _hist : Array; // 移動履歴。要素はArray. (要素の要素は[移動差分x, 移動差分y, 移動方法])
        
        // 座標変換行列
        private var _mat : Matrix; // スクリーン座標→フィールド座標
        private var _imat : Matrix; // フィールド座標→スクリーン座標
        
        // -1 : 未スタート
        // 0 : 未選択, 1 : 駒選択時, 2 : 駒移動中
        private var _state : int;
        private var _selected : Robo;
        
        // 敵アルゴリズム
        private var _algo : Algorithm;
        
        // スコア表示用
        private var _tfScore0 : TextField;
        private var _tfScore1 : TextField;
        
        private var _moveTween : ITween; // 移動記述用Tween
        private var _leapTween : ITween = null; // 勝利のジャンプ記述用Tween
        
        private var _tf : TextField; // デバッグ用
        
        public function SquareGame() {
            _state = -1;
            _mat = new Matrix(
                180/N, 90/N,
                -180/N, 90/N,
                465/2, 465/2-2*90/N-2*90/N
                );
//                x * 180 / N - y * 180 / N + 465/2,
//                (x-2) * 90 / N + (y-2) * 90 / N + 465/2
            _imat = _mat.clone();
            _imat.invert();
            
            _showLayer = new Shape();
            addChild(_showLayer);
            
            _roboLayer = new Sprite();
            addChild(_roboLayer);
            
            // フィールドのライン引き
            graphics.lineStyle(1, 0x000000);
            var i : int;
            var c1 : Point, c2 : Point;
            for(i = -M;i <= N+M;i++){
                if(i < 0 || i > N){
                    c1 = trans(0, i);
                    c2 = trans(N, i);
                    graphics.moveTo(c1.x, c1.y);
                    graphics.lineTo(c2.x, c2.y);
                    c1 = trans(i, 0);
                    c2 = trans(i, N);
                    graphics.moveTo(c1.x, c1.y);
                    graphics.lineTo(c2.x, c2.y);
                }else{
                    c1 = trans(-M, i);
                    c2 = trans(N+M, i);
                    graphics.moveTo(c1.x, c1.y);
                    graphics.lineTo(c2.x, c2.y);
                    c1 = trans(i, -M);
                    c2 = trans(i, N+M);
                    graphics.moveTo(c1.x, c1.y);
                    graphics.lineTo(c2.x, c2.y);
               }
            }
            
            graphics.lineStyle(2, 0xcc0000);
            c1 = trans(0, 0); graphics.moveTo(c1.x, c1.y);
            c1 = trans(-M, 0); graphics.lineTo(c1.x, c1.y);
            c1 = trans(-M, N); graphics.lineTo(c1.x, c1.y);
            c1 = trans(0, N); graphics.lineTo(c1.x, c1.y);
            c1 = trans(0, 0); graphics.lineTo(c1.x, c1.y);
            
            c1 = trans(N, 0); graphics.moveTo(c1.x, c1.y);
            c1 = trans(N+M, 0); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N+M, N); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, N); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, 0); graphics.lineTo(c1.x, c1.y);
            
            graphics.lineStyle(2, 0x0000cc);
            c1 = trans(0, 0); graphics.moveTo(c1.x, c1.y);
            c1 = trans(0, -M); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, -M); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, 0); graphics.lineTo(c1.x, c1.y);
            c1 = trans(0, 0); graphics.lineTo(c1.x, c1.y);
            
            c1 = trans(0, N); graphics.moveTo(c1.x, c1.y);
            c1 = trans(0, N+M); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, N+M); graphics.lineTo(c1.x, c1.y);
            c1 = trans(N, N); graphics.lineTo(c1.x, c1.y);
            c1 = trans(0, N); graphics.lineTo(c1.x, c1.y);
            
            // スコア表示の準備
            _tfScore0 = new TextField();
            addChild(_tfScore0);
            _tfScore0.autoSize = "center";
            _tfScore0.x = 50; _tfScore0.y = 330;
            _tfScore0.defaultTextFormat = new TextFormat("Comic Sans MS", 100, 0xcc0000);
            
            _tfScore1 = new TextField();
            addChild(_tfScore1);
            _tfScore1.autoSize = "center";
            _tfScore1.x = 400; _tfScore1.y = 330;
            _tfScore1.defaultTextFormat = new TextFormat("Comic Sans MS", 100, 0x0000cc);
            
            // 説明用フィールド
            var tfInfo : TextField = new TextField();
            addChild(tfInfo);
            tfInfo.width = 300;
            tfInfo.height = 100;
            tfInfo.x = 120;
            tfInfo.text = 
                "1ターンに隣接8マス、または、\n" + 
                "他の隣接ロボを1個飛んでどこまでもいけるよ。\n" +
                "赤が自軍、青はCPUが動かすよ。\n" +
                "先に" + SCORE_WIN + "体を対岸に入れた方の勝ちだよ!"; 
            
            // roboを読み込む
            var loader : Loader = new Loader();
            loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onLoadComplete );
            loader.load( new URLRequest(IMGSRC), new LoaderContext(true) );
        }
        
        private function onLoadComplete(e : Event) : void
        {
            var loader : Loader = e.target.loader;
            var bmdRobo : BitmapData = Bitmap( loader.content ).bitmapData;
            loader.contentLoaderInfo.removeEventListener( Event.COMPLETE, onLoadComplete );
            
            // roboの生成
            _robos = [];
            for(var i : uint = 0;i < 2*N*M;i++){
                var r : Robo = new Robo(bmdRobo, 8, true);
                _robos.push(r);
                r.visible = true;
                _roboLayer.addChild(r);
            }
            bmdRobo.dispose();
            
            init();
            
            _tf = new TextField();
            _tf.width = 300;
            _tf.height = 100;
//            addChild(_tf);
            
            stage.addEventListener(MouseEvent.CLICK, onClick);
        }
        
        private function trans(x : Number, y : Number) : Point
        {
            return _mat.transformPoint(new Point(x, y));
        }
        private function itrans(x : Number, y : Number) : Point
        {
            return _imat.transformPoint(new Point(x, y));
        }
        
        // ゲームごとの初期化
        private function init() : void
        {
            _showLayer.graphics.clear();
            if(_leapTween != null)_leapTween.stop();
            
            var i : int, j : int;
            _places = [];
            for(i = -M;i < N + M;i++){
                _places[i] = [];
                for(j = -M;j < N + M;j++){
                    _places[i][j] = null;
                }
            }
            
            _hist = [];
            for(i = -M;i < N + M;i++){
                _hist[i] = [];
                for(j = -M;j < N + M;j++){
                    _hist[i][j] = null;
                }
            }
            
            var p : int = 0;
            for(i = 1;i <= M;i++){
                for(j = 0;j < N;j++){
                    var c : Point;
                    // 赤軍
                    c = trans(0.5 - i, 0.5 + j);
                    _robos[p].x = c.x - 10;
                    _robos[p].y = c.y - 27;
                    _robos[p].dir = 1;
                    _robos[p].visible = true;
                    _robos[p].mx = -i;
                    _robos[p].my = j;
                    _robos[p].side = 0;
                    _robos[p].paint(COLORS[0]);
                    _places[int(-i)][j] = _robos[p];
                    p++;
                    
                    // 青軍
                    c = trans(0.5 + j, 0.5 - i);
                    _robos[p].x = c.x - 10;
                    _robos[p].y = c.y - 27;
                    _robos[p].dir = 0;
                    _robos[p].visible = true;
                    _robos[p].mx = j;
                    _robos[p].my = -i;
                    _robos[p].side = 1;
                    _robos[p].paint(COLORS[1]);
                    _places[j][int(-i)] = _robos[p];
                    p++;
                }
            }
            
            // 敵アルゴリズム
            _algo = new Algorithm(M, N, _robos, _places);
            
            updateScores();
            
            _selected = null;
            _moveTween = null;
            _state = 0;
        }
        
        private function onClick(e : MouseEvent) : void
        {
            if(_state == -1){
                init();
            }
            
            // 対応する床のフィールド座標を取得
            var fp : Point = itrans(mouseX, mouseY);
            fp.x = Math.floor(fp.x);
            fp.y = Math.floor(fp.y);
            
            // e.targetやフィールド座標からroboに変換
            var r : Robo = e.target is Robo ? Robo(e.target) : null;
            if(r == null && isValid(fp.x, fp.y) && _places[fp.x][fp.y] != null){
                r = _places[fp.x][fp.y];
            }
            if(r != null && r.side != 0)r = null;
            
            switch(_state){
            case 0:
                if(r != null){
                    // 味方roboが選択されたら、そのroboの移動可能範囲を表示
                    showMovable(r.mx, r.my, 0);
                    _state = 1;
                    _selected = r;
                    _selected.paint(COLORS_SELECTED[0]);
                }
                break;
            case 1:
                if(r != null){
                    _selected.paint(COLORS[0]);
                    
                    // 味方roboが選択されたら、そのroboの移動可能範囲を表示
                    showMovable(r.mx, r.my, 0);
                    _selected = r;
                    _selected.paint(COLORS_SELECTED[0]);
                }else{
                    if(isValid(fp.x, fp.y) && _hist[fp.x][fp.y] != null){
                        // 移動可能範囲が選択されたら移動
                        var rx : Number = _selected.mx;
                        var ry : Number = _selected.my;
                        // 移動roboを最前面に
                        _roboLayer.setChildIndex(_selected, _roboLayer.numChildren-1);
                        var st : Array = [];
                        for each(var ss : Array in _hist[fp.x][fp.y]){
                            rx += ss[0]; ry += ss[1];
                            if(ss[2] == 0){
                                // 歩く
                                st.push(makeMoveTween(_selected, rx, ry));
                            }else{
                                // ジャンプする
                                st.push(makeJumpTween(_selected, rx, ry, ss[0], ss[1]));
                            }
                        }
                        _moveTween = BetweenAS3.serialTweens(st);
                        _moveTween.onComplete = function() : void {
                            // 移動後処理
                            _places[rx][ry] = _selected;
                            _places[_selected.mx][_selected.my] = null;
                            
                            _selected.mx = rx;
                            _selected.my = ry;
                            
                            clearMovable();
                            updateScores();
                            var jg : int = judgeEnd();
                            if(jg != -1){
                                _state = -1;
                                leap(jg);
                            }else{
                                doEnemysTurn();
                            }
                        };
                        _moveTween.play();
                        _state = 2;
                    }else{
                        // 移動可能範囲が選択されなければ無選択状態にする
                        clearMovable();
                    }
                }
            default: break;
            }
        }
        
        // 敵のターン!
        private function doEnemysTurn() : void
        {
            // アルゴリズムを走らせる
            var res : Array = _algo.run(1);
            
            _selected = res[0];
            var fp : Point = res[1];
            showMovable(_selected.mx, _selected.my, 1);
            _selected.paint(COLORS_SELECTED[1]);
            
            var rx : Number = _selected.mx;
            var ry : Number = _selected.my;
            _roboLayer.setChildIndex(_selected, _roboLayer.numChildren-1);
            var st : Array = [];
            for each(var ss : Array in _hist[fp.x][fp.y]){
                rx += ss[0]; ry += ss[1];
                if(ss[2] == 0){
                    st.push(makeMoveTween(_selected, rx, ry));
                }else{
                    st.push(makeJumpTween(_selected, rx, ry, ss[0], ss[1]));
                }
            }
            _moveTween = BetweenAS3.serialTweens(st);
            _moveTween.onComplete = function() : void {
                // 移動後処理
                _places[rx][ry] = _selected;
                _places[_selected.mx][_selected.my] = null;
                            
                _selected.mx = rx;
                _selected.my = ry;
                            
                clearMovable();
                updateScores();
                var jg : int = judgeEnd();
                if(jg != -1){
                    _state = -1;
                    leap(jg);
                }else{
                    _state = 0;
                }
            };
            _moveTween.play();
            _state = 2;
        }
        
        // 勝利のジャンプをする
        private function leap(side : uint) : void
        {
            zsort();
            
            var indLeap : uint;
            var i : uint;
            
            var tws : Array = [];
            for each(var r : Robo in _robos){
                if(r.side == side){
                    tws.push(BetweenAS3.repeat(
                        BetweenAS3.physicalTo(r, {y : r.y}, new PhysicalAccelerate(-Math.random() * 5 - 3, 1, 30)),
                         99999));
                }
            }
            _leapTween = BetweenAS3.parallelTweens(tws);
            _leapTween.play();
        }
        
        // フィールド座標でz=x+yの値に従ってroboをソート
        private function zsort() : void
        {
            var buf : Array = [];
            for each(var r : Robo in _robos){
                buf.push({z : r.mx + r.my, r : r});
            }
            buf.sortOn("z", Array.NUMERIC);
            
            for(var i : uint = 0;i < buf.length;i++){
                _roboLayer.setChildIndex(buf[i].r, i);
            }
        }
        
        // 終了判定
        private function judgeEnd() : int
        {
            if(_scores[0] >= SCORE_WIN)return 0;
            if(_scores[1] >= SCORE_WIN)return 1;
            return -1;
        }
        
        // スコアの再計算
        private function updateScores() : void
        {
            _scores = [0, 0];
            for(var i : uint = 0;i < M;i++){
                for(var j : uint = 0;j < N;j++){
                    if(_places[i+N][j] != null && _places[i+N][j].side == 0)_scores[0]++;
                    if(_places[j][i+N] != null && _places[j][i+N].side == 1)_scores[1]++;
                }
            }
            _tfScore0.text = "" + _scores[0];
            _tfScore1.text = "" + _scores[1];
        }
        
        // 移動可能範囲表示解除など
        private function clearMovable() : void
        {
            if(_selected != null)_selected.paint(COLORS[_selected.side]);
            _selected = null;
            _showLayer.graphics.clear();
            _state = 0;
        }
        
        // ジャンプ移動のTweenを作成
        // (tx, ty)は移動先、(dx, dy)は移動差分
        private function makeJumpTween(r : Robo, tx : int, ty : int, dx : int, dy : int) : ITween
        {
            var c : Point = trans(tx + 0.5, ty + 0.5);
            var cont : Point = trans(tx - dx * 0.5 + 0.5 - 2, ty - dy * 0.5 + 0.5 - 2);
            return BetweenAS3.bezierTo(r, {x : c.x - 10, y : c.y - 27}, {x : cont.x - 10, y : cont.y - 27}, 0.5);
        }
        
        // 歩行移動のTweenを作成
        // (tx, ty)は移動先
        private function makeMoveTween(r : Robo, tx : int, ty : int) : ITween
        {
            var c : Point = trans(tx + 0.5, ty + 0.5);
            return BetweenAS3.to(r, {x : c.x - 10, y : c.y - 27}, 0.5);
        }
        
        // 移動可能範囲を探して表示
        private function showMovable(x : int, y : int, side : uint) : void
        {
            _showLayer.graphics.clear();
            _hist = [];
            var i : int, j : int;
            for(i = -M;i < N + M;i++){
                _hist[i] = [];
                for(j = -M;j < N + M;j++){
                    _hist[i][j] = null;
                }
            }
            
            _hist[x][y] = [];
            for(i = -1;i <= 1;i++){
                for(j = -1;j <= 1;j++){
                    if(i == 0 && j == 0)continue;
                    if(isValid(x + i, y + j)){
                        if(_places[int(x+i)][int(y+j)] == null){
                            // 隣接駒がなければ
                            _hist[int(x+i)][int(y+j)] = [[i, j, 0]];
                            paintCell(_showLayer.graphics, x+i, y+j, COLORS_CELL[side]);
                        }else{
                            // 隣接駒があれば
                            rec(x, y, i, j, side);
                        }
                    }
                }
            }
        }
        
        // ジャンプ移動可能範囲を探して表示
        private function rec(x : int, y : int, dx : int, dy : int, side : uint) : void
        {
            if(!isValid(x + dx, y + dy))return;
            
            var jx : int = x + 2 * dx;
            var jy : int = y + 2 * dy;
            if(
                _places[x+dx][y+dy] != null && // 飛び越える対象が存在する
                isValid(jx, jy) && // ジャンプ先が有効な範囲内
                _places[jx][jy] == null && // ジャンプ先が空いている
                (!_hist[jx][jy] || _hist[jx][jy].length > _hist[x][y].length + 1) // ジャンプ先への移動履歴がまだないか、これから作られる移動履歴より長い場合
                ){
                _hist[jx][jy] = _hist[x][y].concat([[2*dx, 2*dy, 1]]);
                paintCell(_showLayer.graphics, jx, jy, COLORS_CELL[side]);
                var i : int, j : int;
                for(i = -1;i <= 1;i++){
                    for(j = -1;j <= 1;j++){
                        if(i == -dx && j == -dy)continue;
                        if(i == 0 && j == 0)continue;
                        rec(jx, jy, i, j, side);
                    }
                }
            }
        }
        
        // 1マスを塗る
        private function paintCell(g : Graphics, x : int, y : int, c : uint) : void
        {
            g.lineStyle(1, 0x000000);
            g.beginFill(c);
            var p : Point;
            p = trans(x, y); g.moveTo(p.x, p.y);
            p = trans(x, y+1); g.lineTo(p.x, p.y);
            p = trans(x+1, y+1); g.lineTo(p.x, p.y);
            p = trans(x+1, y); g.lineTo(p.x, p.y);
            p = trans(x, y); g.lineTo(p.x, p.y);
            g.endFill();
        }
        
        // 移動可能範囲かどうか
        private function isValid(x : int, y : int) : Boolean
        {
            if(x >= -M && x < N + M && y >= 0 && y < N)return true;
            if(y >= -M && y < N + M && x >= 0 && x < N)return true;
            return false;
        }
        
        private function tr(...o : Array) : void
        {
            _tf.appendText(o + "\n");
        }
    }
}

import flash.display.*;
import flash.events.*;
import flash.geom.*;

class Robo extends Sprite {
    private var _frame:Bitmap;
    
    // フィールド座標
    public var mx : int;
    public var my : int;
    
    public var side : int; // 0 : 味方, 1 : 敵
    
    // 塗られている色
    private var _color : uint;
    
    public var frames:Array;
    public var dir : int = 0; // left : 0, right : 1
    
    private var _currentFrame:int = 0;
    private var _totalFrames:int = 0;
    
    private var _frameRate:int = 8;
    private var _autoPlay:Boolean = false;
    public function Robo( bmd : BitmapData, frameRate:int = 8, autoPlay:Boolean = false ){
        _frameRate = frameRate;
        _autoPlay = autoPlay;
        cacheAsBitmap = true;
        
        _frame = new Bitmap();
        addChild( _frame );
        
        frames = [[], []];
        
        const frameWidth:int = 20;
        const frameHeight:int = 34;
        
        var numFrames:int = bmd.width/frameWidth;
        for( var i:int=0; i< numFrames; ++i ) {
            for( var f:int=0; f<_frameRate; ++f ) {
                // 順向き
                var frame:BitmapData = new BitmapData( frameWidth, frameHeight, true, 0 );
                var matrix:Matrix = new Matrix();
                matrix.translate( -i*frameWidth, 0 );
                frame.draw( bmd, matrix );
                frames[0].push( frame );
                
                // 逆向き
                var frame2:BitmapData = new BitmapData( frameWidth, frameHeight, true, 0 );
                matrix = new Matrix();
                matrix.translate( -(i+1)*frameWidth, 0 );
                matrix.scale(-1, 1);
                frame2.draw( bmd, matrix );
                frames[1].push( frame2 );
            }
        }
        _totalFrames = frames[0].length;
        
        _color = 0xffffffff;
        
        update();
        
        if( _autoPlay ) play();
    }
    
    // 非透過の白い部分をcで塗る
    public function paint(c : uint) : void
    {
        for each(var frame : Array in frames){
            for each(var bmd : BitmapData in frame){
                bmd.threshold(bmd, bmd.rect, new Point(), "==", _color, c, 0xffffffff, true);
            }
        }
        _color = c;
    }
    
    private function update(e:Event=null):void {
        _frame.bitmapData = frames[dir][_currentFrame];
        _currentFrame = (_currentFrame+1) % _totalFrames;
    }
    
    public function play():void {
        addEventListener( Event.ENTER_FRAME, update );
    }
    public function stop():void {
        removeEventListener( Event.ENTER_FRAME, update );
    }
    
    public function get currentFrame():int { return _currentFrame+1; }
    public function get totalFrames():int { return _totalFrames; }
}

class Algorithm {
    private var _M : uint;
    private var _N : uint;
    
    private var _robos : Array;
    private var _places : Array;
    
    public function Algorithm(M : uint, N : uint, robos : Array, places : Array) : void
    {
        _M = M;
        _N = N;
        _robos = robos;
        _places = places;
    }
    
    // [Robo, Point]
    public function run(side : int) : Array
    {
        var maxScore : Number = Number.MIN_VALUE;
        var bestMovs : Array = [];
        // 各roboについて
        for each(var r : Robo in _robos){
            if(r.side == 1){
                // 移動可能範囲すべてをなめて
                var movs : Array = enumMovable(r.mx, r.my);
                for each(var mov : Point in movs){
                    // もっとも劇的に移動する(ただしゴールに入る移動のスコアは3倍にする)移動を選ぶ
                    var score : Number = mov.y - r.my;
                    if(mov.y >= _N && r.my < _N)score *= 3;
                    
                    if(score > maxScore){
                        maxScore = score;
                        bestMovs = [[r, mov]];
                    }else if(score == maxScore){
                        bestMovs.push([r, mov]);
                    }
                }
            }
        }
        
        if(bestMovs.length == 0)return null; 
        return bestMovs[int(Math.random() * bestMovs.length)]; // 同スコアの中からランダムに選択
    }
    
    // 移動可能範囲の列挙(showMovableの劣化版)
    private function enumMovable(x : int, y : int) : Array
    {
        var i : int, j : int;
        var hist : Array = [];
        for(i = -_M;i < _N + _M;i++){
            hist[i] = [];
            for(j = -_M;j < _N + _M;j++){
                hist[i][j] = null;
            }
        }
        
        for(i = -1;i <= 1;i++){
            for(j = -1;j <= 1;j++){
                if(i == 0 && j == 0)continue;
                if(isValid(x + i, y + j)){
                    if(_places[int(x+i)][int(y+j)] == null){
                        // 隣接駒がなければ
                        hist[int(x+i)][int(y+j)] = 1;
                    }else{
                        // 隣接駒があれば
                        rec(x, y, i, j, hist);
                    }
                }
            }
        }
        
        var ret : Array = [];
        for(i = -_M;i < _N + _M;i++){
            for(j = -_M;j < _N + _M;j++){
                if(hist[i][j])ret.push(new Point(i, j));
            }
        }
        return ret;
    }
   
    // ジャンプ移動可能範囲の列挙
    private function rec(x : int, y : int, dx : int, dy : int, hist : Array) : void
    {
        if(!isValid(x + dx, y + dy))return;
           
        var jx : int = x + 2 * dx;
        var jy : int = y + 2 * dy;
        if(
            _places[x+dx][y+dy] != null && 
            isValid(jx, jy) && 
            _places[jx][jy] == null &&
            !hist[jx][jy]
            ){
            hist[jx][jy] = 1;
            var i : int, j : int;
            for(i = -1;i <= 1;i++){
                for(j = -1;j <= 1;j++){
                    if(i == -dx && j == -dy)continue;
                    if(i == 0 && j == 0)continue;
                    rec(jx, jy, i, j, hist);
                }
            }
        }
    }
    
    private function isValid(x : int, y : int) : Boolean
    {
        if(x >= -_M && x < _N + _M && y >= 0 && y < _N)return true;
        if(y >= -_M && y < _N + _M && x >= 0 && x < _N)return true;
        return false;
    }
}