Index

HOME > プログラムTOP > JavaScript



ゲーム BingoPlus(1)

 昔見かけた対コンピュータの対戦型ビンゴゲームをつくってみました。正式な名称は分かりません。勝手に " BingoPlus " と付けています。
 ルールはシンプルで、交互に数字を選んでいって、五個一列の数字を消していきます。たてよこななめ12列のうち、先に5列を完成させた方が勝ちとなります。画像は一切使っていませんが、クラスやDOM、CSSの初歩的な知識が必要です。なお、下図はゲームの初期画面です。(IE5以上/NN6以上)





ページ1
概要
Cell クラス
Player クラス
初期化

ページ2
終了の判断
コンピュータの思考
イベント処理

sasaraan programming

Exposition

■概要

 以下に揚げる二つのクラスと十個の関数をつくります。関数は、これ以外にも、クラスの中にメソッドとして登録しているものがあります。
●クラス
 ・Cell クラス  ・・・ 一つ一つのます目の情報と状態 / ここから50のオブジェクトを生成
 ・Player クラス ・・・ 参加プレーヤーのゲーム状況 / ここから二つのオブジェクトを生成
●ゲームを扱う関数
 ・InitBingo関数   ・・・ 盤の作成とクラスの初期化 / 最初に呼び出す関数
 ・CreateArea関数  ・・・ ゲームエリアの作成 / InitBingo関数から呼び出し
 ・CreateTable関数 ・・・ テーブルの作成(CPU/ユーザー) / CreateArea関数から呼び出し
 ・IsGameOver関数 ・・・ ゲーム終了の判断
 ・CPU_Turn関数   ・・・ CPUの手番 / CPU側のます目の選択
●イベントを扱う関数
 ・Cell_Click関数   ・・・ セルのクリックイベント(ます目の選択)
 ・Start_Click関数  ・・・ スタートボタンのクリックイベント(ゲームの開始)
 ・Order_Click関数  ・・・ 順番(先攻・後攻)ボタンのクリックイベント(順番の指定)
 ・Help_Click関数   ・・・ ヘルプボタンのクリックイベント(Helpの表示)
 ・Cell_MouseMove関数 ・・・ セルのマウスイベント(背景色の変更)
 以下は、今回使用した CSS のソースと HTML の記述例です。CSS は好みで変更すれば様々なデザインにアレンジできると思います。HTML では、ゲーム盤を挿入したい箇所に InitBingo 関数を配置するだけとなります。ただし、バージョンチェックが必要となります。(IE5以上/NN6以上等DOM使用環境)
/* CSS ソース */
<style type="text/css"><!--
    table.bingo {font-size:middle; border:0; margin:0; padding:0; background-color:gray;}
    .bingo th    {line-height:1.5em; padding:0; color:white;
                    border-style:solid; border-width:1px; border-color:#CCC #333 #333 #CCC;}
    .bingo td    {line-height:1.5em; padding:0; color:white; text-align:left; font-size:small;}
    .bingo input {width:6em; margin:1ex;}
    .bingo a     {display:block; width:100%; text-decoration:none; color:white;}
    th.cell       {width:2em; line-height:1.8em; font-family:times, sans-serif;}
--></style> 
<!-- HTML ソース -->
<body id="body1">
    <script type="text/javascript"><!--
        if (document.getElementById) InitBingo();
    //--></script>
</body> 

■Cell クラス

 Cellクラスは、50個分の数字のます目を扱うクラスです。4つのプロパティと2つのメソッドから成ります。プロパティは this キーワードを使って登録します。ここで指定する値が初期値と成ります。一方、メソッドは、普通に関数として定義した後、 prototype プロパティを使ってクラスに登録します。
 なお、TextNode オブジェクトを利用しますが、nodeValue (または data)プロパティを使えば、値の設定と取得が可能です。ここでは、メソッドにして簡単な記述ですむように試みています。
// Cell クラス(引数figure : ます目の数字の初期値)
function Cell(figure) 
{
    /*** プロパティ ***/

    this.Selected = false;    // 選択されているかどうか(true | false)
    this.Figure = figure;      // 数字
    this.Weight = 0;            // ます目のウェイト(ます目の位置に依存 / 1 - 9)
    this.Node = document.createTextNode(" ");  // TextNodeオブジェクト

    /*** メソッド ***/

    // ノードの値の取得
    function _GetValue() {
        return this.Node.nodeValue;
    }
    Cell.prototype.GetValue = _GetValue;

    // ノードの値の設定
    function _SetValue(value) {
        this.Node.nodeValue = value;
    }
    Cell.prototype.SetValue = _SetValue;
} 

■Player クラス

 Player クラスは、プレーヤー(CPU とユーザー)のそれぞれの状態を保持したり、データを処理するためのクラスです。
// Player クラス(引数name : プレーヤー名)
function Player(name) 
{
    /*** プロパティ ***/

    this.Name = name;                        // プレーヤー名
    this.Cells = new Array(25);             // Cell オブジェクト(ます目)の配列(25個分)
    this.Lines = new Array(0,0,0,0,0,0);  // x(0〜5)個そろっている行がいくつあるか
    this.Order = 0;                              // 先攻 0 : 後攻 1
    this.Turn = true;                           // 自分の手番かどうか(false時はます目の選択不可)
    this.Node = document.createTextNode("");  // TextNodeオブジェクト
    this.DefColor = "#F63";                 // 通常の背景色
    this.SelColor = "#FC9";                 // 選択状態の背景色
    this.OverColor = "#FC0";               // マウスオーバー時の背景色

    /*** メソッド ***/

    // ステータス文字列(ノードの値)の取得
    function _GetStatus() {
        return this.Node.nodeValue;
    }
    Player.prototype.GetStatus = _GetStatus;

    // ステータス文字列(ノードの値)の設定
    function _SetStatus(status) {
        this.Node.nodeValue = status;
    }
    Player.prototype.SetStatus = _SetStatus;

    // シャッフル(ます目の数字の入れ替え)
    function _Shuffle() {
        var i;
        for (i=0; i<25; i++) {
            var r = Math.floor(Math.random() * 25);
            var temp;
            temp = this.Cells[i].Figure;
            this.Cells[i].Figure = this.Cells[r].Figure;
            this.Cells[r].Figure = temp;
        }
    }
    Player.prototype.Shuffle = _Shuffle;

    // ある行に選択されたセルがいくつあるか(GetLineCount 関数から呼び出し)
    // ... 引数  : start : 開始インデックス、interval : ます目の間隔
    // ... 戻り値 : 選択状態のセルの数 (0〜5) 
    function _GetCellCount(start, interval) {
        var nCells = 0;
        var i;
        for (i=0; i<5; i++) {
            if (this.Cells[start + i * interval].Selected) nCells++;  // 選択状態のセル
        }
        return nCells;
    }
    Player.prototype.GetCellCount = _GetCellCount;

    // 選択されたセルがx(0〜5)個の行がいくつあるか(盤面の評価や終了判定で使用)
    // ... 取得した値は Lines プロパティ(配列)に代入
    function _GetLineCount() {
        var i
        var nCells;
        for (i=0; i<this.Lines.length; i++) this.Lines[i] = 0;  // 配列を初期化
        for (i=0; i<25; i+=5) {                /* 横の列(5列分) */
            nCells = this.GetCellCount(i, 1);
            this.Lines[nCells]++;
        }
        for (i=0; i<5; i++) {                    /* 縦の列(5列分) */
            nCells = this.GetCellCount(i, 5);
            this.Lines[nCells]++;
        }
        nCells = this.GetCellCount(0, 6);  /* ななめの列(左上:0→右下:24) */
        this.Lines[nCells]++;
        nCells = this.GetCellCount(4, 4);  /* ななめの列(右上:4→左下:20) */
        this.Lines[nCells]++;
    }
    Player.prototype.GetLineCount = _GetLineCount;

    // 次の手の選択(CPU側のます目を選択するロジック)
    // ... セルのインデックス
    function _GetNextCell() {
        var nIndex = 0;            // セルのインデックス
        var nPoint = 0;            // セルの評価ポイント
        var i;
        for (i=0; i<25; i++) {
            var temp;
            if (this.Cells[i].Selected) continue;    // すでに選択されているセルはパス
            this.Cells[i].Selected = true;            // 選択状態にしてみる
            this.GetLineCount();                       // 盤面の選択状態の取得
            if (this.Lines[5] < 3) {                 /* 評価ポイント(5個の列が三つそろうまで) */
                temp = this.Lines[1] + this.Lines[2] * 2 + this.Lines[3] * 3 + this.Lines[4] * 4
                        + this.Lines[5] * 5 + this.Cells[i].Weight;
            } else {                                      /* 評価ポイント(5個の列が三つ以上の時) */
                temp = this.Lines[1] + this.Lines[2] * 2 + this.Lines[3] * 4 + this.Lines[4] * 7 
                        + this.Lines[5] * 10 + this.Cells[i].Weight;
            }
            this.Cells[i].Selected = false;            // 選択状態を解除
            if (temp > nPoint) {                      /* 評価ポイントが高い方を選択 */
                nIndex = i;
                nPoint = temp;
            }				
        }
        return nIndex;
    }
    Player.prototype.GetNextCell = _GetNextCell;
} 

■初期化

 InitBingo 関数はゲームの初期化を行う関数です。盤面の作成とクラスの初期化を行っています。CreateArea 関数は盤面を HTML に書き出す関数です。
 数字を表示するます目には a要素を使用しています。ただし、クリックイベントでJavaScript の関数を呼び出す必要があります。これには、"href=javascript:関数名"というように記述します。
// グローバル変数
var oCPU;                     // Playerオブジェクト : コンピュータ側 
var oYOU;                     // Playerオブジェクト : ユーザー側
var sID_CPU = "CPU";    // プレーヤー名 : コンピュータ側
var sID_YOU = "YOU";    // プレーヤー名 : ユーザー側
var aWeight = new Array(3,1,2,1,3, 1,2,2,2,1, 2,2,9,2,2, 1,2,2,2,1, 3,1,2,1,3); // ます目毎の重み

// 初期化処理
function InitBingo() 
{
    if (!document.getElementById) return;
    var i;
    oCPU = new Player(sID_CPU);        // Playerオブジェクトの生成(CPU側)
    oYOU = new Player(sID_YOU);        // Playerオブジェクトの生成(ユーザー側)
    CreateArea();                              // 盤面の作成
    document.getElementById(oCPU.Name).appendChild(oCPU.Node); // TextNodeの追加
    document.getElementById(oYOU.Name).appendChild(oYOU.Node); // TextNodeの追加
    for (i=0; i<25; i++) {                     /* ます目の初期化 */
        var oCell;    // A要素(ます目の数字を表示する)のオブジェクト

        oCPU.Cells[i] = new Cell(i+1);  // Cellオブジェクト生成(数字はとりあえず順番に付与)
        oCPU.Cells[i].Weight = aWeight[i];
        oCell = document.getElementById(oCPU.Name + i);
        oCell.appendChild(oCPU.Cells[i].Node);    // TextNode の追加(数字用)

        oYOU.Cells[i] = new Cell(i+1);  // Cellオブジェクト生成(数字はとりあえず順番に付与)
        oYOU.Cells[i].Weight = aWeight[i];
        oCell = document.getElementById(oYOU.Name + i);
        oCell.appendChild(oYOU.Cells[i].Node);    // TextNode の追加(数字用)
    }
    Start_Click();    // ゲームの開始
}

// ゲームエリアの作成
function CreateArea() 
{
    document.write("<table class='bingo'>");
    document.write("<tr><th colspan='2'>");
    document.write("【 BINGO PLUS 】</tr>");
    document.write("<tr>");
    for (var i=0; i<2; i++) {
        document.write("<th>");
        CreateTable(i);        // テーブルの作成
        document.write("</th>");
    }
    document.write("</tr>");
    document.write("<tr><th colspan='2'>");
    document.write("<input type='button' id='start' value='スタート' onclick='Start_Click()'/>");
    document.write("<input type='button' id='order' value='先 攻' onclick='Order_Click()'/>");
    document.write("<input type='button' id='help' value='ヘルプ' onclick='Help_Click()' />");
    document.write("</tr>");
    document.write("</table>");
}

// テーブルの作成(index値=0...左側(CPU) , index値=1...右側(ユーザー))
function CreateTable(index) 
{
    var i;
    var strName = (index==0)? oCPU.Name: oYOU.Name;
    document.write("<table style='margin:1ex 1em 1em 1em'>")
    document.write("<tr><td id=", strName, " colspan='5'></tr>");
    document.write("<tr>");
    for (i=0; i<=24; i++) {
        var strID = strName + i;
        var strClick = "javascript:Cell_Click('" + i + "') ";            // クリック
        var strOver = "Cell_MouseMove('" + i + "','" + 0 + "') ";  // マウスオーバー
        var strOut = "Cell_MouseMove('" + i + "','" + 1 + "') ";    // マウスアウト
        document.write("<th class='cell'>");
        if (index == 0) {
            document.write("<a id=", strID, " href='#' >");
        } else {
            document.write("<a id=", strID, " href=" + strClick);
            document.write("onmouseover=", strOver, "onmouseout=", strOut, " >");
        }
        document.write("</a>");
        if (i % 5 == 4 && i != 24) {
            document.write("</tr><tr>");
        }
    }
    document.write("</tr></table>");
} 

www.sasaraan.net

(c) morijoh