18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Javascript 簡単にマインスイーパーを作ってみる【初心者向け】

Last updated at Posted at 2023-09-07

はじめに

プログラミング初学者の学習用にマインスイーパーを作ってみました。
嘘です、マインスイーパーやったことなかったので、変なものができました。

以下説明のため作成した簡易版のURLです。
(ページ末尾に本気モードのURLもあります)

概要

この記事内で使用している技術は以下のとおりです。
・2重配列
・ループ文
・条件分岐
・クリックイベント
・ランダム関数

マインスイーパー作成必要な処理は以下のとおりです。
・ボード作成処理
・爆弾配置処理
・周囲の爆弾の数をパネルに表示する処理
・パネルクリック処理

2重配列・2重ループ文

2重配列・2重ループ文の説明です
※理解している方は読み飛ばしてください

2重配列

縦をy軸 横をx軸とすると
hoge[y][x]という形で呼び出せます

let sample =[
    [0-0, 0-1, 0-2],
    [1-0, 1-1, 1-2],
    [2-0, 2-1, 2-2],
];

例えば、上記のような配列があった場合、
sample[0][2] = '0-2'
sample[1][0] = '1-0'
sample[2][1] = '2-1'
となります。

2重ループ文

y軸のループ内にx軸のループを記述することで、y軸の階層ごとにx軸のループを回すことが可能です。

for(let y = 0; y < 3; y++){
    for(let x = 0; x < 3; x++){
        sample[y][x] = y-x;
    }
}

ボード作成処理

ゲームボード作成には2重配列とループ文を使用しています。
まずは9*9マスのボードを作成します。
ボードの中心からスタート可能で、外側に出ることができればゲームクリアという感じにしたいので、
board[4][4](ボードの中心) = 1に設定しています

// ボード(9*9マス)作成
let board = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0],
];

その後2重のループ文を使って、boardの要素を一つずつ読み込みゲームボードを作成します。

<table id="board" style="text-align: center;"></table>

(中略)

// ボードを生成する処理
let BoardTable = document.getElementById("board");
for (let y = 0; y < 9; y++) {
    let tr = document.createElement("tr");
    for (let x = 0; x < 9; x++) {
        let td = document.createElement("td");
        td.style.width = "50px";
        td.style.height = "50px";
        td.style.border = "solid 1px black";
        td.id = y + '-' + x;
        if (board[y][x] == 1) {
            td.style.backgroundColor = "white";
        } else {
            td.style.backgroundColor = "silver";
        }
        // 生成
        tr.appendChild(td);
    }
    BoardTable.appendChild(tr);
}

これにより、<table id="board" style="text-align: center;">内に次のようなボードが描画されます。

爆弾配置処理

先ほど作成した9*9マスのゲームボードにランダムに爆弾を配置する処理を作成します。
爆弾の配置はlet mines[]という配列にループ文とランダム関数を使って保存していきます。
ランダム関数Math.floor(Math.random() * 100);は0~99のランダムな数字を生成します。

そのため、以下の条件分岐で各マスに15%の確率で爆弾が設置される処理を記述できます。
if(Math.floor(Math.random() * 100) < 15)

let mines = []; // からの配列を作成
for (let y = 0; y < 9; y++) {
    mines[y] = []; // y行目の配列を作成
    for (let x = 0; x < 9; x++) {
        if (board[y][x] == 1) {
            mines[y][x] = 0;
        }
        else {
            rand = Math.floor(Math.random() * 100); // 0~99 までのランダムな数
            if (rand < 15) {
                mines[y][x] = 1; // 爆弾あり
            } else {
                mines[y][x] = 0; // 爆弾なし
            }
        }
    }
}

これで各マスに爆弾があるマス、ないマスを作成することができました。

周囲の爆弾の数をパネルに表示する処理

爆弾カウント処理

それでは、先ほど設置した爆弾が周囲8マスに何個あるかカウントしてパネルに表示する処理を作成します。
まずは周囲8マスの爆弾の個数をカウントする処理を作りましょう

// 周りの爆弾の数を数える処理
let minesAround = [];
for (let y = 0; y < 9; y++) {
    minesAround[y] = [];
    for (let x = 0; x < 9; x++) {
        let counter = 0;
        if (y != 0 && x != 0) counter += mines[y - 1][x - 1];   // 左上
        if (y != 0) counter += mines[y - 1][x];                 // 上
        if (y != 0 && x != 8) counter += mines[y - 1][x + 1];   // 右上
        if (x != 0) counter += mines[y][x - 1];                 // 左
        if (x != 8) counter += mines[y][x + 1];                 // 右
        if (y != 8 && x != 0) counter += mines[y + 1][x - 1];   // 左下
        if (y != 8) counter += mines[y + 1][x];                 // 下
        if (y != 8 && x != 8) counter += mines[y + 1][x + 1];   // 右下
        minesAround[y][x] = counter;
    }
}

minesAround[]という配列にカウントを格納していくことでマスの位置ごとにカウントが分かります
panel[y][x]を中心とした周囲8マスを調べます
周囲8マスにアクセスしたい場合以下のルールを参照してください

y軸上で上方向は`y-1` 下方向は`y+1`
x軸上で左方向は`x-1` 右方向は`x+1`

必ずしも周囲に8マスある訳ではないため、以下のルールも参照しておきましょう

例えば、該当マスが panel[0][8](一番右上)の場合
y軸上方向 x軸右方向にマスは存在しません
そういう場合を考慮して以下のような条件分岐を作ります

if(y == 0) // y軸の一番上の時
if(x == 8) // x軸の一番右の時

今回のコードでは上記の条件分岐に該当しない場合に処理を行う設定にしています。

カウントをパネルに表示する処理

showCounter(y, x)という関数を作成しています。
関数に y , x の情報を渡すことで、
どのマスの周囲8マスのパネルにカウントを表示すればいいのかを指示することができます。

function showCounter(y, x) {
    let counter = 0;
    for (let y = 0; y < 9; y++) {
        for (let x = 0; x < 9; x++) {
            if (board[y][x] == 1) {
                if (y != 0 && x != 0) {
                    let td = document.getElementById((y - 1) + '-' + (x - 1));
                    td.innerText = minesAround[y - 1][x - 1];
                }
                if (y != 0) {
                    let td = document.getElementById((y - 1) + '-' + (x));
                    td.innerText = minesAround[y - 1][x];
                }
                if (y != 0 && x != 8) {
                    let td = document.getElementById((y - 1) + '-' + (x + 1));
                    td.innerText = minesAround[y - 1][x + 1];
                }
                if (x != 0) {
                    let td = document.getElementById((y) + '-' + (x - 1));
                    td.innerText = minesAround[y][x - 1];
                }
                if (x != 8) {
                    let td = document.getElementById((y) + '-' + (x + 1));
                    td.innerText = minesAround[y][x + 1];
                }
                if (y != 8 && x != 0) {
                    let td = document.getElementById((y + 1) + '-' + (x - 1));
                    td.innerText = minesAround[y + 1][x - 1];
                }
                if (y != 8) {
                    let td = document.getElementById((y + 1) + '-' + (x));
                    td.innerText = minesAround[y + 1][x];
                }
                if (y != 8 && x != 8) {
                    let td = document.getElementById((y + 1) + '-' + (x + 1));
                    td.innerText = minesAround[y + 1][x + 1];
                }
            }
        }
    }
}

showCounter(4, 4); // スタート地点の周囲のカウントを表示

showCounter(4, 4)をことで行うことで
スタート地点の周囲8マスのカウンターは表示された状態になっています。

クリック関数作成

クリック時に行う処理
・周囲8マスのカウンターを表示
・上下左右4マスをクリック可能にする
・ボードの端にたどり着いたらゲームクリア
・パネルが爆弾だった場合ゲームオーバー

function ClickPanel(y, x) {
    board[y][x] = 1;
    let td = document.getElementById(y + '-' + x);
    td.style.backgroundColor = "white";
    if (mines[y][x] == 1) {
        td.innerText = "爆";
        alert("ゲームオーバー");
    } else {
        td.innerText = '';
        showCounter(y, x); // 関数呼び出し
        if (y == 0 || x == 0 || y == 8 || x == 8) {
            let screen = document.getElementById("screen");
            screen.innerText = "ゲームクリア次のステージに進む";
        }
    }
}

ClickPanel(y, x)関数内でshowCounter(y, x)を呼び出すことで、
周囲8マスのカウンターを表示することができます。

上下左右の4マスをクリック可能にする処理はshowCounter(y, x)内に追記します

以下追記後のコードです

function showCounter(y, x) {
    let counter = 0;
    for (let y = 0; y < 9; y++) {
        for (let x = 0; x < 9; x++) {
            if (board[y][x] == 1) {
                if (y != 0 && x != 0) {
                    let td = document.getElementById((y - 1) + '-' + (x - 1));
                    td.innerText = minesAround[y - 1][x - 1];
                }
                if (y != 0) {
                    let td = document.getElementById((y - 1) + '-' + (x));
                    td.innerText = minesAround[y - 1][x];
                    td.onclick = function () {
                        ClickPanel(y - 1, x);
                    };
                    td.style.cursor = "pointer";
                }
                if (y != 0 && x != 8) {
                    let td = document.getElementById((y - 1) + '-' + (x + 1));
                    td.innerText = minesAround[y - 1][x + 1];
                }
                if (x != 0) {
                    let td = document.getElementById((y) + '-' + (x - 1));
                    td.innerText = minesAround[y][x - 1];
                    td.onclick = function () {
                        ClickPanel(y, x - 1);
                    };
                    td.style.cursor = "pointer";
                }
                if (x != 8) {
                    let td = document.getElementById((y) + '-' + (x + 1));
                    td.innerText = minesAround[y][x + 1];
                    td.onclick = function () {
                        ClickPanel(y, x + 1);
                    };
                    td.style.cursor = "pointer";
                }
                if (y != 8 && x != 0) {
                    let td = document.getElementById((y + 1) + '-' + (x - 1));
                    td.innerText = minesAround[y + 1][x - 1];
                }
                if (y != 8) {
                    let td = document.getElementById((y + 1) + '-' + (x));
                    td.innerText = minesAround[y + 1][x];
                    td.onclick = function () {
                        ClickPanel(y + 1, x);
                    };
                    td.style.cursor = "pointer";
                }
                if (y != 8 && x != 8) {
                    let td = document.getElementById((y + 1) + '-' + (x + 1));
                    td.innerText = minesAround[y + 1][x + 1];
                }
            }
        }
    }
}

これでマインスイーパーに必要な処理の作成が完了しました。

GitHub

以下はgithubのコードです、参考にして見てください。

最後に

配列、ループ文、条件分岐を覚えるとコードの書きやすさが変わります。
今回のサンプルコードを使って色々遊んでみてください。

サンプルコード改造例として、
マインスイーパーhardモードのURLも貼っておきますので遊んでみてください

読んでいただきありがとうございました!

宣伝

普段は株式会社バイネームで開発業務を行っています。
興味を持っていただけたら以下のリンクからアクセスして見てください!

18
13
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?