囲碁アプリケーションをつくる(5)〜同じ箇所に碁石を置けないようにする〜

▼囲碁アプリケーションをつくるシリーズ
囲碁アプリケーションをつくる(1)
囲碁アプリケーションをつくる(2)〜ざっくりとした仕様とバージョン管理〜
囲碁アプリケーションをつくる(3)〜canvasで碁盤を描画する〜
囲碁アプリケーションをつくる(4)〜クリックしたら碁石を描画する〜

▼GitHub
https://github.com/shogo0525/go

▼ざっくりとした仕様

◆STEP1:とりあえず囲碁っぽくなるフェーズ
・碁盤をcanvasで描画する。まずは19路盤から。
・碁盤上をクリックしたら、黒石または白石が置けるようになる。基本的には交互に色が変わっていく。
・引数を渡すと、19路盤以外にも13路盤や9路盤など自由なサイズで碁盤を描画できるようになる。MAXは19路。

◆STEP2:囲碁のロジックを組み込む
・石を取る、コウなど囲碁のルールを組み込んで行く
・ここでは試合計算など、終局に関わることは判断しない◆STEP3:互先の棋譜データを記録できるようになる
・SGFといった、囲碁の棋譜データの形式に変換できるようにする
・戻る、進むボタンを用意して、自由に盤面上の碁石を操れるようにする

◆STEP1000:弱小コンピューターとの対戦ができるようになる

***

第5回目では、前回問題として上がっていた「同じ箇所に再度打つことができてしまう」ことを解決していきます。

方針としては、まず、①囲碁の棋譜を保存するデータ形式「sgf」を作成するプログラムを作ります。一手打つごとに、sgfデータを更新していくようなイメージです。

①ができたら、次は、②一手打とうとするときに、sgfを参照して、打とうとしている手がすでにsgfにあるかどうかを検索します。

ちなみに、sgfとは「Smart Game Format」の略語です。主に囲碁で使われる棋譜のファイル形式です。

中核となるのは下記の部分で、BlackかWhite、そして縦横の座標をアルファベット2文字で表したものになります。

;B[pd];W[dp];B[pq];W[dd];B[qk];W[jd]

この①と②を実装すると、「同じ箇所に再度打つことができない」プログラムができます。

下記のコードが今回実装したプログラムです(一部抜粋)。
①をupdateSGFメソッド、②をcanPutメソッドで定義しています。


    canvas.addEventListener('click', onClick, false);
    function onClick(e) {
      // getBoundingClientRect()でcanvasの左上の座標を取得
      var rect = e.target.getBoundingClientRect();
      // クリック座標であるe.clientX,e.clientYからrectの値を引く
      var x = e.clientX - rect.left;
      var y = e.clientY - rect.top;

      //クリック場所が碁盤の外側なら何もしない
      if (x < cellSize || x > cellSize * boardSize ||
          y < cellSize || y > cellSize * boardSize) {
        return;
      }

      // zahyoは1~19の値をとる(19路盤の場合)
      var zahyo = { x: getZahyo(x), y: getZahyo(y) };

      if (canPut(zahyo)) {
        drawStone(zahyo);
        updateSGF(zahyo);

        nextTurn();
      } else {
        alert('そこに打つことはできません');
      }
    }
    // 数値からsgf形式のデータに変換するための配列
    var numberToSGF = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
                       'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's'
                      ];
    // 一手打つごとにsgfを更新する
    function updateSGF(zahyo) {
      var move = ";" + turn + "[" + numberToSGF[zahyo.x-1] + numberToSGF[zahyo.y-1] + "]";
      sgf += move;
    }

    // zahyoに碁石が置けるかどうか
    function canPut(zahyo) {
      // 現在はsgfを中身をみて、過去に一度でも打った場所があるかどうかを判断するのみ
      // 囲碁ロジックをきちんと組み込むときには修正が必要
      var move = "[" + numberToSGF[zahyo.x-1] + numberToSGF[zahyo.y-1] + "]";
      return sgf.indexOf(move) > -1 ? false : true;
    }

同じ箇所に打とうとすると、アラートがでるようにしました。

sgf形式にするupdateSGFは、囲碁独特のロジックなのであまり見なくてよいです。ポイントはcanPutメソッド内でsgfを検索している箇所ですね。indexOfで打とうとしている一手がすでにsgf内にあるかどうかを判断しています。

ただし、本格的に囲碁のロジックを組み込んでいくときには、canPutには「石がすでにある」以外にも、「自殺点には置けない(相手の石を取れるときは置ける)」「コウ取りができるか」といったことも考慮しなければいけないので、ごそっと書き直す可能性は十分にありますね。

まあとりあえず、今回の実装によって、空いている箇所にしか碁石が打てないようになり、不自然な挙動はなくなりました。

●今回実装したプログラムはこちら

***

これでもまだまだ問題は山積み。

「間違って打っても元に戻すことができない」といった問題があります。

次回は、取り消しボタンを用意して、間違って打った手を消せるようにしてみたいと思います!!描画した碁石をどう消すかを考えなきゃ行けないですね・・(汗)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする