Help us understand the problem. What is going on with this article?

三目並べをCanvasとjQueryで作る

はじめに

三目並べは小規模ながら簡単なルールを持つボードゲームであるため,プログラミング入門として良く作られる。実際,HTML上で動く三目並べは,JavaScript,Java,Ruby で多く作られている 1 2 3 4 5。中でも JavaScript は初心者向きであり,多くの人が最初に取り組もうとすると思われる。一方,HTML5 でサポートされた Canvas は,美しい図形を容易に描くことができ,位置を座標で指定できるため,リバーシや囲碁のような大きな盤面にも容易に拡張できる。そこで,三目並べの作成に Canvas を使いたいと思うのは自然であろう。

JavaScript のボタンやテーブルを使うときは,マウスボタンのクリックイベントに対して,関数の引数にオブジェクトを取得することができ,そのオブジェクトに対する処理を順次記述していくことになる。それに対して,HTML5 Canvas を使うときは,マウスボタンのクリックイベントに対して,jQuery のカスタム trigger を発行してそれを受信した時,ユーザの処理を記述する手順が必要となる。ここでは,この手順について解説する。実際に動いているプログラムは,ここを参照されたい。プログラムも置いてある 6

完成後にプログラムを見直したところ,わざわざ,カスタム trigger を使わなくても良いことがわかった。しかし,カスタム trigger の使い方としては役立つので,初版としてそのまま残すことにした。三目並べに関しては,より簡潔なコード改訂版として,改めて提示することにした6

実を言えば,HTML5 Canvas と jQuery を使った囲碁の対局サイトがある7。但し,そのプログラムの解説はなく,開発日誌8 が見られるだけなので詳細が判らない。そのため,ここに自分なりに作成したプログラムを紹介して,ご批判を仰ぎたい。

Canvas による画面の作成

Canvas を利用すると,美しい図形を容易に描くことができる。作成した三目並べの初期画面を,下図に示す。

三目並べの初期画面

この画面を表示するための JavaScript は,下に示すように,簡単である。

function boardset(){
    ctx.fillStyle = 'rgb(0, 160, 80)';
    ctx.rect(60, 20, 180, 180);
    ctx.fill();
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 2;
    ctx.beginPath();
    for (var i = 0; i <= 3; i++) {
        ctx.moveTo(60, 20+60*i);
        ctx.lineTo(240,20+60*i);
        ctx.stroke();
    }
    for (var j = 0; j <= 3; j++) {
        ctx.moveTo(60+60*j, 20);
        ctx.lineTo(60+60*j,200);
        ctx.stroke();
    }
    message("あなたの番です");
    r2message("再度挑戦    終了");
    drawsq(dx,dy,dw,dh,dr,dcolor);
    drawsq(dx+110,dy,dw-30,dh,dr,dcolor);
}

jQuery カスタム trigger の利用方法

先に述べたように,マウスボタンのクリックイベントに対して,jQuery のカスタム trigger を発行してそれを受信した時,ユーザの処理を記述する手順が必要となる。その主要部分を示すと,下記のようになる。

    // ユーザーの一手
    // user カスタムイベントを作成・受信
    $(document).on('user', function() {
        console.log('user カスタムイベントが実行されました');
        if (value[p] != 0) {
            message("そこは置けません");
        }
        else if(value[p] == 0) {
            value[p] = 1;
            circle(p);
            turn++;
            if (5 <= turn) r = judge();
            if ( r == 0 ) {
                // cpu にカスタムイベントを送る
                message("CPU の番です");
                $(document).trigger('cpu');
            }
        }
    });

    // マウスボタンのクリックイベントに対する処理
    $('#board').on('click', function(e) {
        var m = e.target.getBoundingClientRect();
        x = e.clientX - m.left;
        y = e.clientY - m.top;
        if ( (60 <= x && x < 240)  &&  (20 <= y && y < 200 )) {
            if ( 60 <= x && x < 240 ) {
                if( 20 <= y && y < 80 ) {
                    if( x < 120 ) p = 0;
                    else if( x < 180 ) p = 1;
                    else if( x < 240 ) p = 2; }
                ...
            }
            // user にカスタムイベントを送る
            $(document).trigger('user');
            return;
        }

上記で大事な点は,\$(document).trigger('user') {},すなわち,user にカスタムイベントを送る命令の前に,それを受信する命令 \$(document).on('user', function() {} ) を作成しておくことである。マウスクリックイベントを受信すると,ここに制御が渡ってくるので,ユーザーの一手を記述しておく。ユーザーはその後で,cpu にカスタムイベントを送ることになる。当然ながら,このカスタムイベントを送る命令の前に,これを受信する命令を記述しておかなければならない。このように考えると,ゲームを進める構造が見えてくる。

trigger を省略したコード改訂版について

上記のプログラムを見直したところ,わざわざ,カスタム trigger を使わなくても良いことがわかった。trigger を使わない簡潔なコードは,下記のようになる。

    // マウスボタンのクリックイベントに対する処理
    $('#board').on('click', function(e) {
        var m = e.target.getBoundingClientRect();
        x = e.clientX - m.left;
        y = e.clientY - m.top;
        if ( (60 <= x && x < 240)  &&  (20 <= y && y < 200 )) {
            if ( 60 <= x && x < 240 ) {
                if( 20 <= y && y < 80 ) {
                    if( x < 120 ) p = 0;
                    else if( x < 180 ) p = 1;
                    else if( x < 240 ) p = 2; }
                ...
            }
            // ユーザーの一手
            if (value[p] != 0) {
                message("そこは置けません");
            }
            else if(value[p] == 0) {
                value[p] = 1;
                circle(p);
                turn++;
                if (5 <= turn) r = judge();
                if ( r == 0 ) {
                    message("CPU の番です");
                    // コンピュータの一手
            ...

初版で,\$(document).trigger('user') {},すなわち,user にカスタムイベントを送る命令を書いた部分に,それを受信する命令 \$(document).on('user', function() {} ) の中で書いたコード部分を直接書くことによって,コードが簡潔になることがわかった。

CPU の一手と終局の判定方法

三目並べの対局途中画面を,下図に示す。左側の図にみられるように,〇印はユーザーの手を示し,✖印は cpu の手を示す。現在,5 手まで進んだ局面である。

三目並べの対局途中画面

cpu の一手は,ランダムに決められる。すなわち,三目並べの枠 0 - 8 の数値を乱数で発生させて,対応する枠がすでに使われている場合は,再度試行する。9 回繰り返しても,空きが見つからない場合は,0 番から順に検索して,最初の空きに入れる。

ここで,枠内に描く図形と枠内を表す値は,対応関係さえあれば良い。そこで,終局の判定をしやすい値を用いた。上図の右側に示したように,ユーザーの手は 1 ,cpu の手は 10 を対応させる。これによって,縦横斜めいずれかの枠内合計値が 3 のときユーザーの勝ち,合計値が 30 のとき cpu の勝ちと判定できる。合計する枠内に相手の手が入っていても,この判定で良いことを確かめられたい。

従って,判定プログラムは,下記のように簡単になる。

function judge() {
    // 勝ち
    if ((value[0]+value[1]+value[2]) == 3 ||
        (value[3]+value[4]+value[5]) == 3 ||
        (value[6]+value[7]+value[8]) == 3 ||
        (value[0]+value[3]+value[6]) == 3 ||
        (value[1]+value[4]+value[7]) == 3 ||
        (value[2]+value[5]+value[8]) == 3 ||
        (value[0]+value[4]+value[8]) == 3 ||
        (value[2]+value[4]+value[6]) == 3 ) {
            message("あなたの勝ちです");
            return 1;
    }
    ...
}

おわりに

三目並べを作るときに,HTML5 Canvas を利用する方法について述べた。本文で示したように,プログラムの可読性は良いと考えている。頭書に示した URL から,プログラム HTML と JavaScript ファイル6 をダウンロードして実行しながら見れば,より理解しやすいと思われる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした