Tic-tac-toe ○×ゲーム Level 0

by hermit
Tic-tac-toe, ○×ゲーム, 三目並べ.
*  since 2010-06-24
*
* TODO
* [ ] レベル選択機能
* [ ] level 1: ランダムに取る
* [ ] level 2: 考えて取る
* [ ] ボタンにフォーカスしたら目立たせる(白っぽくする?)
* [ ] 枠○×を「ぼわっと光った線」で描画する
* [ ] 勝敗がついた行・列を強く光らせる
* [ ] 枡を取る時の表現を「ぼわっと光る」ものにする
* [ ] ダイアログに影をつける
* [ ] 先攻を×にする
* [ ] 得点記録
* [ ] Flash Builderから実行した時、意図せず拡大されて下にはみ出すのを解決する
* [ ] 広告を入れる
* [ ] BetweenAS3を使ってみる    http://www.be-interactive.org/index.php?itemid=472
*                                http://wonderfl.kayac.com/code/7e3f080227a081bafbcdb09dd898b27c057cbf76
* [/] リプレイ時に左上に○と×が重なって表示されるバグを解決
* [/] 勝敗を大きく表示する
* [/] リプレイ機能
♥0 | Line 315 | Modified 2010-07-05 14:16:47 | MIT License
play

ActionScript3 source code

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

/**
 * Tic-tac-toe, ○×ゲーム, 三目並べ.
 *  since 2010-06-24
 *
 * TODO
 * [ ] レベル選択機能
 * [ ] level 1: ランダムに取る
 * [ ] level 2: 考えて取る
 * [ ] ボタンにフォーカスしたら目立たせる(白っぽくする?)
 * [ ] 枠○×を「ぼわっと光った線」で描画する
 * [ ] 勝敗がついた行・列を強く光らせる
 * [ ] 枡を取る時の表現を「ぼわっと光る」ものにする
 * [ ] ダイアログに影をつける
 * [ ] 先攻を×にする
 * [ ] 得点記録
 * [ ] Flash Builderから実行した時、意図せず拡大されて下にはみ出すのを解決する
 * [ ] 広告を入れる
 * [ ] BetweenAS3を使ってみる    http://www.be-interactive.org/index.php?itemid=472
 *                                http://wonderfl.kayac.com/code/7e3f080227a081bafbcdb09dd898b27c057cbf76
 * [/] リプレイ時に左上に○と×が重なって表示されるバグを解決
 * [/] 勝敗を大きく表示する
 * [/] リプレイ機能
 */

package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;

    [SWF(backgroundColor = 0)]

    public class Main extends Sprite
    {
        private var dialog:Dialog =
            new Dialog('Who marks first?',
                        'You', you1st, 'Me', me1st);

        public function Main()
        {
            game = this;

            addChild(board);
            //addChild(msgArea);
            addChild(dialog);
        }

        private function you1st(e:MouseEvent):void
        {
            removeChild(dialog);
            board.clear();
            board.enableBoxButtons();
        }

        private function me1st(e:MouseEvent):void
        {
            removeChild(dialog);
            board.clear();
            board.myTurn();
            board.enableBoxButtons();
        }

        public function replay():void
        {
            addChild(dialog);
        }
    }
}

var game:Main;
const board:Board = new Board;

///**
// * デバッグメッセージ表示領域
// */
//const msgArea:MessageArea = new MessageArea;

// ゲーム本体 ///////////////////////////////////////////////

const T:uint = 10;        // thickness 枠線、○、×の太さ
const UNIT:uint = 148;    // 桝目の大きさ    (stage.stageWidth - 5*2 - 10) / 3

import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.filters.GlowFilter;
import flash.utils.Timer;

class Board extends Sprite
{
    private const boxes:Array = [[], [], []];
    private const resultMsg:ResultMsg = new ResultMsg;

    public function Board()
    {
        drawGrid();

        for (var i:uint = 0; i < 3; ++i)
            for (var j:uint = 0; j < 3; ++j) {
                boxes[i][j] = new Box;
                boxes[i][j].x = T + UNIT * i;
                boxes[i][j].y = T + UNIT * j;
                addChild(boxes[i][j]);
            }
    }

    public function clear():void
    {
        for each (var line:Array in boxes)
            for each (var box:Box in line)
                box.clear();
    }

    /**
     * 枠を描く
     */
    private function drawGrid():void
    {
        x = y = 5;    // padding... 座標をここに書くのは変

        //filters = [new BlurFilter(2, 2)];
        //filters = [new GlowFilter(0xffffff)];
        with (graphics) {
            lineStyle(T, 0xffffff);
            moveTo(T/2 + UNIT,     T/2);
            lineTo(T/2 + UNIT,     T/2 + UNIT * 3);
            moveTo(T/2 + UNIT * 2, T/2);
            lineTo(T/2 + UNIT * 2, T/2 + UNIT * 3);
            moveTo(T/2,            T/2 + UNIT);
            lineTo(T/2 + UNIT * 3, T/2 + UNIT);
            moveTo(T/2,            T/2 + UNIT * 2);
            lineTo(T/2 + UNIT * 3, T/2 + UNIT * 2);
        }
    }

    /**
     * コンピュータが一手進める
     */
    public function myTurn():void
    {
        // Level 0: 端から取っていく
        for each (var line:Array in boxes)
            for each (var box:Box in line)
                if (box.status == Box.NONE) {
                    box.mark();
                    return;
                }
    }

    public function enableBoxButtons():void
    {
        for each (var line:Array in boxes)
            for each (var box:Box in line)
                if (box.status == Box.NONE)
                    box.enableButton();
    }

    public function disableBoxButtons():void
    {
        for each (var line:Array in boxes)
            for each (var box:Box in line)
                if (box.status == Box.NONE)
                    box.disableButton();
    }

    /**
     * 勝敗判定.
     *
     * 勝敗をbox.status形式で返す。
     */
    private function check():uint
    {
        for (var i:uint = 0; i < 3; ++i) {
            if (boxes[i][0].status != Box.NONE &&
                boxes[i][0].status == boxes[i][1].status &&
                boxes[i][0].status == boxes[i][2].status)
                return boxes[i][0].status;
            if (boxes[0][i].status != Box.NONE &&
                boxes[0][i].status == boxes[1][i].status &&
                boxes[0][i].status == boxes[2][i].status)
                return boxes[0][i].status;
        }
        if (boxes[0][0].status != Box.NONE &&
            boxes[0][0].status == boxes[1][1].status &&
            boxes[0][0].status == boxes[2][2].status)
            return boxes[0][0].status;
        if (boxes[0][2].status != Box.NONE &&
            boxes[0][2].status == boxes[1][1].status &&
            boxes[0][2].status == boxes[2][0].status)
            return boxes[0][2].status;
        return Box.NONE;
    }

    /**
     * 判定して、勝敗がついていたら結果を表示する.
     *
     * メソッド名がよくない。
     */
    public function checkAndNext():uint
    {
        var result:uint = check();
        switch (result) {
            case Box.NONE:
                return result;

            case Box.COMPUTER:
                resultMsg.display('You lost!');
                break;
            case Box.PLAYER:
                resultMsg.display('You won!');
        }
        disableBoxButtons();
        return result;
    }
}

/**
 * 桝目, grid.
 *
 * TODO
 * [ ] buttonModeをクラス外から直接変更できない様にする
 */
class Box extends Sprite
{
    public static const NONE:uint     = 0;
    public static const COMPUTER:uint = 1;
    public static const PLAYER:uint   = 2;

    public var status:uint = NONE;
    function Box()
    {
        clear();

        //addEventListener(Event.ADDED, function():void {
        //    width = height = UNIT - T;
        //    msgArea.text = width + ', ' + height;
        //});
    }

    public function clear():void
    {
        status = NONE;

        with (graphics) {
            lineStyle(0);
            beginFill(0);
            drawRect(0, 0, UNIT - T, UNIT - T);
        }
    }

    public function enableButton():void
    {
        buttonMode = true;
        addEventListener(MouseEvent.CLICK, clicked);
    }

    public function disableButton():void
    {
        buttonMode = false;
        removeEventListener(MouseEvent.CLICK, clicked);
    }

    private function clicked(event:MouseEvent):void
    {
        // プレイヤーがこの枡を取る
        disableButton();
        status = PLAYER;
        with (graphics) {
            lineStyle(T, 0xffffff);
            beginFill(0);
            drawCircle(width / 2, height / 2, width / 2 - T - T);
        }

        if (Board(parent).checkAndNext() == NONE)
            Board(parent).myTurn();
    }

    /**
     * コンピュータがこの枡を取る
     */
    public function mark():void
    {
        disableButton();
        status = COMPUTER;
        // draw cross "X"
        with (graphics) {
            lineStyle(T, 0xffffff);
            moveTo(T * 2, T * 2);
            lineTo(UNIT - T * 3, UNIT - T * 3);
            moveTo(UNIT - T * 3, T * 2);
            lineTo(T * 2, UNIT - T * 3);
        }

        Board(parent).checkAndNext();
    }
}

class ResultMsg extends TextField
{
    private const timer:Timer = new Timer(2000, 1);

    function ResultMsg()
    {
        selectable = false;
        defaultTextFormat =
            new TextFormat("Arial", 100, 0xffffff);
        autoSize = TextFieldAutoSize.LEFT;
        filters = [new GlowFilter(0)];

        timer.addEventListener(TimerEvent.TIMER_COMPLETE, next);
    }

    public function display(str:String):void
    {
        text = str;

        board.addChild(this);
        x = (parent.width  - width)  / 2;
        y = (parent.height - height) / 2;

        timer.start();
    }

    private function next(e:TimerEvent):void
    {
        parent.removeChild(this);

        game.replay();
    }
}

// メッセージ表示領域 ///////////////////////////////////////

/**
 * メッセージ表示領域
 */
class MessageArea extends TextField
{
    function MessageArea()
    {
        defaultTextFormat = new TextFormat("Arial", null, 0xffffff);
    }
}

// ダイアログとボタン ////////////////////////////////////////

/**
 * ダイアログとボタン
 */
import flash.geom.Matrix;
import flash.display.GradientType;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.events.MouseEvent;
import flash.events.Event;

const textPoint:uint = 52;    // ダイアログとボタンに使う文字の大きさ(ポイント)

class GradientBox extends Sprite
{
    function GradientBox(_width:uint, _height:uint, _x:uint = 0, _y:uint = 0)
    {
        var m:Matrix = new Matrix();
        m.createGradientBox(_width, _height,
            Math.PI / 2); // rotation, 90 degrees
        // createGradientBox(200, 100, Math.PI / 2)
        //   #=> Matrix(7.474404015029158e-18, 0.06103515625, -0.1220703125, 3.737202007514579e-18, 100, 50)
        
        //message.text = 'm(' +
        // String(m.a) + ', ' + String(m.b) + ', ' +
        // String(m.c) + ', ' + String(m.d) + ',\n' +
        // String(m.tx) + ', ' + String(m.ty) + ')';
        with (graphics) {
            beginGradientFill(GradientType.LINEAR,
                [0x333333, 0x666666],
                [0.8, 0.8],
                [0, 80],
                m);
            drawRoundRect(_x, _y, _width, _height, 30, 30);
        }
    }
}

class Dialog extends GradientBox
{
    function Dialog(msg:String, b1s:String, b1fn:Function, b2s:String, b2fn:Function)
    {
        // TODO
        // [ ] 大きさを計算で決める
        // [x] 計算して中央に配置する
        // [x] 位置決定をDialogのコンストラクタに入れる
        var _width:uint = 450;
        var _height:uint = 300;
        
        var _x:uint = 0;
        var _y:uint = 0;
        const thickness:uint = 2;
        with (graphics) {
            lineStyle(thickness, 0xffffff);
            //drawRoundRect(0, 0, _width, _height, 30, 30);
            //_width  -= thickness * 2;
            //_height -= thickness * 2;
            //_x += thickness;
            //_y += thickness;
            
            //lineStyle(1, 0);
            //drawRoundRect(3, 3, _width, _height, 30, 30);
            //_width -= 2; _height -= 2;
        }
        super(_width, _height, _x, _y);
        
        with (addChild(new TextField())) {
            defaultTextFormat = new TextFormat("Arial", textPoint, 0xffffff);
            autoSize = TextFieldAutoSize.LEFT;
            text = msg;
            selectable = false;
            x = (parent.width  - width)  / 2;
            y = (parent.height - height) / 3.3;    // 適当
        }
        
        var b1:Button = new Button(b1s);
        b1.x = 16;
        b1.y = 182;
        addChild(b1);
        b1.addEventListener(MouseEvent.CLICK, b1fn);
        
        var b2:Button = new Button(b2s);
        b2.x = b1.x * 2 + b1.width;
        b2.y = b1.y;
        addChild(b2);
        b2.addEventListener(MouseEvent.CLICK, b2fn);
        
        addEventListener(Event.ADDED, function():void {
            x = (stage.stageWidth  - width)  / 2;
            y = (stage.stageHeight - height) / 2;
            //msgArea.text = String('dialog.x: ' + x);
        });
    }
}

class Button extends GradientBox
{
    function Button(s:String,
                    _width:uint = 200, _height:uint = 100)
    {
        buttonMode = true;
        mouseChildren = false;
        
        const thickness:uint = 1;
        with (graphics) {
            lineStyle(thickness, 0xffffff);
            drawRoundRect(0, 0, _width, _height, 30, 30);
            _width  -= thickness * 2;
            _height -= thickness * 2;
            lineStyle(1, 0);
        }
        super(_width, _height, thickness, thickness);
        
        with (addChild(new TextField)) {
            //border = true;
            //borderColor = 0x00ff00;
            defaultTextFormat = new TextFormat("Arial", textPoint, 0xffffff);
            autoSize = TextFieldAutoSize.LEFT;
            text = s;
            //selectable = false;
            x = (parent.width  - width)  / 2;
            y = (parent.height - height) / 2;
        }
    }
}

Forked