【VBAリファレンス】Excel将棋の深淵へ:VBAで実現する棋譜の永続化と自動再生システム構築 (№14)

スポンサーリンク

概要

Excel VBAで構築された将棋アプリケーションは、その手軽さから多くのVBA開発者にとって魅力的な題材です。しかし、一度の対局で終わってしまっては、その奥深さを十分に味わうことはできません。本記事では、Excel将棋の可能性を飛躍的に広げる「棋譜ファイルの出力と読込、そして自動再生」の仕組みに焦点を当てます。対局の記録をファイルとして永続化し、いつでも好きな時にその対局を再現できる機能は、ユーザー体験の向上はもちろん、将棋AIの開発や戦術分析の基盤としても極めて重要です。

この記事では、VBAを用いたテキストファイルI/Oの基本から、棋譜データを効率的に保存・読み込むためのデータ構造、そして読み込んだ棋譜を盤面に自動で反映させ再生するロジックまでを、実践的な視点から詳細に解説します。これにより、単なるゲームとしてのExcel将棋から一歩進んだ、より高度で実用的なアプリケーションへと昇華させるための技術を習得できるでしょう。

詳細解説

Excel将棋における棋譜の永続化と自動再生は、VBAのファイル操作、データ構造、そしてUI制御の技術が統合された高度な機能です。それぞれの要素を深く掘り下げていきましょう。

1. 棋譜データの設計と記録

棋譜は、将棋の一手一手を示すデータ群です。これをどのように表現し、格納するかが最初の課題となります。最もシンプルな形式は、各指し手をテキスト文字列として表現し、それを順序立てて記録する方法です。

* **指し手データ形式:** 一手は「どの駒が、どこからどこへ移動したか、そして成ったか、取った駒は何か」といった情報で構成されます。例えば、「手番,移動元セルアドレス,移動先セルアドレス,成りの有無,取った駒の種類」といったCSV(Comma Separated Values)のような形式が考えられます。
* 例: `先手,B2,B3,N,なし` (2筋2段目の駒が2筋3段目に移動、成らず、何も取っていない)
* 例: `後手,H7,H6,Y,歩兵` (8筋7段目の駒が8筋6段目に移動、成り、歩兵を取った)
* **棋譜コレクション:** 対局中に発生する各指し手は、VBAの`Collection`オブジェクトや`Array`に順次追加していくのが効率的です。`Collection`は柔軟性が高く、要素の追加が容易なため、実用性が高いでしょう。

2. 棋譜ファイルの出力(保存)

対局が終了した際、記録された棋譜コレクションをファイルに書き出す処理です。VBAでのファイルI/Oには、`Open`ステートメントによる伝統的な方法と、`FileSystemObject`(FSO)オブジェクトを利用する方法の二つがあります。FSOはよりオブジェクト指向的で柔軟性が高く、推奨されます。

* **ファイルパスの指定:** ユーザーに保存場所を選択させる`Application.GetSaveAsFilename`関数を利用するか、特定のディレクトリに固定で保存する方法があります。
* **ファイルオープンと書き込み:**
* **FSOの場合:** `CreateTextFile`メソッドで新規ファイルを作成し、`TextStream`オブジェクトの`WriteLine`メソッドで一行ずつ棋譜データを書き込みます。
* **Openステートメントの場合:** `Open filePath For Output As #FreeFile` でファイルを書き込みモードで開きます。`Print #FileNumber, dataString` で一行ずつ書き込み、最後に`Close #FileNumber`で閉じます。
* **エラーハンドリング:** ファイルパスが無効な場合、書き込み権限がない場合など、様々なエラーが考えられます。`On Error GoTo` を用いた堅牢なエラーハンドリングが必須です。

3. 棋譜ファイルの読込

保存された棋譜ファイルを読み込み、棋譜コレクションとしてアプリケーションメモリ上に復元する処理です。

* **ファイルパスの指定:** ユーザーにファイルを選択させる`Application.GetOpenFilename`関数を利用します。
* **ファイルオープンと読み込み:**
* **FSOの場合:** `OpenTextFile`メソッドでファイルを開き、`TextStream`オブジェクトの`ReadLine`メソッドで一行ずつ読み込みます。`AtEndOfStream`プロパティでファイルの終端を判定します。
* **Openステートメントの場合:** `Open filePath For Input As #FreeFile` でファイルを読み込みモードで開きます。`Line Input #FileNumber, dataString` で一行ずつ読み込み、`EOF(FileNumber)`でファイルの終端を判定します。
* **データ解析とコレクション格納:** 読み込んだ各行の文字列(指し手データ)を`Split`関数などで解析し、適切なデータ形式(例: `Variant`配列やカスタムクラスのインスタンス)に変換後、棋譜コレクションに追加していきます。

4. 棋譜の自動再生

読み込んだ棋譜コレクションを基に、盤面を自動で動かしていく機能です。これは、各指し手を順番に実行するループ処理と、盤面描画の更新、そして再生速度の制御が核となります。

* **指し手の実行ロジック:** 棋譜コレクションから一つずつ指し手データを取り出し、その情報(移動元、移動先、成り、取った駒)に基づいて、実際の将棋盤の駒を動かすVBAプロシージャを呼び出します。このプロシージャは、手動で駒を動かす際と同じロジックを再利用できるはずです。
* **盤面描画の更新:** 各指し手を実行した後、Excelシート上の盤面(図形やセル)を最新の状態に更新する必要があります。`Application.ScreenUpdating = True`を適切に設定し、必要に応じて`DoEvents`を呼び出すことで、VBAの処理中にUIが固まるのを防ぎ、アニメーションのような滑らかな動きを実現できます。
* **再生速度の制御:** 各指し手間の「間」を設けることで、人間が目で追える速度での再生を可能にします。`Application.Wait Now + TimeValue(“00:00:01”)` のように、指定した時間だけ処理を一時停止させることで、再生速度を調整できます。より高度な制御には、`Sleep` API関数を利用することも考えられます。
* **再生コントロール:** 一時停止、再開、早送り、巻き戻しといった機能も実装することで、ユーザーはより自由に棋譜を分析できるようになります。これらの機能は、グローバル変数やPublic変数で再生状態を管理し、ボタンクリックイベントなどで制御します。

サンプルコード

以下に、棋譜の保存、読み込み、自動再生の核となるVBAプロシージャの簡略化されたサンプルコードを示します。実際のExcel将棋アプリケーションに組み込む際は、盤面操作や駒の描画ロジックを適宜追加してください。

Option Explicit

‘ 棋譜データ構造の例 (手番,移動元,移動先,成り,取った駒)
‘ 例: “先手,A1,B1,N,なし”

Private Const KIFU_FILE_PATH As String = “C:\Temp\kifu_log.txt” ‘ 棋譜ファイルのデフォルトパス

‘ — 棋譜コレクションの定義 (モジュールレベルで管理する場合) —
‘ Public kifuCollection As Collection

‘ ——————————————————————————–
‘ 棋譜をファイルに保存するプロシージャ
‘ Arguments:
‘ kifuData: 保存する棋譜データのCollectionオブジェクト
‘ filePath: 保存先のファイルパス
‘ ——————————————————————————–
Sub SaveKifuToFile(kifuData As Collection, Optional filePath As String = KIFU_FILE_PATH)
Dim fso As Object
Dim ts As Object
Dim kifuLine As Variant

‘ ファイルパスが指定されていない場合はデフォルトを使用
If filePath = “” Then filePath = KIFU_FILE_PATH

On Error GoTo ErrorHandler

Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ ファイルが存在しない場合は作成、存在する場合は上書き
Set ts = fso.CreateTextFile(filePath, True)

For Each kifuLine In kifuData
ts.WriteLine kifuLine ‘ 各指し手を1行ずつ書き込む
Next kifuLine

ts.Close
Set ts = Nothing
Set fso = Nothing

MsgBox “棋譜をファイルに保存しました: ” & filePath, vbInformation
Exit Sub

ErrorHandler:
MsgBox “棋譜の保存中にエラーが発生しました: ” & Err.Description, vbCritical
If Not ts Is Nothing Then ts.Close
Set ts = Nothing
Set fso = Nothing
End Sub

‘ ——————————————————————————–
‘ ファイルから棋譜を読み込むプロシージャ
‘ Returns:
‘ 読み込んだ棋譜データを含むCollectionオブジェクト
‘ ——————————————————————————–
Function LoadKifuFromFile(Optional filePath As String = KIFU_FILE_PATH) As Collection
Dim fso As Object
Dim ts As Object
Dim kifuLine As String
Dim loadedKifu As New Collection

‘ ファイルパスが指定されていない場合はデフォルトを使用
If filePath = “” Then filePath = KIFU_FILE_PATH

On Error GoTo ErrorHandler

Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FileExists(filePath) Then
MsgBox “指定された棋譜ファイルが見つかりません: ” & filePath, vbExclamation
Set LoadKifuFromFile = New Collection ‘ 空のコレクションを返す
Exit Function
End If

Set ts = fso.OpenTextFile(filePath, 1) ‘ 1 = ForReading

Do While Not ts.AtEndOfStream
kifuLine = ts.ReadLine
If Trim(kifuLine) <> “” Then
loadedKifu.Add kifuLine ‘ 1行ずつコレクションに追加
End If
Loop

ts.Close
Set ts = Nothing
Set fso = Nothing

Set LoadKifuFromFile = loadedKifu
MsgBox “棋譜をファイルから読み込みました: ” & filePath, vbInformation
Exit Function

ErrorHandler:
MsgBox “棋譜の読み込み中にエラーが発生しました: ” & Err.Description, vbCritical
If Not ts Is Nothing Then ts.Close
Set ts = Nothing
Set fso = Nothing
Set LoadKifuFromFile = New Collection ‘ エラー時は空のコレクションを返す
End Function

‘ ——————————————————————————–
‘ 棋譜を自動再生するプロシージャ
‘ Arguments:
‘ kifuData: 再生する棋譜データのCollectionオブジェクト
‘ playbackSpeedSec: 各手間の再生間隔(秒)
‘ ——————————————————————————–
Sub PlayKifu(kifuData As Collection, Optional playbackSpeedSec As Double = 1.0)
Dim kifuLine As Variant
Dim parts As Variant
Dim turn As String, fromCell As String, toCell As String, naru As String, tookKom As String
Dim moveCount As Long

If kifuData Is Nothing Or kifuData.Count = 0 Then
MsgBox “再生する棋譜データがありません。”, vbExclamation
Exit Sub
End If

Application.ScreenUpdating = False ‘ 画面更新を停止して高速化
Application.EnableEvents = False ‘ イベントを一時停止

moveCount = 0
For Each kifuLine In kifuData
moveCount = moveCount + 1

‘ 棋譜ラインを解析 (例: “先手,A1,B1,N,なし” -> Split関数で分割)
parts = Split(CStr(kifuLine), “,”)
If UBound(parts) >= 4 Then ‘ 最低限の要素があるか確認
turn = parts(0) ‘ 手番
fromCell = parts(1) ‘ 移動元セルアドレス
toCell = parts(2) ‘ 移動先セルアドレス
naru = parts(3) ‘ 成りの有無 (“Y” or “N”)
If UBound(parts) >= 4 Then tookKom = parts(4) Else tookKom = “なし” ‘ 取った駒

‘ — ここに実際の盤面操作ロジックを実装 —
‘ 例: Call MoveKom(turn, fromCell, toCell, naru, tookKom)
‘ MsgBox “手番: ” & turn & “, ” & fromCell & ” -> ” & toCell & ” (成: ” & naru & “, 取: ” & tookKom & “)” & vbCrLf & _
‘ “現在 ” & moveCount & “手目を再生中…”, vbInformation, “棋譜再生”

‘ 仮の盤面更新表示 (実際の盤面操作に置き換える)
ThisWorkbook.Sheets(“Sheet1”).Range(“A1”).Value = “手番: ” & turn & “, ” & fromCell & ” -> ” & toCell
ThisWorkbook.Sheets(“Sheet1”).Range(“A2”).Value = “取った駒: ” & tookKom
ThisWorkbook.Sheets(“Sheet1”).Range(“A3”).Value = “現在 ” & moveCount & “手目を再生中…”

Application.ScreenUpdating = True ‘ 一時的に画面を更新
DoEvents ‘ UIイベント処理を許可し、画面更新を反映
Application.ScreenUpdating = False ‘ 再び画面更新を停止

Application.Wait

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