【VBAリファレンス】VBAサンプル集Excel囲碁:万波奈穂先生に捧ぐ

スポンサーリンク

### 概要:Excel VBAによる囲碁AI開発への挑戦と万波奈穂先生への敬意

本稿では、Excel VBAという、一見すると高度なAI開発とは縁遠いように思われる環境を用いて、囲碁の対局をシミュレートするプログラム開発の可能性を探求します。特に、若手女流棋士として期待される万波奈穂先生に敬意を表し、その情熱と才能に触発された技術的な挑戦として、このテーマを取り上げます。

囲碁AIの開発は、深層学習などの最先端技術が主流となりつつありますが、VBAという制約の多い環境下で、基本的な囲碁のルールに基づいた着手判断ロジックを実装することは、プログラミングの基礎力、アルゴリズムの理解、そして何よりも粘り強い試行錯誤を養うための優れた教材となり得ます。本記事では、VBAで囲碁プログラムを作成する上での基本的な考え方、実装上の課題、そして具体的なサンプルコードを提示することで、読者の皆様が自身のスキルアップや、あるいは趣味のプログラミングとして、この挑戦に踏み出す一助となることを目指します。

### 詳細解説:Excel VBAで囲碁プログラムを実装する技術的アプローチ

Excel VBAで囲碁プログラムを開発するにあたり、まず考慮すべきは「盤面の表現」と「着手判断ロジック」の二つの主要な要素です。

#### 盤面の表現

Excelのワークシートは、二次元のマス目にデータを格納するのに非常に適しています。囲碁の盤面もまた二次元のグリッドであるため、ワークシートのセルをそのまま盤面として利用するのが最も直感的で実装しやすい方法です。

* **セルの値**: 各セルに、黒石(例: 1)、白石(例: 2)、空点(例: 0)といった数値を格納します。
* **セルの色**: セルの背景色を黒、白、または無色に設定することで、視覚的に盤面を表現することも可能です。これにより、ユーザーはゲームの進行状況を容易に把握できます。
* **盤面のサイズ**: 囲碁の標準的な盤面サイズは19×19ですが、VBAでの開発においては、まずは9×9や13×13といった小規模な盤面から着手するのが現実的です。これにより、計算負荷やコードの複雑さを抑え、デバッグも容易になります。

#### 着手判断ロジック

VBAで囲碁の着手判断ロジックを実装する際に、最も基本的なアプローチは「ルールベース」です。これは、囲碁の基本的なルール(石の配置、取れる石の判定、ダメヅマリ、コウ)をプログラムで忠実に再現していく方法です。

1. **合法手の判定**:
* 指定されたマスに石を置くことができるか(既に石がないか、ダメヅマリでないか)を判定します。
* **ダメヅマリの判定**: 石を置いた際に、その石またはその石に隣接する同色の石群(呼吸点)がすべて塞がれてしまう場合、その着手は無効となります。隣接する空点(ダメ)の数を数えることで判定します。
* **コウの判定**: 直前の相手の着手によって取られた石と同じ状態に盤面が戻るような着手は、コウとして禁止されます。直前の盤面状態を保存しておく必要があります。

2. **石を取る判定**:
* 石を置いた際に、その石に隣接する相手の石群のダメがなくなった場合、その相手の石群は盤上から取り除かれます。
* この処理は、石を置いた直後、およびその石に隣接する同色の石群のダメがなくなった場合に、再帰的に、またはキューを用いた幅優先探索(BFS)などで実装するのが一般的です。

3. **着手候補の生成と評価**:
* **単純な着手**: 空いているマスに片っ端から着手し、その結果(取れる石の数など)を評価する。
* **基本的な防御・攻撃**:
* 自分の石群のダメを増やす(生きるための手)。
* 相手の石群のダメを減らす(相手を殺すための手)。
* **簡単な定石の導入**: 特定の局面で有効な定石パターンをいくつかプログラムに組み込む。
* **乱数による着手**: 評価値が同程度の場合、ランダムに手を選ぶことで、単調な思考を避ける。

#### 実装上の課題と工夫

* **計算量**: 盤面が大きくなると、合法手判定や石を取る判定の計算量が増大します。特に、複雑な局面での評価や、複数手先の読み込み(ミニマックス法など)をVBAで行うのは、パフォーマンスの観点から非常に困難です。
* **メモリ制限**: VBAはExcelのプロセス内で動作するため、メモリ使用量には限界があります。盤面の状態や過去の棋譜などを大量に保持しようとすると、パフォーマンスが著しく低下する可能性があります。
* **ユーザーインターフェース**: VBAはExcelのGUI機能を利用できるため、盤面の表示やマウスでの着手、棋譜の保存・読み込みといったインターフェースは比較的容易に実装できます。

#### サンプルコードの考え方

以下に示すサンプルコードは、非常に基本的な着手判断ロジックに焦点を当てています。実際の囲碁AIとして実用的なレベルには到底及びませんが、VBAで囲碁プログラムを構築する上での「入り口」となることを意図しています。

* **盤面データ**: `Array` を使用して、各セルの状態(0:空, 1:黒, 2:白)を保持します。
* **着手関数**: `PlaceStone` 関数で、指定された座標に石を置きます。
* **石取り判定関数**: `CheckAndRemoveStones` 関数で、石を置いた後に取れる石がないかを確認し、あれば盤面から削除します。
* **ダメ計算関数**: `CountLiberties` 関数で、指定された石群のダメ(呼吸点)の数を数えます。
* **合法手判定関数**: `IsValidMove` 関数で、着手が合法であるかを判定します。

### サンプルコード:Excel VBAによる囲碁の基本機能実装

ここでは、9×9の盤面を想定し、石の配置、石を取る機能、そして簡単な合法手判定を行うVBAコードの例を示します。このコードは、Excelのシートモジュールや標準モジュールに記述して使用することを想定しています。

Option Explicit

‘ 盤面のサイズ (9×9)
Const BOARD_SIZE As Integer = 9

‘ セルの状態定数
Const EMPTY As Integer = 0
Const BLACK As Integer = 1
Const WHITE As Integer = 2

‘ 盤面データを保持するグローバル変数 (またはモジュールレベル変数)
‘ 実際にはワークシートのセルと連動させるのが一般的ですが、ここでは簡略化のため配列で表現します。
Dim board(1 To BOARD_SIZE, 1 To BOARD_SIZE) As Integer

‘ 直前の盤面状態を保持 (コウ判定用)
Dim previousBoard(1 To BOARD_SIZE, 1 To BOARD_SIZE) As Integer

‘ 直前の着手座標 (コウ判定用)
Dim lastMoveCol As Integer
Dim lastMoveRow As Integer

‘ ————— 初期化処理 —————

‘ 盤面を初期化するプロシージャ
Sub InitializeBoard()
Dim r As Integer, c As Integer
For r = 1 To BOARD_SIZE
For c = 1 To BOARD_SIZE
board(r, c) = EMPTY
previousBoard(r, c) = EMPTY ‘ 初期化
Next c
Next r
lastMoveCol = 0 ‘ 初期化
lastMoveRow = 0 ‘ 初期化
‘ 必要に応じて、Excelシートのセルも初期化する処理を追加
‘ 例: Range(“A1”).Resize(BOARD_SIZE, BOARD_SIZE).ClearContents
‘ Range(“A1”).Resize(BOARD_SIZE, BOARD_SIZE).Interior.ColorIndex = xlNone
End Sub

‘ ————— 着手処理 —————

‘ 指定された座標に石を置くメインのプロシージャ
Sub MakeMove(col As Integer, row As Integer, player As Integer)
If Not IsValidMove(col, row, player) Then
MsgBox “無効な着手です。”, vbExclamation
Exit Sub
End If

‘ 直前の盤面状態を保存
SaveCurrentBoardState

‘ 石を配置
board(row, col) = player

‘ 置いた石によって取れる相手の石をチェック
Dim opponent As Integer
opponent = IIf(player = BLACK, WHITE, BLACK)
CheckAndRemoveStones col, row, opponent

‘ コウ判定のための直前の着手情報を更新
lastMoveCol = col
lastMoveRow = row

‘ 必要に応じて、Excelシートのセルに石を反映する処理を追加
‘ 例: Cells(row, col).Value = player
‘ Cells(row, col).Interior.Color = IIf(player = BLACK, vbBlack, vbWhite)

‘ デバッグ用: 盤面表示 (コンソールまたはイミディエイトウィンドウ)
‘ DebugPrintBoard
End Sub

‘ ————— 石取り判定 —————

‘ 指定された座標(col, row)に置かれた石(player)によって取れる相手の石をチェックし、削除する
Sub CheckAndRemoveStones(col As Integer, row As Integer, opponent As Integer)
Dim directions(1 To 4, 1 To 2) As Integer
directions(1, 1) = 0: directions(1, 2) = -1 ‘ 上
directions(2, 1) = 0: directions(2, 2) = 1 ‘ 下
directions(3, 1) = -1: directions(3, 2) = 0 ‘ 左
directions(4, 1) = 1: directions(4, 2) = 0 ‘ 右

Dim removedCount As Integer
removedCount = 0

Dim i As Integer
Dim neighborCol As Integer, neighborRow As Integer
Dim liberties As Integer

‘ 周囲4方向の相手の石群をチェック
For i = 1 To 4
neighborCol = col + directions(i, 1)
neighborRow = row + directions(i, 2)

‘ 盤面の範囲内かチェック
If neighborRow >= 1 And neighborRow <= BOARD_SIZE And neighborCol >= 1 And neighborCol <= BOARD_SIZE Then ' 隣接するマスが相手の石で、かつその石群のダメが0であれば、石を取る If board(neighborRow, neighborCol) = opponent Then liberties = CountLibertiesForGroup(neighborCol, neighborRow, opponent) If liberties = 0 Then ' この石群は取れる removedCount = removedCount + RemoveStonesAt(neighborCol, neighborRow, opponent) End If End If End If Next i ' 取った石の数を返す (必要であれば) ' Debug.Print removedCount & "個の石が取られました。" End Sub ' 指定された座標(col, row)から始まる相手の石群のダメ(呼吸点)の数を数える Function CountLibertiesForGroup(startCol As Integer, startRow As Integer, stoneColor As Integer) As Integer Dim liberties As Integer liberties = 0 Dim visited(1 To BOARD_SIZE, 1 To BOARD_SIZE) As Boolean Dim queue(1 To BOARD_SIZE * BOARD_SIZE, 1 To 2) As Integer ' BFS用のキュー Dim head As Integer, tail As Integer ' 初期化 Dim r As Integer, c As Integer For r = 1 To BOARD_SIZE For c = 1 To BOARD_SIZE visited(r, c) = False Next c Next r head = 1 tail = 1 ' 開始点をキューに追加 queue(tail, 1) = startCol queue(tail, 2) = startRow visited(startRow, startCol) = True tail = tail + 1 Dim directions(1 To 4, 1 To 2) As Integer directions(1, 1) = 0: directions(1, 2) = -1 ' 上 directions(2, 1) = 0: directions(2, 2) = 1 ' 下 directions(3, 1) = -1: directions(3, 2) = 0 ' 左 directions(4, 1) = 1: directions(4, 2) = 0 ' 右 Dim currentCol As Integer, currentRow As Integer Dim nextCol As Integer, nextRow As Integer Dim i As Integer ' キューが空になるまで処理 While head < tail currentCol = queue(head, 1) currentRow = queue(head, 2) head = head + 1 ' 周囲4方向をチェック For i = 1 To 4 nextCol = currentCol + directions(i, 1) nextRow = currentRow + directions(i, 2) ' 盤面の範囲内かチェック If nextRow >= 1 And nextRow <= BOARD_SIZE And nextCol >= 1 And nextCol <= BOARD_SIZE Then If board(nextRow, nextCol) = EMPTY Then ' ダメが見つかった If Not IsInLiberties(currentCol, currentRow, nextCol, nextRow, visited) Then ' 重複カウント防止 liberties = liberties + 1 ' ダメ自体は訪問済みリストに追加しない(別の石群のダメになる可能性があるため) End If ElseIf board(nextRow, nextCol) = stoneColor Then ' 同じ色の石で、まだ訪問していない場合 If Not visited(nextRow, nextCol) Then visited(nextRow, nextCol) = True queue(tail, 1) = nextCol queue(tail, 2) = nextRow tail = tail + 1 End If End If End If Next i Wend CountLibertiesForGroup = liberties End Function ' 指定された座標(col, row)にある、指定された色の石群を盤上から削除する Function RemoveStonesAt(startCol As Integer, startRow As Integer, stoneColor As Integer) As Integer Dim removedCount As Integer removedCount = 0 Dim visited(1 To BOARD_SIZE, 1 To BOARD_SIZE) As Boolean Dim queue(1 To BOARD_SIZE * BOARD_SIZE, 1 To 2) As Integer Dim head As Integer, tail As Integer Dim r As Integer, c As Integer For r = 1 To BOARD_SIZE For c = 1 To BOARD_SIZE visited(r, c) = False Next c Next r head = 1 tail = 1 queue(tail, 1) = startCol queue(tail, 2) = startRow visited(startRow, startCol) = True tail = tail + 1 Dim directions(1 To 4, 1 To 2) As Integer directions(1, 1) = 0: directions(1, 2) = -1 ' 上 directions(2, 1) = 0: directions(2, 2) = 1 ' 下 directions(3, 1) = -1: directions(3, 2) = 0 ' 左 directions(4, 1) = 1: directions(4, 2) = 0 ' 右 Dim currentCol As Integer, currentRow As Integer Dim nextCol As Integer, nextRow As Integer Dim i As Integer While head < tail currentCol = queue(head, 1) currentRow = queue(head, 2) head = head + 1 ' 石を削除 board(currentRow, currentCol) = EMPTY removedCount = removedCount + 1 ' 必要に応じてExcelシートのセルも更新 ' Cells(currentRow, currentCol).Value = EMPTY ' Cells(currentRow, currentCol).Interior.ColorIndex = xlNone ' 周囲4方向をチェック For i = 1 To 4 nextCol = currentCol + directions(i, 1) nextRow = currentRow + directions(i, 2) If nextRow >= 1 And nextRow <= BOARD_SIZE And nextCol >= 1 And nextCol <= BOARD_SIZE Then ' 同じ色の石で、まだ訪問していない場合 If board(nextRow, nextCol) = stoneColor And Not visited(nextRow, nextCol) Then visited(nextRow, nextCol) = True queue(tail, 1) = nextCol queue(tail, 2) = nextRow tail = tail + 1 End If End If Next i Wend RemoveStonesAt = removedCount End Function ' --------------- 合法手判定 --------------- ' 指定された座標(col, row)への着手(player)が合法か判定する Function IsValidMove(col As Integer, row As Integer, player As Integer) As Boolean ' 1. 盤

タイトルとURLをコピーしました