文系人間がプログラミングをはじめてみた。

プログラミング初心者のゼロからの勉強記録。

マクロで作ったマインスイーパーの解説 詳細編 ゲームの状態を判定する

どうもこんにちは。

 

今日もマクロで作ったマインスイーパーのコード解説をしていきます。

どんな感じのマインスイーパーになっているかはこちらからご覧ください。

programminghajimetemita.hatenablog.com

 

今回はゲームの状態を判定する関数について解説したいと思います。

では内容に入ります。

 

 

1.コードの内容

まずコードの全体の内容をご紹介します。

ゲームの状態の判定として、ゲームオーバーとゲームクリアの2種類の判定を行っています。

それぞれマインスイーパーのメインのマクロ内で以下のように利用しています。

ゲームオーバーは赤太字部分、ゲームクリアは青太字部分です。

ゲームオーバーを判定する関数(CheckGameOver)のコードは以下のとおりです。

ゲームクリアを判定する関数(CheckGameClear)のコードは以下のとおりです。

 

2.マインスイーパーの挙動

マインスイーパーのゲームオーバーとゲームクリアの挙動についてです。

 

まず、ゲームオーバーについてですが、マスを開く動作を行い、爆弾マスを開いてしまったときにゲームオーバーになります。

そのため、開いたマスが爆弾マスかどうかを判定することがゲームオーバーの判定になります。

 

ゲームオーバーと判定されると、すべてのマスがオープンされ、ゲームオーバーのメッセージが表示されます。

 

次に、ゲームクリアについてです。

ゲームクリアは爆弾マス以外のマスをすべて開き、かつ、すべての爆弾マスにフラグを設置したときにゲームクリアと判定する仕様としました。
(本来のマインスイーパーでは爆弾マス以外のマスをすべて開くとゲームクリアになると思うので、若干異なる仕様となっています)

 

ゲームクリアと判定されると、ゲームクリアのメッセージが表示され、クリア時のタイムが〇分〇秒の形で表示されます。

 

 

以上のまとめです。

 

■ ゲームオーバー

<判定>

・爆弾マスを開くとゲームオーバーと判定する

<ゲームオーバー時の処理>

・すべてのマスを開く

・ゲームオーバーのメッセージを表示する

 

■ ゲームクリア

<判定>

・以下2つの条件を満たすとゲームクリアと判定する

  a. 爆弾マス以外のすべてのマスを開く

  b. すべての爆弾マスにフラグを設置する

<ゲームクリア時の処理>

・ゲームクリアのメッセージを表示する

・クリア時の経過時間を"〇分〇秒"の形で表示する

 

ではコードの内容解説に入ります。

 

3.コードの内容解説

ゲームオーバーのコードから解説します。

まずはゲームオーバーの判定部分です。1.で紹介したCheckGameOver関数が当該判定を制御しています。

この関数では、マス全体を範囲に、マスの背景色が赤色のものを検索し、

ヒットするマスがあれば、Trueを返すという処理を行っています。

 

ここでやりたいことは、"爆弾マスを開くとゲームオーバーと判定すること"ですので、

爆弾マスを開いたときにそのマスの背景色が赤色になることを利用して、上記のようにコードを組みました。

 

なお、繰返処理Do~Loopの中に当該判定を組み込んでおり(1.参照)、

その判定の直前にマスを開く操作を処理するマクロを実行しているので、

マスを開くたびに、ゲームオーバーの判定が行われるようになっています。

(マスを開く⇒ゲームオーバーの判定⇒マスを開く⇒判定・・・の繰返し)

これで、本物のマインスイーパーと同様、爆弾マスを開いた瞬間ゲームオーバーになる動きが実現できています。

 

では関数のコードの詳細解説に戻ります。

まず、Dim~の部分で検索用の変数を宣言しています。

 

その次のWith~End Withの部分では書式検索を行うときの書式を指定しています。

先ほど述べたように、背景色赤色のマスを検索したいので、

検索処理を行う前に検索対象の書式を指定しているわけです。

 

なお、検索対象を爆弾アイコンとしてしまうと、爆弾マスが未オープンの状態でも検索に引っかかってしまい適切な判定とならないので、

背景色で検索を行うようにしています。

 

Set red_cell~の部分が背景色赤色のマスを検索する処理になります。

 

その次のIf~の部分では、Set red_cell~の部分で検索にヒットしたマスがあるかを判定しており、

ヒットしたマスがあれば、Trueを返す(CheckGameOverをTrueにする)処理を行っています。

 

以上で関数の解説を終わります。

次に、当該関数でTrueが返されたときの処理について解説します。

2.で触れたとおり、ゲームオーバーになったときは、

・すべてのマスを開く

・ゲームオーバーのメッセージを表示する

の2つを行わせる必要があります。

 

そのために、まずこのIfの部分で、ゲームオーバーと判定したとき(=CheckGameOverがTrueとなったとき)に、

Then以下の処理を行わせる、という条件分岐を設定しています。

Then以下の処理が上記2つの処理になっています。

 

Then以下の処理1つ目、すべてのマスを開く処理です。

一行目のRange~ではNumberFormatLocalで表示形式を標準に変更し、

各マスの値が画面上表示されるようにしています。

 

次に、三行目のFor Each~の部分ではマスの値ごとに背景色を変更する処理を行っています。

爆弾マスは赤色、フラグマスは黄色、それ以外は灰色にする必要がありますので、

マスの値で場合分けを行いながらマス一つずつの背景色変更を繰り返し実行しています。

 

なお、Application.ScreenUpdating~の部分はマクロの処理速度向上のために入れている処理になります。

 

次に、ゲームオーバーのメッセージを表示する部分です。

上記記述により""で囲んだ部分がポップアップメッセージとして表示されます。

 

上記2つの処理完了後、上記コードにより繰返処理Do~Loopを終了しています。

一度ゲームオーバーになれば繰返処理を行う必要はなくなるので、

このコードで終了させるようにしています。

 

以上でゲームオーバーの処理の解説を終わります。

 

 

続いて、ゲームクリアの処理について解説します。

まずはゲームクリアの判定です。この判定の制御はCheckGameClear関数で行っています。

この関数では以下の3つの処理を行っています。

・マス全体を範囲に非表示のマス(表示形式が";;;"のマス)を検索する

・非表示マスがないとき、爆弾マスが0個になっているか判定する

・爆弾マスが0個のとき、CheckGameClearをTrueにする

 

ゲームクリアと判定する条件は以下の2つと述べました。

a. 爆弾マス以外のすべてのマスを開く

b. すべての爆弾マスにフラグが設置されている

 

まず、a.については、マスが開かれている=表示形式が";;;"でない、となるので、

これを利用して表示形式";;;"のマスを検索し、ヒットしないときに関数内の次の処理を実行させるようにしました。

 

次にb.については、フラグが設置されるとそのマスの値がフラグに変わる、という仕様を利用しました。

爆弾マスにフラグが設置されると、そのマスの値は爆弾からフラグに変わります。

ですので、すべての爆弾マスにフラグが設置されたときマスの範囲内にある爆弾の個数は0個になるはずです。

 

したがって、マスの範囲内の爆弾の個数をカウントし、カウント=0であればb.の条件を満たすことになるので、

そうなったときにCheckGameClearをTrueにする処理を行わせるようにしました。

 

ということで、この関数ではa. とb. の条件を満たしたときにゲームクリアと判定する(CheckGameClearをTrueにする)挙動を実現しています。

 

ではコードの詳細について見ていきたいと思います。

まず、Dim~の部分で検索用の変数gray_cellを宣言しています。

 

次にWith~End Withの部分で書式検索時の書式を設定しています。

表示形式が";;;"のマスを検索したいので上記のようなコードになります。

 

その次のSet~部分は書式検索の処理になります。

この処理により指定書式(表示形式";;;")のマスを検索しています。

 

その後のIf~の部分では、検索でヒットしないときにThen以下の処理を行わせるという条件分岐を行っています。

検索を行ったとき、ヒットするものがなければ、"gray_cell = Nothing"という状態になるので、

これを利用した条件分岐になります。

 

検索でヒットするものがなかったときに二つ目のIf~が実行されます。

ここでは、マスの範囲内の爆弾の個数が0個かどうかを判定しています。

爆弾の個数をCOUNTIF関数でカウントし、カウント結果=0かを条件にしているわけです。

 

最後に、上記条件を満たしたときに、CheckGameClearをTrueにする処理を行っています。

 

以上で関数で行う処理の解説を終わります。

続いてゲームクリア判定時(CheckGameClear=True)の処理です。

まず、ゲームクリアと判定したときに処理を行わせたいので、

一行目のIf~の条件分岐でCheckGameClear=Trueを条件に設定しています。

これでゲームクリア判定時にThen以下の処理を行わせることができます。

 

ゲームクリア時には、ゲームクリアのメッセージとクリア時の経過時間を表示させたいので、

MsgBoxを利用して当該メッセージを表示しています。

 

MsgBoxの""内の、「Game Clear!" & vbCrLf & "おめでとう!」という部分がクリアメッセージです。(vbCrLfとあるのは改行を意味します)

 

クリア時の経過時間部分は、

「Clear Time:" & Int(t / 60) & "分" & t - Int(t / 60) * 60 & "秒」で表示しています。

 

経過時間を〇分〇秒という形で表示するために、

経過時間を示す変数tを60で割ることで〇分の部分を計算し、

変数tから経過分数に対応する秒数(Int(t/60)*60)を引くことで〇秒の部分を計算しています。

 

例えば、t=379を〇分〇秒の形式で表すと6分19秒になりますが、

上記処理に当てはめて考えると、

〇分部分 = Int(t/60)= 6

〇秒部分 = t - Int(t/60)*60 = 379 - 6*60 = 379 - 360 = 19、となります。

正しく計算できていることが確認できました。

 

以上でゲームクリア時の処理の解説を終わります。

 

4.特に重要なコード

今回も特に重要なコードはないのでこの部分は割愛します。

 

今回の処理では、挙動で実現したい状態(爆弾マスを開いたらゲームオーバーと判定するなど)を

エクセル上の処理に置き換えたらどんな状態といえるのかを考えることが肝になっていますので、

そのあたりを意識しながら3.の内容を見ていただけたらと思います。

 

5.おわりに

今回はゲームの状態の判定について解説しました。

今回の解説をもってマインスイーパーの解説を終わります。

次からはまた別のゲームのコード解説を行っていきたいと思います。

ではまた。