forked from: 食物連鎖シミュレーション

by WRXSTI forked from 食物連鎖シミュレーション (diff: 198)
肉食動物、草食動物、植物、植物の種子による食物連鎖シミュレーション
ルール
・肉食動物は、草食動物を捕食する。
・草食動物は、植物を捕食する。
・植物は、捕食されると消滅する。
・肉食動物と草食動物は、時間と共にライフが減少する。
・肉食動物は、ライフが0になると植物の種子となる。
・草食動物は、ライフが0、または捕食されると植物の種子となる。
・植物の種子は、一定時間後、植物となる。
・肉食動物と草食動物は、残りライフが少なくなると空腹になる。
・肉食動物は、ランダムに動き回るが、空腹時は視界に入った草食動物へ向かう。
・草食動物は、ランダムに動き回るが、肉食動物が視界に入ると逃げる。しかし、空腹時は視界に入った植物へ向かう。
・肉食動物と草食動物は、空腹時は移動速度が速くなる。しかし、その分ライフも消耗する。また、その時視界に捕食対象がいない時は、ランダムに動き回り対象を探す。
・肉食動物と草食動物は、捕食後、一定時間再捕食しない。
・肉食動物と草食動物は、一定数捕食を行うと、交尾を行い繁殖する。

視線
・白線は、捕食対象への視線を表す。
・桃線は、交尾対象への視線を表す。
・点線は、草食動物が肉食動物から逃げているのを表す。

個体数やルール作り等、色々問題あるかと思うんですが、上手い事ループしてくれないかと。
あと重いです。すいません。
♥0 | Line 389 | Modified 2011-10-19 18:22:51 | MIT License
play

ActionScript3 source code

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

// forked from h6k's 食物連鎖シミュレーション
package {
   
            _aniNum[1] = (int)(_startNum[1].text);
            for (i = 0; i < _aniNum[1]; i++) {
                _ani[i] = new Animal(Math.random() * (_sw - _sz), Math.random() * (_sh - _sz), 0x00FF00, 1, 0);
            }
            // 草食
            _aniNum[2] = _ani.length + (int)(_startNum[2].text);
            for (i = _ani.length; i < _aniNum[2]; i++) {
                _ani[i] = new Animal(Math.random() * (_sw - _sz), Math.random() * (_sh - _sz), 0x0000FF, 500, 3);
            }
            // 肉食
            _aniNum[0] = _ani.length + (int)(_startNum[0].text);
            for (i = _ani.length; i < _aniNum[0]; i++) {
                _ani[i] = new Animal(Math.random() * (_sw - _sz), Math.random() * (_sh - _sz), 0xFF0000, 500, 3);
            }
            
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }
        
        // メインループ
        private function onEnterFrame(e:Event):void {
            _bmd.lock();
            _bmd.fillRect(_bg, 0x000000);
            
            var tar:Object;
            var i:int, j:int, cid:int;
            var k:int, d:int, distance:int;            // 距離計測用
            var len:int = _ani.length;                // 種子~肉食の数
            var nNo:int = len - _aniNum[0];            // 最初の肉食の要素番号
            var sNo:int = _aniNum[3] + _aniNum[1];    // 最初の草食の要素番号
            for (i = 0; i < len; i++) {
                tar = _ani[i];
                if (tar.life > 0) {
                    d = _d;
                    k = 0;
                    switch(tar.col) {
                        case 0xFF0000:
                            // 寿命
                            /*
                            if (++tar.years % 2000 == 0) {
                                tar.life = 0;
                                tar.eat = 0;
                            }
                            */
                            // 描画
                            if (tar.life > tar.hungry) { cid = 0; } else { cid = 3; }
                            _bmd.copyPixels(_circle[cid], _circle[cid].rect, new Point(tar.x, tar.y));
                            
                            // 当たり判定(草食~肉食)
                            for (j = sNo; j < len; j++) {
                                if (_ani[j].life > 0) {
                                    // 相手も生きてる
                                    distance = Math.pow(_ani[j].x - tar.x, 2) + Math.pow(_ani[j].y - tar.y, 2);
                                    if (distance < _sz2) {
                                        // 当たっている
                                        switch(_ani[j].col) {
                                            case 0x0000FF:
                                                // 肉食→草食
                                                if (_frame - tar.eFrame > _eWait) {
                                                    // 前回食べた時から指定フレーム数が経過
                                                    tar.eat++;
                                                    tar.eFrame = _frame;
                                                    tar.life += _rec;
                                                    if (tar.life > tar.max) { tar.life = tar.max; }
                                                    // 種子になる
                                                    changeSeed(_ani[j]);
                                                }
                                                break;
                                            case 0xFF0000:
                                                // 肉食→肉食
                                                if (tar.eat >= tar.ready && _ani[j].eat >= _ani[j].ready && j != i) {
                                                    // お互い交配準備ができてる
                                                    tar.eat = 0;
                                                    _ani[j].eat = 0;
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    //_ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                }
                                                break;
                                        }
                                    }
                                    
                                    // 一番近いのを検索
                                    if((tar.type == _EAT && _ani[j].col == 0x0000FF) || (tar.type == _BIRTH && _ani[j].col == 0xFF0000 && _ani[j].eat >= _ani[j].ready && j != i)) {
                                        if (distance < d) {
                                            d = distance;
                                            k = j;
                                        }
                                    }
                                }
                            }
                            
                            // ある程度近い時だけ方向変更(_RANDOMの時はスルー)
                            if (d < _range) {
                                changeDirection(tar, k);
                                drawView(tar, k);
                            }
                            // 移動
                            move(tar);
                            break;
                        case 0x00FF00:
                            // 描画
                            _bmd.copyPixels(_circle[1], _circle[1].rect, new Point(tar.x, tar.y));
                            break;
                        case 0x0000FF:
                            // 寿命
                            /*
                            if (++tar.years % 2000 == 0) {
                                tar.life = 0;
                                tar.eat = 0;
                            }
                            */
                            // 描画
                            if (tar.life > tar.hungry) { cid = 2; } else { cid = 4; }
                            _bmd.copyPixels(_circle[cid], _circle[cid].rect, new Point(tar.x, tar.y));
                            
                            // 当たり判定
                            for (j = _aniNum[3]; j < len; j++) {
                                if (_ani[j].life > 0) {
                                    // 相手も生きてる
                                    distance = Math.pow(_ani[j].x - tar.x, 2) + Math.pow(_ani[j].y - tar.y, 2);
                                    if (distance < _sz2 && j < nNo) {
                                        // 当たっている(肉食との判定は除く)
                                        switch(_ani[j].col) {
                                            case 0x00FF00:
                                                // 草食→植物
                                                if (_frame - tar.eFrame > _eWait) {
                                                    // 前回食べた時から指定フレーム数が経過
                                                    tar.eat++;
                                                    tar.eFrame = _frame;
                                                    tar.life += _rec;
                                                    if (tar.life > tar.max) { tar.life = tar.max; }
                                                    // 消去
                                                    _ani[j].life = -100;
                                                }
                                                break;
                                            case 0x0000FF:
                                            // 草食→草食
                                                if (tar.eat >= tar.ready && _ani[j].eat >= tar.ready && j != i) {
                                                    tar.eat = 0;
                                                    _ani[j].eat = 0;
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                    _ani[_ani.length] = new Animal(tar.x, tar.y, tar.col, 500, 3);
                                                }
                                                break;
                                        }
                                    }
                                    
                                    // 自分以外で一番近いのを検索
                                    if ((tar.type == _RANDOM && _ani[j].col == 0xFF0000) || (tar.type == _EAT && _ani[j].col == 0x00FF00) || (tar.type == _BIRTH && _ani[j].col == 0x0000FF && _ani[j].eat >= _ani[j].ready && j != i)) {
                                        if (distance < d) {
                                            d = distance;
                                            k = j;
                                        }
                                    }
                                }
                            }
                            
                            // ある程度近い時だけ方向変更
                            if (d < _range) {
                                if (_ani[k].col == 0xFF0000) { tar.type = _RUNAWAY; }
                                changeDirection(tar, k);
                                drawView(tar, k);
                            }
                            // 移動
                            move(tar);
                            break;
                    }
                } else if (tar.col != 0x000000) {
                    // life無し
                    changeSeed(tar);
                } else if (tar.life == 0) {
                    // tar.col = 0x000000
                    _bmd.copyPixels(_circle[5], _circle[5].rect, new Point(tar.x, tar.y));
                    if (tar.wait < _sWait) { tar.wait++; } else {
                        // 指定フレーム経過後、植物になる
                        tar.life = 1;
                        tar.col = 0x00FF00;
                    }
                }
            }
            
            // 配列再生成
            var tmp:Array = [];
            // 描画順にソート
            tmp = tmp.concat(zSort(0));
            _aniNum[3] = tmp.length;
            tmp = tmp.concat(zSort(0x00FF00));
            _aniNum[1] = tmp.length - _aniNum[3];
            tmp = tmp.concat(zSort(0x0000FF));
            _aniNum[2] = tmp.length - _aniNum[3] - _aniNum[1];
            tmp = tmp.concat(zSort(0xFF0000));
            _aniNum[0] = tmp.length - _aniNum[3] - _aniNum[1] - _aniNum[2];
            _ani = tmp;
            /*
            for (i = 0; i < _ani.length; i++) {
                if (_ani[i].life > -10) { tmp.push(_ani[i]); }
            }
            _ani = tmp;
            */
            
            // グラフデータをシフト
            _graph.shift();
            _graph.push([_aniNum[0], _aniNum[1], _aniNum[2], _aniNum[3]]);
            
            // 過去も含めた最大値を取得
            var max:int = -1;
            var tmax:int;
            len = _graph.length;
            for (i = 0; i < len; i++) {
                tmax = Math.max(_graph[i][0], _graph[i][1], _graph[i][2], _graph[i][3]);
                if (max < tmax) { max = tmax; }
            }
            
            // 最大値に合わせて描画
            _bmd.fillRect(_status, 0x333333);
            var x:int, y:int;
            for (i = 0; i < len; i++) {
                x = i + 170;
                y = _gmin - (_gmax * _graph[i][0] / max);
                _bmd.setPixel(x, y, 0xFF0000);
                
                y = _gmin - (_gmax * _graph[i][1] / max);
                _bmd.setPixel(x, y, 0x00FF00);
                
                y = _gmin - (_gmax * _graph[i][2] / max);
                _bmd.setPixel(x, y, 0x0000FF);
                
                y = _gmin - (_gmax * _graph[i][3] / max);
                _bmd.setPixel(x, y, 0x999999);
            }
            _bmd.unlock();
            
            // ステータス
            _frame++;
            var t:uint = getTimer();
            if (t - _sec >= 1000) {
                _fps = Math.round((_frame - _bframe) * 1000 / (t - _sec));
                _bframe = _frame;
                _sec = t;
            }
            _tfld.text = "肉食(赤):" + _aniNum[0] + "\n植物(緑):" + _aniNum[1] + "\n種子(灰):" + _aniNum[3] + "\n草食(青):" + _aniNum[2] + "\nFPS:" + _fps + " / " + stage.frameRate;
        }
        
        // 描画順にソート
        private function zSort(col:uint):Array {
            var tmp:Array = [];
            var tmp2:Array = [];
            var len:int = _ani.length;
            for (var i:int = 0; i < len; i++) {
                if (_ani[i].life != -100) {
                    if (_ani[i].col == col) { tmp.push(_ani[i]); } else { tmp2.push(_ani[i]);  }
                }
            }
            _ani = tmp2;
            return tmp;
        }
        
        // 種子に変わる
        private function changeSeed(tar:Object):void {
            // 上下左右斜めにランダムで増やす
            if (tar.eat > 0) {
                // 左
                if(tar.x - _sz > _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x - _sz, tar.y, 0, 0, 0);
                }
                // 右
                if(tar.x + _sz < _sw - _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x + _sz, tar.y, 0, 0, 0);
                }
                // 上
                if (tar.y - _sz > _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x, tar.y - _sz, 0, 0, 0);
                }
                // 下
                if (tar.y + _sz < _sh - _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x, tar.y + _sz, 0, 0, 0);
                }
                /*
                // 左上
                if(tar.x - _sz > _sz && tar.y - _sz > _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x - _sz, tar.y - _sz, 0, 0, 0);
                }
                // 右上
                if(tar.x + _sz < _sw - _sz&& tar.y - _sz > _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x + _sz, tar.y - _sz, 0, 0, 0);
                }
                // 左下
                if (tar.x - _sz > _sz && tar.y + _sz < _sh - _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x - _sz, tar.y + _sz, 0, 0, 0);
                }
                // 右下
                if (tar.x + _sz < _sw - _sz && tar.y + _sz < _sh - _sz && Math.random() * 2 > 1) {
                    _ani[_ani.length] = new Animal(tar.x + _sz, tar.y + _sz, 0, 0, 0);
                }
                */
            }
            
            tar.life = 0
            tar.col = 0x000000;
            tar.wait = 0;
        }
        
        // 方向変換
        private function changeDirection(tar:Object, k:int):void {
            var diffx:int = tar.x - _ani[k].x;
            var diffy:int = tar.y - _ani[k].y;
            
            if (diffx < 0 && diffy < 0) {
                // 対象が右下
                if (Math.abs(diffx) > Math.abs(diffy)) {
                    // x幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 1;
                            break;
                        case _RUNAWAY:
                            tar.direction = 3;
                            break;
                    }
                } else {
                    // y幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 2;
                            break;
                        case _RUNAWAY:
                            tar.direction = 0;
                            break;
                    }
                }
            } else if (diffx > 0 && diffy < 0) {
                // 対象が左下
                if (Math.abs(diffx) > Math.abs(diffy)) {
                    // x幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 3;
                            break;
                        case _RUNAWAY:
                            tar.direction = 1;
                            break;
                    }
                } else {
                    // y幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 2;
                            break;
                        case _RUNAWAY:
                            tar.direction = 0;
                            break;
                    }
                }
            } else if (diffx < 0 && diffy > 0) {
                // 対象が右上
                if (Math.abs(diffx) > Math.abs(diffy)) {
                    // x幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 1;
                            break;
                        case _RUNAWAY:
                            tar.direction = 3;
                            break;
                    }
                } else {
                    // y幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 0;
                            break;
                        case _RUNAWAY:
                            tar.direction = 2;
                            break;
                    }
                }
            } else if(diffx > 0 && diffy > 0) {
                // 対象が左上
                if (Math.abs(diffx) > Math.abs(diffy)) {
                    // x幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 3;
                            break;
                        case _RUNAWAY:
                            tar.direction = 1;
                            break;
                    }
                } else {
                    // y幅が大きい
                    switch(tar.type) {
                        case _EAT:
                        case _BIRTH:
                            tar.direction = 0;
                            break;
                        case _RUNAWAY:
                            tar.direction = 2;
                            break;
                    }
                }
            }
        }
        
        // 視線描画
        private function drawView(tar:Object, k:int):void {
            switch(tar.type) {
                case _EAT:
                    // ロックオン
                    drawDottedLine(tar.x + _c, tar.y + _c, _ani[k].x + _c, _ani[k].y + _c, 0xFFFFFF, 1);
                    break;
                case _RUNAWAY:
                    // 逃げる
                    drawDottedLine(tar.x + _c, tar.y + _c, _ani[k].x + _c, _ani[k].y + _c, 0xFFFF00, 4);
                    break;
                case _BIRTH:
                    // 交尾
                    drawDottedLine(tar.x + _c, tar.y + _c, _ani[k].x + _c, _ani[k].y + _c, 0xFF00FF, 1);
                    break;
            }
        }
        
        private function drawDottedLine(fromx:int, fromy:int, tox:int, toy:int, col:uint, interval:int):void {
            // y = ax + b
            var a:Number = (toy - fromy) / (tox - fromx);    // 傾き
            var b:Number = fromy - (fromx * a);            // 切片
            var x:int;
            var y:int;
            if (Math.abs(tox - fromx) > Math.abs(toy - fromy)) {
                // X幅が大きい
                if(fromx < tox) {
                    for (x = fromx; x < tox; x += interval) {
                        _bmd.setPixel(x, a * x + b, col);
                    }
                } else {
                    for (x = tox; x < fromx; x += interval) {
                        _bmd.setPixel(x, a * x + b, col);
                    }
                }
            } else {
                // Y幅が大きい
                if (fromy < toy) {
                    for (y = fromy; y < toy; y += interval) {
                        _bmd.setPixel((y - b) / a, y, col);
                    }
                } else {
                    for (y = toy; y < fromy; y += interval) {
                        _bmd.setPixel((y - b) / a, y, col);
                    }
                }
            }
        }
        
        // 移動
        private function move(tar:Object):void {
            // 方向と繰り返し回数を決定
            if (--tar.repeat < 0) {
                tar.direction = Math.random() * 5;
                tar.repeat = Math.random() * 100;
            }
            
            if (tar.life < tar.hungry) {
                // 空腹時はスピードアップ
                tar.dx = tar.dy = 2;
                tar.life -= 2;
                tar.type = _EAT;
                // 強制的に動かす
                if (tar.direction == 4) { tar.direction = Math.random() * 4; }
            } else {
                // 普段
                tar.dx = tar.dy = 1;
                tar.life--;
                if (tar.eat >= tar.ready) { tar.type = _BIRTH; } else { tar.type = _RANDOM; }
            }
            
            switch(tar.direction) {
                case 0:
                    // 上
                    if (tar.y > tar.dy) { tar.y -= tar.dy; }
                    break;
                case 1:
                    // 右
                    if (tar.x < _sw - _sz) { tar.x += tar.dx; }
                    break;
                case 2:
                    // 下
                    if(tar.y < _sh - _sz) { tar.y += tar.dy; }
                    break;
                case 3:
                    // 左
                    if (tar.x > tar.dx) { tar.x -= tar.dx; }
                    break;
            }
        }
    }
}

class Animal
{
    public var x:int, y:int, col:uint, life:int, max:int, hungry:int, ready:int;
    public var direction:int, repeat:int, eat:int, eFrame:uint, years:uint;
    public var type:int, dx:int, dy:int, wait:int;
    public function Animal(x:int, y:int, col:uint, life:int, ready:int):void {
        this.x = x;                // 座標
        this.y = y;
        this.col = col;            // 色
        this.life = life;        // ライフ
        this.max = life;            // MAX
        this.hungry = life / 3;    // 空腹
        this.ready = ready;        // 交尾までの捕食数
        this.direction = 0;        // 方向
        this.repeat = 0;            // 移動の繰り返し回数
        this.eat = 0;            // 捕食数
        this.eFrame = 0;            // 捕食時のフレーム
        this.years = 0;            // 寿命
    }
}