今回は対戦型のマインスイーパーをつくります。

元々のマインスイーパーは1980年代に発明された一人用のコンピュータゲームです。ゲーム画面は正方形のマスが敷き詰められた長方形のフィールドから構成されています。地雷の置かれているマスを開けないように、それ以外のすべてのマスを開ければ成功です。

地雷が置かれていないマスを開けたときは、隣接する8方向のマスのいずれかに地雷がある場合はその個数が表示されます。隣接するマスに地雷がない場合はその部分が自動的に開きます。

またプレイヤーは地雷の位置の推測を容易にするために、地雷が置かれていると思われるマスに旗を立てることができます。
Microsoft Windowsのものでは、

初級:9×9のマスに10個の地雷
中級:16×16のマスに40個の地雷
上級:30×16のマスに99個の地雷

があります。ところがWindows 8からは標準では搭載されず、ストアアプリからダウンロードしなければなりません。

ただこれから作ろうとしているものは対戦型なのでもっとフィールドを広くしようと考えています。32×32なんてのはどうでしょうか?地雷があると思われる場所には旗を立てることができますが、他のプレイヤーからはみることはできません。

それではさっそくつくってみましょう。

Positionクラスの定義

まず名前空間ですがMinesweeperGameとします。

最初に位置を格納するためのPositionクラスを定義します。

次にゲームに関する情報、処理をするGameクラスを定義します。

Gameクラスの定義

と書くべきところを名前空間を省略して

と書くことにします。

まずフィールド変数を示します。

最初の3つは見ての通りです。MapとIsMapOpenは二次元配列でMapの各要素は-1なら地雷、それ以外の部分はその周囲に存在する地雷の数です。IsMapOpenはすでに自分または他のプレイヤーによって開かれているかどうかを示すものです。

_randomは地雷の場所を決めるときに使う乱数生成用のものであり、PlayersはAspNetCore.SignalRで接続したときのConnectionIDとPlayerオブジェクトの辞書です。

初期化の処理を示します。ここでは32×32のセルにランダムに地雷をセットしてセルを空けたときに表示される数字をセットしています。

各クライアントにセルを描画するための情報を送信しなければならないのですが、GetStringFromMapメソッドはその送信するカンマ区切りの文字列を生成します。

旗が立っているのであれば-3、すでに誰かが開いているセルであれば表示すべき数字、開かれていないのであれば-2とします。

GetUnopenedCellCountメソッドは地雷がセットされていないセルの数を返します。これが0になったらステージクリアとなります。

Playerクラスの定義

次にプレイヤーに関する処理をおこなうPlayerクラスを示します。

と書くべきところを名前空間を省略して

と書くことにします。

コンストラクタとプロパティを示します。

ConnectionIdはAspNetCore.SignalRで接続したときに定まるIDです。

Nameはプレイヤーの名前で、空文字列のときは名無しさんになります。また長すぎる名前の場合は16文字に切り詰めています。それからスコアランキングをカンマ区切りのテキストファイルで管理するのでプレイヤー名のなかにカンマがある場合は別の文字に置き換えています。

Scoreは読んで字のごとしそのプレイヤーのスコアです。

旗の管理

プレイヤーが地雷があるかもしれないと思った場所に旗を立てますが、これはそれ以外のプレイヤーに見られないようにします。そこでどこに旗を立てているかは各Playerオブジェクトで管理します。