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

GAで固定弾幕回避学習

右側の欄に追尾弾の情報をいれてStart To Learn.

評価値 : 100 / (1 + 当たった回数 + 0.001*(ストローク数-1) + 0.00001*(非0の個数))\n"
0が不移動, 1が右で、以降反時計回りに動きを割り当てている。
50点より上はノーミス。実際はコードに従って動きを3フレーム毎に変化させる。
3*300フレーム分を学習。
Get Adobe Flash player
by uwi 04 Nov 2009
/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/6pl1
 */

package {
    import com.flashdynamix.utils.SWFProfiler;
    import flash.text.TextField;
    import flash.display.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.ui.*;
    import com.bit101.components.*;
    
    // 右側の欄に追尾弾の情報をいれてStart To Learn.
    // 
    // 評価値 : 100 / (1 + 当たった回数 + 0.001*(ストローク数-1) + 0.00001*(非0の個数))\n"
    // 0が不移動, 1が右で、以降反時計回りに動きを割り当てている。
    // 50点より上はノーミス。実際はコードに従って動きを3フレーム毎に変化させる。
    // 3*300フレーム分を学習。

    [SWF(backgroundColor="#000000", frameRate="30")]
    public class GATest extends Sprite {
        private var _bullets : Array;
        private var _myx : Point;
        private var _nhit : int;
        private const R_BULLET : Number = 10.0;
        private const R_ME : Number = 5.0;
        private const INFD : Number = (R_BULLET + R_ME) * (R_BULLET + R_ME);
        
        private var _tf : TextField;
        private var _tfinput : TextField;
        private var _submit : PushButton;
        private var _stop : PushButton;
        
        private var _shotPattern : Array;
        private var _av : GALearner;
        
        private var W : Number = 400;
        private var H : Number = 465;
        
        private var _state : int;
        
        public function GATest() {
            Wonderfl.capture_delay(5);
            SWFProfiler.init(this);
            
            var tfinputhead : TextField = new TextField();
            setParams(tfinputhead, {
                text : "x y v interval",
                textColor : 0xffffff,
                borderColor : 0xffffff,
                border : true,
                x : 370,
                y : 0,
                width : 90,
                height : 20
            });
            addChild(tfinputhead);
            
            _tfinput = new TextField();
            setParams(_tfinput, {
                type : "input",
                text : "0 0 10 6\n150 0 10 6\n400 0 10 6",
                textColor : 0xffffff,
                borderColor : 0xffffff,
                border : true,
                x : 370,
                y : 20,
                width : 90,
                height : 200
            });
            addChild(_tfinput);
            
            _submit = new PushButton(this, 370, 250, "Start To Learn", onSubmit);
            _submit.width = 90;

            _stop = new PushButton(this, 370, 270, "Stop/Resume Learning", onStop);
            _stop.width = 90;

            // デバッグ用
            _tf = new TextField();
            setParams(_tf, {
                autoSize : "left",
                textColor : 0xffffff,
                borderColor : 0xffffff,
                border : true
            });
            addChild(_tf);
            
            _state = 0;
        }
        
        private static function setParams(t : Object, v : Object) : Object
        {
            for(var k : String in v){
                t[k] = v[k];
            }
            return t;
        }
        
        private function onSubmit(e : MouseEvent) : void
        {
            removeEventListener(Event.ENTER_FRAME, onLearnStep);
            
            init();
            learn();
        }
        
        private function onStop(e : MouseEvent) : void
        {
            if(_state == 0)return;
            if(hasEventListener(Event.ENTER_FRAME)){
                removeEventListener(Event.ENTER_FRAME, onLearnStep);
            }else{
                addEventListener(Event.ENTER_FRAME, onLearnStep);
            }
        }
        
        private function init() : void
        {
            _state = 1;

            _myx = new Point(W / 2, H / 2);
            _bullets = [];
            _nhit = 0;
            _shotPattern = [];
            for each(var line : String in _tfinput.text.split(/[\r\n]/)){
                var seg : Array = line.split(' ');
                if(seg.length == 4){
                    if(int(seg[3]) > 0 && Number(seg[2]) > 0){
                        _shotPattern.push({
                            x : Number(seg[0]),
                            y : Number(seg[1]),
                            v : Number(seg[2]),
                            interval : int(seg[3])
                        });
                    }
                }
            }
        }
        
        private function learn() : void
        {
             _av = new GALearner(300, 20, simulate, _tf);
             _av.init();
             addEventListener(Event.ENTER_FRAME, onLearnStep);
             _g = 0;
        }
        
        private var _g : int;
        
        private function onLearnStep(e : Event) : void
        {
             for(var i : int = 0;i < 2;i++){
                 _g++;
                 _av.evolve();
             }
             
             var elstr : String = _av.getElite().code.join('');
             
             _tf.text = "" + 
                 _av.getScores().join('\n') + "\n" + 
                 "step : " + _g + "\n" + 
                 elstr.substring(0, 60) + "\n" + 
                 elstr.substring(60, 120) + "\n" +
                 elstr.substring(120, 180) + "\n" +
                 elstr.substring(180, 240) + "\n" +
                 elstr.substring(240, 300) + "\n"
                 ;
        }
        
        private const ST : Array = [
            [0, 0],
            [6, 0], [4, -4], [0, -6], [-4, -4],
            [-6, 0], [-4, 4], [0, 6], [4, 4]
            ];
        
        // 0 : no move
        // 1 : R から左回り
        private function simulate(code : Array) : Number
        {
            init();
            var t : int;
            var prev : int = -1;
            var nOperate : int = -1;
            var nNon0 : int = 0;
            for(t = 0;t < 600;t++){
                judge();
                
                for each(var ptn : Object in _shotPattern){
                    if(t % ptn.interval == 0){
                        addBullet(ptn.x, ptn.y, ptn.v);
                    }
                }
                
                _myx.x += ST[code[int(t / 2)]][0];
                _myx.y += ST[code[int(t / 2)]][1];
                
                moveBullets();
                
                if(prev != code[t])nOperate++;
                if(code[t] != 0)nNon0++;
                prev = code[t];
            }
            
//            _tf.appendText("" + _nhit + "\n");
            return 100 / (1 + _nhit + nOperate * 0.001 + nNon0 * 0.0000001);
        }
        
        private function moveBullets() : void
        {
            // 弾
            for each(var b : Bullet in _bullets){
                b.xx += b.vx;
                b.xy += b.vy;
            }
        }
        
        // 弾削除
        private function removeBullet(i : int) : void
        {
            if(i < _bullets.length - 1){
                _bullets[i] = _bullets.pop();
            }else{
                _bullets.pop();
            }
        }
        
        // 当たり判定
        private function judge() : void
        {
            for(var i : int = _bullets.length - 1;i >= 0;i--){
                var b : Bullet = _bullets[i];
                if(
                    (b.xx - _myx.x) * (b.xx - _myx.x) + 
                    (b.xy - _myx.y) * (b.xy - _myx.y)
                    < INFD){
                        _nhit++;
                        removeBullet(i);
                        continue;
                }
                if(b.xx < 0 || b.xx > W || b.xy < 0 || b.xy > H){
                    removeBullet(i);
                }
            }
            if(_myx.x < 0 || _myx.x > W || _myx.y < 0 || _myx.y > H){
                _nhit+=10;
                _myx.x = W / 2;
                _myx.y = H / 2;
            }
        }
        
        // 弾追加
        private function addBullet(x : Number, y : Number, v : Number) : void
        {
            var r : Number = Math.sqrt((_myx.x - x) * (_myx.x - x) + (_myx.y - y) * (_myx.y - y));

            var b : Bullet = new Bullet();
            b.xx = x;
            b.xy = y;
            b.vx = v * (_myx.x - x) / r;
            b.vy = v * (_myx.y - y) / r;
            b.r = R_BULLET;
            _bullets.push(b);
        }        
    }
}

class Bullet
{
    public var xx : Number;
    public var xy : Number;
    public var vx : Number;
    public var vy : Number;
    public var r : Number;
}

import flash.text.TextField;

class GALearner
{
    private var _cur : Array;
    
    private var _N : int; // 集団の個体数
    private var _T : int; // シミュレートする時間
    private var _eval : Function;
    
    private var _deb : TextField;
    
    public function GALearner(T : int, N : int, eval : Function, deb : TextField)
    {
        _deb = deb;
        
        _T = T;
        _N = N;
        _cur = [];
        _eval = eval;
    }
    
    public function init() : void
    {
        var i : int;
        for(i = 0;i < _N;i++){
            _cur.push(generate());
        }
    }
    
    // 生成
    private function generate() : Object
    {
        var code : Array = new Array(_T);
        var i : int;
        for(i = 0;i < _T;i++){
            code[i] = int(Math.random() * 9);
        }
        return {code : code, score : _eval(code)};
    }
    
    // 進化
    public function evolve() : void
    {
        var next : Array = [];
        next.push(getElite());
        // 交叉で順当に強くなるとは考えにくい問題なので、
        // エリートは1個体だけ残して多様性を重視
        for(var i : int = 1;i < _N;i++){
            var p : Number = Math.random();
            if(p < 0.02){
                next.push(mutate());
            }else{
                next.push(cross());
            }
        } 
        _cur = next;
    }
    
    // エリート取得
    public function getElite() : Object
    {
        var i : int;
        var elite : int = -1;
        var maxscore : Number = 0.0;
        for(i = 0;i < _N;i++){
            if(_cur[i].score > maxscore){
                maxscore = _cur[i].score;
                elite = i;
            }
        }
        return _cur[elite];
    }
    
    /*
    // 選択(ルーレット法)
    private function select() : int
    {
        var sum : Array = new Array(_N);
        var i : int;
        var s : Number = 0;
        for(i = 0;i < _N;i++){
            s += 1 / _cur[i].score;
            sum[i] = s;
        }
        
        var r : Number = Math.random() * sum[_N - 1];
        for(i = 0;i < _N && r >= sum[i];i++);
        return i;
    }
    */
    
    // 選択(ランキング法)
    private function select() : int
    {
        var p : Number = Math.random();
        var i : int;
        for(i = 0;i < _N - 1;i++){
            if(p > 1 - 0.3){
                break;
            }
            p /= 0.3; 
        }
        return i;
    }
    
    // 交叉
    private function cross() : Object
    {
        var a : int, b : int;
        a = Math.random() * _N;
        b = Math.random() * _N;
        
        /*
        var p : int = Math.random() * _T;
        var q : int = Math.random() * _T;
        if(q < p){
            var w : int = p; p = q; q = w;
        }
        */
         
//        _deb.appendText("" + a + "\t" + b + "\t" + p + "\t" + _cur.length + "\n");

        // 一点交叉
//        var code : Array = _cur[a].code.slice(0, p).concat(_cur[b].code.slice(p));
        // 二点交叉
//        var code : Array = _cur[a].code.slice(0, p).concat(_cur[b].code.slice(p, q)).concat(_cur[a].code.slice(q));
         
        // 一様交叉
        var i : int;
        var code : Array = new Array(_T);
        for(i = 0;i < _T;i++){
            code[i] = Math.random() < 0.5 ? _cur[a].code[i] : _cur[b].code[i];
        }
        
        return {code : code, score : _eval(code)};
    }
    
    // 突然変異
    private function mutate() : Object
    {
        var a : int = Math.random() * _N;
        
        var code : Array = _cur[a].code.concat();
        for(var i : int = 0;i < 10;i++){
            var p : int = Math.random() * _T;
            code[p] = int(Math.random() * 9);
        }
        
        return {code : code, score : _eval(code)};
    }
    
    // スコア配列を取得
    public function getScores() : Array
    {
        var ret : Array = [];
        var i : int;
        for(i = 0;i < _N;i++){
            ret.push(_cur[i].score);
        }
        ret.sort(Array.NUMERIC | Array.DESCENDING);
        return ret;
    }
    
    public function get Cur() : Array
    {
        return _cur;
    }
}