はじめに
数年前に作ったマインスイーパをアップデートして作り変えました。
今回追加した機能は下記の通りです。
・地雷以外のマスをすべて開くとクリアになる
・クリア時に爆弾を表示して他のセルをクリックできないように変更
・爆弾を引いた時に他のセルをクリックできないように変更
・数式バーを強制的に非表示に変更
・リセットボタンをセルからボタンに変更
・爆弾の数とマスのサイズを自分で自由に設定できるように変更
一個ずつ見ていきましょう。
1.クリア判定
爆弾以外の全てのマスを開くと、全ての爆弾が緑になりクリアとなります。
Private Sub CheClear()
Dim CCount As Integer ' 開いていないセル数
Dim idx_row As Integer ' 行インクリメント
Dim idx_clm As Integer ' 列インクリメント
For idx_row = 2 To (conVertical + 1)
For idx_clm = 2 To (conHorizonal + 1)
If Cells(idx_row, idx_clm).Interior.Color = RGB(200, 200, 200) Then
CCount = CCount + 1
End If
Next
Next
' 開いていないセル数が爆弾数以下の場合クリア
If CCount <= conBombs Then
For idx_row = 2 To (conVertical + 1)
For idx_clm = 2 To (conHorizonal + 1)
' セルが爆弾の場合
If Cells(idx_row, idx_clm).Value = "●" Then
Cells(idx_row, idx_clm).Interior.Color = RGB(150, 150, 150)
Cells(idx_row, idx_clm).NumberFormatLocal = ""
Cells(idx_row, idx_clm).Font.Color = RGB(50, 200, 50)
End If
Next 'idx_clm
Next 'idx_row
' セルを編集不可に変更
Range("B2").Resize(conVertical, conHorizonal).Locked = True
End If
End Sub
開いていないセルの数を数えて、その数が爆弾の数以下の場合クリアとしています。(もっとスマートな方法がありそうだけど……)
クリア時はセルの編集を不可にしています。LockedというパラメータがあるのでTrueにすれば編集不可になります。リセットボタンを押した時にLockedをFalseにして制御しています。
ちなみに、クリア時と同様に爆弾を引いた時もLockedをTrueにしてセルの編集を不可にしています。
2.数式バーを非表示にする
Excelを使用した弊害で、実はセルを複数選択することが可能になっています。
一応、セルを複数選択した時は処理を全て抜けるようには作ってあるのですが、
複数選択した後の最後のマスの中身が見えてしまっています。
正直、Excelを使っている以上、複数選択はどうしようもないのでどうしようか悩んだのですが……
消しちゃえ☆
……っていう結論に至りました。
コードは至ってシンプル。
Application.DisplayFormulaBar = False
ボタンを押す度にこの一文で強制的に数式バーを非表示にするだけ。簡単ですね。
3.爆弾の数とマスのサイズを変更できるようにする
まずこの機能を実装するにあたって問題になったのがリセットボタンです。当初はリセットボタンをセルに配置していたため、マスのサイズを大きくした際にリセットボタンを巻き込んでしまっていました。そのため、リセットボタンをボタンに置き換えています。
地味なところではリセットボタンを押した時にフォーカスをA1セルに移動するようにしています。
爆弾の数とマスの変更画面はこんな感じです。
シートを分けました。
無限にマスや爆弾を作られても困るので入力できる範囲を指定しています。マスは縦横それぞれ10~20。爆弾はマス数の合計の8割を最大にしています。(例:マスのサイズが10×10の場合、マスの合計は100なのでその8割である80個まで配置できる)
数式バーの戻し方を注意書きで書いておいてあげるの、ちょっとしたやさしさを感じません?……感じない?あ、そう……。
コードはこんな感じ。
Private Sub Worksheet_Change(ByVal Target As Range)
' 二回目のイベントは処理をしない
If ChangeFlg Then Exit Sub
' 描画停止
Application.ScreenUpdating = False
ChangeFlg = True
Select Case Target.Address
Case "$D$2" ' 爆弾の数
If IsNumeric(Target.Value) = False Then
' 数値ではない場合、15にして処理を抜ける
Target.Value = 15
GoTo continue2
End If
If Target.Value <= 1 Then
' 1以下の場合、1にして処理を抜ける
Target.Value = 1
GoTo continue2
End If
' 小数の場合、整数にする
' 全角を半角に変換
Target.Value = CInt(StrConv(Round(Target.Value), vbNarrow))
Case "$D$3" ' マス(縦)の数
If IsNumeric(Target.Value) = False Then
' 数値ではない場合、10にして処理を抜ける
Target.Value = 10
GoTo continue2
End If
If Target.Value <= 10 Then
' 10以下の場合、10にして処理を抜ける
Target.Value = 10
GoTo continue2
ElseIf Target.Value >= maxVertical Then
' 最大数以上の場合、最大数にして処理を抜ける
Target.Value = maxVertical
GoTo continue2
End If
' 小数の場合、整数にする
' 全角を半角に変換
Target.Value = CInt(StrConv(Round(Target.Value), vbNarrow))
Case "$D$4" ' マス(横)の数
If IsNumeric(Target.Value) = False Then
' 数値ではない場合、10にして処理を抜ける
Target.Value = 10
GoTo continue2
End If
If Target.Value <= 10 Then
' 10以下の場合、10にして処理を抜ける
Target.Value = 10
GoTo continue2
ElseIf Target.Value >= maxHorizonal Then
' 最大数以上の場合、最大数にして処理を抜ける
Target.Value = maxHorizonal
GoTo continue2
End If
' 小数の場合、整数にする
' 全角を半角に変換
Target.Value = CInt(StrConv(Round(Target.Value), vbNarrow))
End Select
continue2: 'Exit Selectがないためcontinueに飛ばす
' 爆弾数が全マス数の8割を越える場合、爆弾数を調整
If Range("$D$2").Value > (Range("$D$3").Value * Range("$D$4").Value * 0.8) Then
Range("$D$2").Value = CInt(Range("$D$3").Value * Range("$D$4").Value * 0.8)
End If
' 描画再開
Application.ScreenUpdating = True
ChangeFlg = False
End Sub
簡単に言ってるけど判定文ちゃんとしたらこれくらいになるよねって感じ。
一応、小数は切り捨てて、全角を半角にしてから、数値以外が入力された場合は最低数にしています。
変更が反映されるタイミングはリセットボタンを押したタイミングです。数値入力後にいちいち切り替えるよりは動きがスムーズになるだろうとの考えから。ちゃんと数値にしてないとリセットボタン押した時に例外処理で落ちてしまいます。
さいごに
結局、右クリックで旗を立てる処理は諦めました。Excelで作る以上はどこかしら妥協しないといけないところは出てきますね。
再三になりますが、こんなExcelを作って業務時間中に遊んではいけませんよ?間違っても次はテトリスとかリバーシを作ろうとか画策してるんじゃないよ?絶対だからね?