概要
これまで5回にわたり、VBAでオセロ(リバーシ)の盤面生成、石の配置、そして最も複雑な「石を裏返す処理」を実装してきました。連載第6回となる今回は、オセロゲームの核心である「ゲーム終了判定」と「勝敗の集計」、そして「パスの判定」という、プログラムの論理的な締めくくりに焦点を当てます。オセロは単に石を置くだけのゲームではありません。盤面がすべて埋まるか、あるいは両者ともに打てる場所がなくなった瞬間にゲームが終了するという厳格なルールがあります。この「ゲームの終焉」をいかにスマートに判定し、ユーザーに結果を伝えるか。VBAのロジックを磨く絶好の機会です。
詳細解説
オセロにおけるゲーム終了の条件は、大きく分けて二つあります。一つは「盤面がすべて石で埋まった場合」、もう一つは「現在のプレイヤーが打てる場所がなく、かつ相手プレイヤーも打てる場所がない場合」です。特に後者は、初心者が見落としがちなエッジケースです。
1. 盤面の全マス走査:
まずは、現在盤面に空きマス(0の状態)があるかをチェックします。すべてのマスが1(黒)または2(白)で埋まっていれば、その時点でゲームは終了です。
2. 打てる場所の有無判定:
第3回で作成した「打てる場所を判定するロジック」を再利用します。現在の手番プレイヤーが打てる場所を全マス走査し、見つからない場合は「パス」となります。ここで重要なのは、現在のプレイヤーがパスをした後に、相手プレイヤーも同様に打てる場所があるかを判定することです。両者ともに打てる場所がゼロであれば、ゲームは即座に終了となります。
3. 勝敗集計:
ゲーム終了時、盤面にある1と2の数をカウントし、その数値を比較して勝敗を決定します。ExcelのRangeオブジェクトから直接セルの値を読み取り、COUNTIF関数をVBA内で活用することで、非常に高速かつ短く記述することが可能です。
サンプルコード
以下のコードを標準モジュールに追加してください。これまでの連載で構築したグローバル変数やサブルーチンを前提としています。
' ゲーム終了判定と勝敗集計のメインルーチン
Sub CheckGameOverAndResult()
Dim blackCount As Long
Dim whiteCount As Long
Dim canCurrentPlayerMove As Boolean
Dim canOpponentMove As Boolean
' 現在の手番と相手の手番の確認(仮にグローバル変数 CurrentPlayer を使用)
' 打てる場所があるか確認(既存のCheckLegalMove関数を利用)
canCurrentPlayerMove = HasAnyLegalMove(CurrentPlayer)
canOpponentMove = HasAnyLegalMove(3 - CurrentPlayer)
' 盤面が埋まっているかチェック
If IsBoardFull() = False Then
' まだ打てる場所があるか確認
If canCurrentPlayerMove Then
' まだゲームは続く
Exit Sub
Else
' パスの処理
If canOpponentMove Then
MsgBox "打てる場所がありません。パスします。"
CurrentPlayer = 3 - CurrentPlayer
Exit Sub
Else
' 両者ともに打てない場合はゲーム終了へ
GoTo EndGame
End If
End If
End If
EndGame:
' 最終集計
blackCount = Application.WorksheetFunction.CountIf(Range("B2:I9"), 1)
whiteCount = Application.WorksheetFunction.CountIf(Range("B2:I9"), 2)
' 結果表示
If blackCount > whiteCount Then
MsgBox "ゲーム終了!黒の勝利!" & vbCrLf & "黒: " & blackCount & " 白: " & whiteCount
ElseIf whiteCount > blackCount Then
MsgBox "ゲーム終了!白の勝利!" & vbCrLf & "黒: " & blackCount & " 白: " & whiteCount
Else
MsgBox "引き分けです!"
End If
End Sub
' 盤面がすべて埋まっているか判定する関数
Function IsBoardFull() As Boolean
Dim rng As Range
Set rng = Range("B2:I9")
' 0(空きマス)が一つもなければTrue
If Application.WorksheetFunction.CountIf(rng, 0) = 0 Then
IsBoardFull = True
Else
IsBoardFull = False
End If
End Function
実務アドバイス
VBAでゲーム開発を行う際、最も重要なのは「状態の可視化」です。今回のコードではMsgBoxを使用していますが、実務的なアプリケーションであれば、Excelシート上の特定のセル(例えば「状況表示欄」など)に結果を書き出す設計にすべきです。
また、`Application.WorksheetFunction`の使用は、VBAのループ処理よりも圧倒的に高速です。セル範囲を走査して値を数えるような処理を自前でFor-Nextループを書いて行うと、どうしても処理速度が低下しがちです。Excelの組み込み関数をVBAから呼び出すテクニックは、業務効率化ツールを作成する際にも非常に強力な武器となります。
さらに、今回のロジックを実装する上で「なぜここでGoTo文を使ったのか」と疑問に思う方もいるかもしれません。状態遷移が複雑なゲームロジックにおいて、特定の条件下で強制的に終了処理へ分岐させるGoToは、可読性を維持するための「脱出用」として非常に有効です。ただし、乱用は厳禁です。あくまで「ゲーム終了」という明確な終了地点への誘導のみに限定して使用しましょう。
まとめ
今回の第6回では、オセロの完成に向けた最後の砦である「終了判定」を実装しました。これにより、ただ石を置くだけのプログラムが、一つの「ゲーム」として完結するようになります。
1. ゲーム終了条件を正しく定義し、分岐させること。
2. ワークシート関数の利点を活かして、集計処理を高速化すること。
3. パス処理を組み込み、プレイヤーの操作不能状態をケアすること。
これら三つの要素は、オセロに限らず、あらゆるボードゲームやシミュレーションツールを作成する際の基本となる考え方です。ここまでで、オセロの基本機能は概ね完成しました。次回は、よりユーザーインターフェースを洗練させ、クリック操作を直感的にするための「イベント駆動型プログラミング」への応用編を予定しています。VBAの可能性は、単なる事務処理の自動化に留まりません。論理を組み立て、形にしていく喜びをぜひこのオセロ制作を通じて感じ取ってください。次回のステップアップにも期待していてください。
