GAで固定弾幕回避学習
右側の欄に追尾弾の情報をいれてStart To Learn.
評価値 : 100 / (1 + 当たった回数 + 0.001*(ストローク数-1) + 0.00001*(非0の個数))\n"
0が不移動, 1が右で、以降反時計回りに動きを割り当てている。
50点より上はノーミス。実際はコードに従って動きを3フレーム毎に変化させる。
3*300フレーム分を学習。
/**
* 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;
}
}