Tron

by h_kamizono forked from test (diff: 597)
♥2 | Line 453 | Modified 2010-09-14 21:17:49 | MIT License
play

ActionScript3 source code

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

package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    import flash.display.Graphics;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    import flash.trace.Trace;
    
    import flash.utils.setTimeout;
    
    /**
     * 
     * ゲームのAIを作成してどのAIが強いかを競う大会 Google AI Challenge)
     * で使われていたゲームをActionScript3.0で作ってみました。
     * 
     * ゲーム
     *     へびゲーム
     *     ( http://ja.wikipedia.org/wiki/%E3%83%98%E3%83%93%E3%82%B2%E3%83%BC%E3%83%A0 )
     *     とほぼおなじ内容です。壁、相手と自分(相手)が通った道とおなじマスに行かないように移動してください。
     *     プレイヤー1は赤色で、先頭が濃い赤で通ったところは薄い赤で描画されます。
     *     プレイヤー2は緑色で、先頭が濃い緑で通ったところは薄い緑で描画されます。
     * 
     * 操作
     *  テンキーの十字で上なら↑、右なら→、下なら↓、左なら←で移動できます。
     * 
     * AIにかんして
     *  AIを改造したい場合は、MyTronBotクラスのmakeMoveを変えるだけでいいです。
     *  AI同士を戦わせるにはPlayerクラスのところを、新たに作成したMyTronBotクラスで置き換えればいいです。
     * 
     * @author h_kamizono
     */
    [SWF(width="465",height="465",backgroundColor="#ffffff")]
    
    public class Main extends Sprite 
    {
        private var _map:Map = new Map();
        private var _text:Text;
        
        private var bot1p:MyTronBot;
        private var bot2p:MyTronBot;
        
        private var _player:Player;
        
        private var _prepos1p:Node;
        private var _prepos2p:Node;
        
        private var _result:int;
        
        // ゲームスピードの設定、小さいほどはやい
        private var _timer:Timer = new Timer(90);
        
        // nodeをremoveするための配列
        private var _nodes:Array;
        
        public function Main():void 
        {    
            _text = new Text();
            _text.display(this, "");
            
            gameInit();
            gameStart();
        }
        
        private function gameInit():void
        {
            _map.init();
            _text.setText("自分が通ったところと相手が通ったところは\n壁とおなじなのでぶつからないように避けましょう。");
            // botのインスタンス作成
            // bot同士戦わせるには下のコメントアウトを消して、_playerの行を消す
            //bot1p = new MyTronBot();
            bot2p = new MyTronBot();
            
            // 人がプレイする時は1pで固定
            _player = new Player();
            stage.focus = _player;
            
            _prepos1p = null;
            _prepos2p = null;
            _result = 0;
            _nodes = new Array();
        }
        
        private function gameStart():void
        {
            // 初期画面描画
            draw();
            
            // ゲーム開始
            _timer.addEventListener(TimerEvent.TIMER, gameLoop);
            _timer.start();
        }
        
        private function draw():void
        {
            for (var i:int = 0; i < _map.height; i++) 
            {
                for (var j:int = 0; j < _map.width; j++) 
                {
                    var node:Node = new Node(j, i);
                    // プレイヤー1を描画
                    if (_map.opX() == j && _map.opY() == i) 
                    {
                        node.draw(0xcc6666);
                    }
                    // プレイヤー2を描画
                    else if (_map.tpX() == j && _map.tpY() == i)
                    {
                        node.draw(0x66cc66);
                    }
                    else
                    {
                        // 壁を描画
                        if (_map.walls[j][i])
                        {
                            node.draw(0x111111);
                        } 
                        // 何もないところは真白で描画
                        else
                        {
                            node.draw(0xffffff);
                        }
                    }
                    addChild(node);
                    _nodes.push(node);
                }
            }
        }
        
        private function gameLoop(event:TimerEvent):void
        {
            var od:int = _player.makeMove();
            // botの移動方向決定、bot同士で戦わせるには、上のプレイヤーのをコメントアウト
            // 下のコメントアウトを消す
            //var od:int = bot1p.makeMove(_map);
            var td:int = bot2p.makeMove(_map.swapPlayers());
            
            // 1p,2pの移動
            var om:int = _map.move1p(od);
            var tm:int = _map.move2p(td);
            
            // お互いぶつかったのでドロー
            if (_map.opX() == _map.tpX() && _map.opY() == _map.tpY()) 
            {
                gameOver(1);
            }
            // お互い壁にぶつかったのでドロー
            else if (om < 0 && tm < 0) 
            {
                gameOver(1);
            }
            // プレイヤー2の勝ち
            else if (om < 0) 
            {
                gameOver(2);
            }
            // プレイヤー1の勝ち
            else if (tm < 0) 
            {
                gameOver(3);
            }
            // 描画更新
            update();    
        }
        
        private function gameOver(i:int):void
        {
            _result = i;
            // ゲーム終了
            _timer.removeEventListener(TimerEvent.TIMER, gameLoop);
            
            _text.setText("");
            
            switch (_result) 
            {
                // Draw
                case 1:
                    _text.setText("Draw  \n Enter キーで再スタートできます。");
                    break;
                // 2p勝利
                case 2:
                    _text.setText("2P Win \n Enter キーで再スタートできます。");
                    break;
                // 1p勝利    
                case 3:
                    _text.setText("1P Win \n Enter キーで再スタートできます。");
                    break;
                default:
                    break;
            }
            
            stage.focus = stage;
            stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void 
            {
                if (e.keyCode == Keyboard.ENTER)
                {   
                    
                    restart();
                }
            });
        }
        
        private function update():void
        {
            var pos1p:Node = new Node(_map.opY(), _map.opX());
            pos1p.draw(0xff0000);
            addChild(pos1p);
            _nodes.push(pos1p);
            
            if (_prepos1p)
            {
                _prepos1p.draw(0xcc6666)
                addChild(_prepos1p);
            }
            _prepos1p = pos1p;
            
            var pos2p:Node = new Node(_map.tpY(), _map.tpX());
            pos2p.draw(0x339933);
            addChild(pos2p);
            _nodes.push(pos2p);
            
            if (_prepos2p)
            {
                _prepos2p.draw(0x66cc66)
                addChild(_prepos2p);
            }
            _prepos2p = pos2p;
        }
        
        private function restart():void
        {
            for (var i:int = 0; i < _nodes.length; i++) 
            {
                removeChild(_nodes[i]);
                _nodes[i] = null;
            }
            
            gameInit();
            gameStart();
        }
    }
}

import flash.display.Graphics;
import flash.trace.Trace;
import flash.geom.Point;
class Map {
    
    // マップの幅と高さ
    private var _width:int = 15;
    private var _height:int = 15;
    
    // マップにある壁用の変数
    private var _walls:Vector.<Vector.<Boolean>>;

    
    // プレイヤーの位置を保存
    private var _opLocation:Point;
    private var _tpLocation:Point;
    /*
     * マップ用のデータ
     * wは壁
     * 1はプレイヤー1(1つだけ)
     * 2はプレイヤー2(1つだけ)
     */
    private var _maps:Vector.<String> = 
         Vector.<String>(["wwwwwwwwwwwwwww",
                          "w1            w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w             w",
                          "w            2w",
                          "wwwwwwwwwwwwwww"]);
    
    public function Map() 
    {
        
    }
    
    public function init():void
    {
        _walls = new Vector.<Vector.<Boolean>>();
        for (var i:int = 0; i < height; i++) 
        {
            var tmp:Vector.<Boolean> = new Vector.<Boolean>();
            for (var j:int = 0; j < width; j++) 
            {
                tmp.push(false);
            }
            _walls.push(tmp);
        }
        readMapData();
    }

    public function get width():int 
    {
        return _width;
    }

    public function get height():int
    {
        return _height;
    }
    
    public function get walls():Vector.<Vector.<Boolean>>
    {
        return _walls;
    }

    // i,jにあるマスが壁かどうか
    public function isWall(i:int, j:int):Boolean
    {
        if (i <= 0 || j <= 0 || i >= width - 1 || j >= height - 1)
            return true;
        else
            return _walls[i][j];
    }

    public function opX():int
    {
        return _opLocation.x;
    }

    public function opY():int
    {
        return _opLocation.y;
    }

    public function tpX():int
    {
        return _tpLocation.x;
    }

    public function tpY():int
    {
        return _tpLocation.y;
    }

    public function readMapData():void
    {
        // プレイヤー1,2は1人だけかどうかのフラグ
        var flag1:Boolean = false;
        var flag2:Boolean = false;
        var w:int = 0;
        var h:int = 0;
        do 
        {
            if (w >= width || h >= height) break;
            var s:String = _maps[w].charAt(h);
            switch (s) 
            {
                case "\n": continue;

                case " ": // ' '
                    _walls[w][h] = false;
                    break;

                case "w": // 'w'
                    _walls[w][h] = true;
                    break;

                case "1": // '1'
                    if (flag1)
                    {
                        trace("ERROR プレイヤー1が1人でないです。");
                    }
                    _walls[w][h] = true;
                    _opLocation = new Point(w, h);
                    flag1 = true;
                    break;

                case "2": // '2'
                    if (flag2)
                    {
                        trace("ERROR プレイヤー2が1人でないです。");
                    }
                    _walls[w][h] = true;
                    _tpLocation = new Point(w, h);
                    flag2 = true;
                    break;
                    
                default:
                    trace(s, " そんな文字はいらないです。");
                    break;
            }
            w++;
            if (w >= width)
            {
                h++;
                w = 0;
            }
        } while (true);
            
    }

    public function move1p(i:int):int
    {
        var x:int = _opLocation.x;
        var y:int = _opLocation.y;
        _walls[x][y] = true;
        switch (i) 
        {
            case 1: // 左に
            _opLocation = new Point(x, y - 1);
            break;
            
            case 2: // 下に
            _opLocation = new Point(x + 1, y);
            break;

            case 3: // 右に
            _opLocation = new Point(x, y + 1);
            break;

            case 4: // 上に
            _opLocation = new Point(x - 1, y);
            break;

            default:
            return -1;
        }
        x = _opLocation.x;;
        y = _opLocation.y;
        return !isWall(x, y) ? 0 : -1;
    }
    
    public function move2p(i:int):int
    {
        var x:int = _tpLocation.x;
        var y:int = _tpLocation.y;
   
        _walls[x][y] = true;
        switch (i) 
        {
            case 1: // 左に
            _tpLocation = new Point(x, y - 1);
            break;
            
            case 2: // 下に
            _tpLocation = new Point(x + 1, y);
            break;

            case 3: // 右に
            _tpLocation = new Point(x, y + 1);
            break;

            case 4: // 上に
            _tpLocation = new Point(x - 1, y);
            break;

            default:
            return -1;
        }
        x = _tpLocation.x;;
        y = _tpLocation.y;
        return !isWall(x, y) ? 0 : -1;
    }

    public function swapPlayers():Map 
    {
        var map:Map = new Map();
        map._walls = this._walls;
        map._width = this._width;
        map._height = this._height;
        map._opLocation = this._tpLocation;
        map._tpLocation = this._opLocation;
        return map;
    }
    
}

// Bot用のクラス。Botを変えるときは、makeMoveの中身を変えればいい
// 今のままだと壁に当たらないように移動するだけなので弱い
class MyTronBot 
{
    public function makeMove(m:Map):int 
    {
        var i:int = m.opX();
        var j:int = m.opY();
        if (!m.isWall(i, j - 1))
            return 1;
        if (!m.isWall(i, j + 1))
            return 3;
        if (!m.isWall(i + 1, j))
            return 2;
        if (!m.isWall(i - 1, j))
            return 4;
        else
            return 1;
    }
}

// プレイヤー用のクラス、switchがいいのかArrayを使った今のがいいのか
import flash.events.KeyboardEvent;
class Player extends Sprite
{
    private var _keytable:Array = [];
    private const VK_LEFT:int = 37;
    private const VK_UP:int = 38;
    private const VK_RIGHT:int = 39;
    private const VK_DOWN:int = 40;
    //private const VK_SPC:int = 32;
    
    private var _direction:int = 2;
    
    public function Player()
    {
        _keytable[VK_LEFT] = 1;
        _keytable[VK_DOWN] = 2;
        _keytable[VK_RIGHT] = 3;
        _keytable[VK_UP] = 4;
        
        this.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void 
        {
            // キーボードの入力が_keytableにあり、逆方向の入力でないなら方向を変える
            if (_keytable[e.keyCode] &&
                (_keytable[e.keyCode] != _direction + 2 && _keytable[e.keyCode] != _direction - 2)) 
            {   
                _direction = _keytable[e.keyCode];
            }
        });
    }
    
    public function set direction(d:int):void
    {
        _direction = d;
    }
    
    public function makeMove():int
    {
        return _direction;
    }
}

import flash.display.Sprite;
// snamuraさんのクラス参照し、少しいじりました。
// 経路探索のためのノードクラス
internal class Node extends Sprite {

    // ノードX座標
    internal var fieldX:int;
    // ノードY座標
    internal var fieldY:int;
    
    public function Node(fieldX:int, fieldY:int)
    {
        this.fieldX = fieldX;
        this.fieldY = fieldY;
        this.x = fieldX * 20 + 70;
        this.y = fieldY * 20 + 70;
    }
    
    public function draw(background:uint):void
    {
        graphics.clear();
        graphics.lineStyle(1);
        graphics.beginFill(background);
        graphics.drawRect(0,0,20,20);
    }
}

import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;

// 文字を表示するためのクラス
class Text 
{
    private var _tf:TextField = null;
    public function Text()
    {
        _tf = new TextField();
        var format:TextFormat = new TextFormat();
        _tf.x = 100;
        format.size = 15; // 
        format.color = 0x000000; 
        format.font = "Arial"; 
        _tf.defaultTextFormat = format;
    }
    
    public function display(sp:Sprite, text:String):void
    {
        _tf.text = text;
        _tf.autoSize = TextFieldAutoSize.LEFT;
        sp.addChild(_tf);
    }
    
    public function setText(string:String):void
    {
        _tf.text = string;
    }
    
}