マインスイーパーのロジックを考える

2021/06/04

マインスイーパー

マインスイーパーとは

マインスイーパ(Minesweeper)は1980年代に発明された、一人用のコンピュータゲームである。ゲームの目的は地雷原から地雷を取り除くことである。

https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%A4%E3%83%BC%E3%83%91

  • マスが敷き詰められたフィードがある。
  • マスは数字が表示されている。
  • マスの数字はそのマスの周りにある地雷の数である。
  • 地雷だと思うマスに旗を立ててすべての地雷に旗を立てたらゲーム終了。
  • 地雷を踏んでしまうと爆発してゲーム失敗。

ゲームマップ作成

とりあえず二次元配列で考える。

地雷の位置を示したマス目を orignalMap と定義し
実際に表示されるマス目を gameMap としたときの
5 x 5 のマップで地雷数は 5個 の場合以下となるはず。

const orignalMap = [
  [0, 0, 0, 1, 0],
  [0, 0, 0, 0, 0],
  [0, 1, 1, 0, 0],
  [0, 0, 1, 0, 0],
  [0, 0, 0, 0, 1],
]

// 便宜上 地雷をxとする。
const gameMap = [
  [0, 0, 1, x, 1],
  [1, 2, 3, 2, 1],
  [1, x, x, 2, 0],
  [1, 3, x, 3, 1],
  [0, 1, 1, 2, x],
]

orignalMap をランダムで生成し、それから gameMap を生成したいときに
i を縦列, j を横列として周りの8マスを見る必要がある。 一旦端のことを考えずに列挙すると以下になる。

orignalMap[i - 1][j - 1]
orignalMap[i - 1][j]
orignalMap[i - 1][j + 1]

orignalMap[i][j - 1]
orignalMap[i][j + 1]

orignalMap[i + 1][j - 1]
orignalMap[i + 1][j]
orignalMap[i + 1][j + 1]

以上のマスを見たとき、そのマスが地雷だった場合カウントを足し gameMap[i][j] に挿入する。

旗を立てる

上記の gameMap から旗を立てたときのことを考える。

// 便宜上 地雷をxとし、旗をfとする。
const gameMap = [
  [0, 0, 1, f, 1],
  [1, 2, 3, 2, 1],
  [1, x, x, 2, 0],
  [1, 3, x, 3, 1],
  [0, 1, 1, 2, x],
]

マインスイーパーには指定したマス目をクリックした時、
その指定したマス目に書いてある数字分隣接するマスに旗が立っている場合、
指定したマス目の周りをオープンする機能がある。

例:
gameMap[0][3] に旗が立ってる場合、gameMap[0][2] が 1 なのでそのマスをクリックすると旗の立っているマス以外の隣接するマスがオープンされる。

クリックされたマスの 縦列のindexを c_i
クリックされたマスの 横列のindexを c_j
とした時その周りのマスの旗の数をカウントする。

gameMap[c_i - 1][c_j - 1]
gameMap[c_i - 1][c_j]
gameMap[c_i - 1][c_j + 1]

gameMap[c_i][c_j - 1]
gameMap[c_i][c_j + 1]

gameMap[c_i + 1][c_j - 1]
gameMap[c_i + 1][c_j]
gameMap[c_i + 1][c_j + 1]

これらをチェックした時、クリックしたマスのカウント数に達していれば周りのマスをオープンする。

マスを開ける

上記の gameMap からマスを開けるときのことを考える。
特定のマスを開けたときそのマスが数字のマスであればそこのみ開ければ良いが、そのマスが何もないマスだった場合隣接するマスを開ける必要がある。
またその開けたマスも何もないマスであればまた数字マスが出るまで隣接するマスを開け続ける。