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

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

Tic-tac-toe, ○×ゲーム, 三目並べ.
*  since 2010-06-24
*
* TODO
* [ ] ダブルリーチがあれば取る
* [ ] 防御しながらリーチになる手を優先して取る
* [ ] ボタンにフォーカスしたら目立たせる(白っぽくする?)
* [ ] 枠○×を「ぼわっと光った線」で描画する
* [ ] 勝敗がついた行・列を強く光らせる
* [ ] 勝敗の表示を、プレイヤーが勝ったら輝かせる・敗けたら溶けて落ちるようにする
* [ ] 枡を取る時の表現を「ぼわっと光る」ものにする
* [ ] ダイアログに影をつける
* [ ] 先攻を×にする
* [ ] 得点記録
* [ ] Flash Builderから実行した時、意図せず拡大されて下にはみ出すのを解決する
* [ ] 広告を入れる
* [ ] BetweenAS3を使ってみる    http://www.be-interactive.org/index.php?itemid=472
*                                http://wonderfl.kayac.com/code/7e3f080227a081bafbcdb09dd898b27c057cbf76
* [ ] ○か×かを選べるようにする
* [/] level 2: 考えて取る
* [/] 引き分け時の処理
* [/] level 1: ランダムに取る
* [/] レベル選択機能
* [/] リプレイ時に左上に○と×が重なって表示されるバグを解決
* [/] 勝敗を大きく表示する
* [/] リプレイ機能
Get Adobe Flash player
by hermit 05 Jul 2010
/**
 * Copyright hermit ( http://wonderfl.net/user/hermit )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dxps
 */

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

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

    [SWF(backgroundColor = 0)]

    public class Main extends Sprite
    {
        private var dialog:Dialog;
        private const board:BoardLevel2 = new BoardLevel2;

        public function Main()
        {
            game = this;

            addChild(board);
            //addChild(msgArea);

            dialog = new Dialog(this, start);
        }

        private function start(e:MouseEvent):void {
            board.clear();
            if (dialog.first)
                board.myTurn();
            board.enableBoxButtons();
        }

        public function replay():void
        {
            dialog.visible = true;
        }

        public function get level():uint { return dialog.level }
    }
}

var game:Main;

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

/**
 * thickness 枠線、○、×の太さ
 */
const T:uint = 10;

/**
 * 桝目の大きさ.
 *
 * (stage.stageWidth - 5*2 - 10) / 3
 */
const UNIT:uint = 148;

import flash.display.Sprite;

// TODO: Mainに入れる?
class Board extends Sprite
{
    protected 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]);
            }

        addChild(resultMsg);
    }

    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 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();
    }

//    /**
//     * コンピュータが一手進める
//     */
//    public function myTurn():void
//    {
//        switch (game.level) {
//            case 0:     level0(); break;
//            case 1:     level1(); break;
//            default: Level2.level2(boxes);
//        }
//    }

    // 枡を取るアルゴリズム ////////////

    /**
     * Level 0: 端から取っていく
     */
    protected function level0():void
    {
        for each (var line:Array in boxes)
            for each (var box:Box in line)
                if (box.status == Box.NONE) {
                    box.mark();
                    return;
                }
    }

    /**
     * Level 1: ランダムに取る
     */
    protected function level1():void
    {
        var box:Box;
        do {
            var rx:uint = Math.floor(Math.random() * 3);    // 0, 1, 2のどれかの乱数
            var ry:uint = Math.floor(Math.random() * 3);
            box = boxes[rx][ry];
        } while (box.status != Box.NONE);

        box.mark();
    }

    // 勝敗判定関連 ////////////

    /**
     * 勝敗判定.
     *
     * 勝敗を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;

        for each (var line:Array in boxes)
            for each (var box:Box in line)
                if (box.status == Box.NONE)        // まだ空きがある
                    return Box.NONE;            //    勝敗がついてない

        return Box.DRAWN;
    }

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

            case Box.COMPUTER:
                resultMsg.display('You lost!');
                break;
            case Box.PLAYER:
                resultMsg.display('You won!');
                break;
            case Box.DRAWN:
                resultMsg.display('Drawn game...', 70);
        }
        disableBoxButtons();
        return false;
    }
}

class BoardLevel2 extends Board
{
    /**
     * コンピュータが一手進める
     */
    public function myTurn():void
    {
        switch (game.level) {
            case 0:     level0(); break;
            case 1:     level1(); break;
            default: level2();
        }
    }

    /**
     * Level 2: 考えて取る
     */
    public function level2():void
    {
        if (getKimarite())                        // 決まり手があれば取る
            return;
        if (getGuard())
            return;                                // 防ぐべき場所があれば取る

        // TODO: ダブルリーチがあれば取る

        if (boxes[1][1].status == Box.NONE) {    // 中央を取る
            boxes[1][1].mark();
            return;
        }
        level1();
    }

    /**
     * 決まり手があれば取ってtrueを返す。
     * なければfalseを返す。
     */
    private function getKimarite():Boolean
    {
        if (tryCorner(Box.COMPUTER) ||
            tryCenter(Box.COMPUTER) ||
            trySide(Box.COMPUTER))
            return true;
        return false;
    }

    /**
     * 四隅に取るべき手があれば、取ってtrueを返す。
     * 見つからなければfalseを返す。
     *
     * @param s Box.COMPUTERなら、決まり手を探す。
     *            Box.PLAYERなら、防ぎ手を探す。
     */
    private function tryCorner(s:uint):Boolean
    {
        if ((boxes[0][0].status == Box.NONE) &&
            ((boxes[0][1].status == s && boxes[0][2].status == s) ||
             (boxes[1][0].status == s && boxes[2][0].status == s) ||
             (boxes[1][1].status == s && boxes[2][2].status == s))) {
            boxes[0][0].mark();
            return true;
        }
        if ((boxes[0][2].status == Box.NONE) &&
            ((boxes[0][0].status == s && boxes[0][1].status == s) ||
             (boxes[1][2].status == s && boxes[2][2].status == s) ||
             (boxes[1][1].status == s && boxes[2][0].status == s))) {
            boxes[0][2].mark();
            return true;
        }
        if ((boxes[2][0].status == Box.NONE) &&
            ((boxes[0][0].status == s && boxes[1][0].status == s) ||
             (boxes[2][1].status == s && boxes[2][2].status == s) ||
             (boxes[1][1].status == s && boxes[0][2].status == s))) {
            boxes[2][0].mark();
            return true;
        }
        if ((boxes[2][2].status == Box.NONE) &&
            ((boxes[2][0].status == s && boxes[2][1].status == s) ||
             (boxes[0][2].status == s && boxes[1][2].status == s) ||
             (boxes[0][0].status == s && boxes[1][1].status == s))) {
            boxes[2][2].mark();
            return true;
        }
        return false;
    }

    /**
     * 中央が取るべき手であれば、取ってtrueを返す。
     * そうでなければfalseを返す。
     *
     * @param s Box.COMPUTERなら、決まり手を探す。
     *            Box.PLAYERなら、防ぎ手を探す。
     */
    private function tryCenter(s:uint):Boolean
    {
        if ((boxes[1][1].status == Box.NONE) &&
            ((boxes[0][0].status == s && boxes[2][2].status == s) ||
             (boxes[0][1].status == s && boxes[2][1].status == s) ||
             (boxes[2][0].status == s && boxes[0][2].status == s) ||
             (boxes[1][0].status == s && boxes[1][2].status == s))) {
            boxes[1][1].mark();
            return true;
        }
        return false;
    }

    /**
     * 辺に取るべき手があれば、取ってtrueを返す。
     * 見つからなければfalseを返す。
     *
     * @param s Box.COMPUTERなら、決まり手を探す。
     *            Box.PLAYERなら、防ぎ手を探す。
     */
    private function trySide(s:uint):Boolean
    {
        if ((boxes[0][1].status == Box.NONE) &&
            ((boxes[0][0].status == s && boxes[0][2].status == s) ||
             (boxes[1][1].status == s && boxes[2][1].status == s))) {
            boxes[0][1].mark();
            return true;
        }
        if ((boxes[2][1].status == Box.NONE) &&
            ((boxes[2][0].status == s && boxes[2][2].status == s) ||
             (boxes[0][1].status == s && boxes[1][1].status == s))) {
            boxes[2][1].mark();
            return true;
        }

        if ((boxes[1][0].status == Box.NONE) &&
            ((boxes[0][0].status == s && boxes[2][0].status == s) ||
             (boxes[1][1].status == s && boxes[1][2].status == s))) {
            boxes[1][0].mark();
            return true;
        }
        if ((boxes[1][2].status == Box.NONE) &&
            ((boxes[0][2].status == s && boxes[2][2].status == s) ||
             (boxes[1][0].status == s && boxes[1][1].status == s))) {
            boxes[1][2].mark();
            return true;
        }

        return false;
    }

    private function getGuard():Boolean
    {
        if (tryCorner(Box.PLAYER) ||
            tryCenter(Box.PLAYER) ||
            trySide(Box.PLAYER))
            return true;
        return false;
    }
}

import flash.events.MouseEvent;

/**
 * 桝目, 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 static const DRAWN:uint      = 3;

    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 - 1, UNIT - T - 1);
        }
    }

    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);
        }

        with (BoardLevel2(parent))
            if (checkAndNext())
                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();
    }
}

import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.filters.GlowFilter;

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

    function ResultMsg()
    {
        selectable = false;
        defaultTextFormat =
            new TextFormat("Arial", null, 0xffffff);
        autoSize = TextFieldAutoSize.LEFT;
        filters = [new GlowFilter(0)]; // 周りをぼわっと黒くする

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

    public function display(str:String, size:uint = 90):void
    {
        text = str;
        setTextFormat(new TextFormat(null, size));

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

        visible = true;

        timer.start();
    }

    private function next(e:TimerEvent):void
    {
        visible = false;

        game.replay();
    }
}

///////////////////////////////////////////////////////////////////////////////

import com.bit101.components.Window;
import com.bit101.components.Label;
import com.bit101.components.RadioButton;
import com.bit101.components.PushButton;
import flash.events.Event;

class Dialog extends Window
{
    private var _level:uint = 2;
    /**
     * ゲームの難しさ
     */
    public function get level():uint { return _level }

    private var _first:Boolean = false;
    /**
     * コンピュータが先攻かどうか
     */
    public function get first():Boolean { return _first }

    public function Dialog(parent:Sprite, f:Function)
    {
        super(parent, 0, 0, 'Tic-tac-toe');
        width = 120;
        height = 130;
        x = (stage.stageWidth  - width)  / 2;
        y = (stage.stageHeight - height) / 2;
        //color = 0;    // これだけだと見にくい

        new Label(this, 10, 30, 'Level');
        var l0:RadioButton = new RadioButton(this, 40, 34, '0', false, function (e:Event):void { _level = 0 });
        var l1:RadioButton = new RadioButton(this, 65, 34, '1', false, function (e:Event):void { _level = 1 });
        var l2:RadioButton = new RadioButton(this, 90, 34, '2', true,  function (e:Event):void { _level = 2 });
        l0.groupName = l1.groupName = l2.groupName = 'level';

        new Label(this, 10, 60, 'Who marks first?');
        var f0:RadioButton = new RadioButton(this, 12, 79, 'You', true,  function (e:Event):void { _first = false });
        var f1:RadioButton = new RadioButton(this, 52, 79, 'Me',  false, function (e:Event):void { _first = true });
        f0.groupName = f1.groupName = '1st';

        var b:PushButton = new PushButton(this, 10, 100, 'Go !', function (e:Event):void { visible = false });
        b.width = 40;
        b.x = (width - b.width) / 2;
        b.addEventListener(MouseEvent.CLICK, f);
    }
}