VBAサンプル集
Excel将棋:将棋盤クラスの作成&単体テスト(№7)

ExcelマクロVBAの実用サンプル、エクセルVBA集と解説
最終更新日:2020-08-31

Excel将棋:将棋盤クラスの作成&単体テスト(№7)


VBA マクロ Excel将棋

Excelで将棋を作ってみましょう。
人vs人で動かしてゲームとして成立するところまでが当面の目標です。


今回は、いよいよ将棋盤クラスを作成します。
駒クラスを2次元配列(1 To 9, 1 To 9)に入れて将棋盤全体を管理します。
作成するクラス全体の設計は、№2. Excel将棋:クラスの設計、こちらを参照してください。
・作成するクラスの役割と作成順 ・作成するクラスのメンバー一覧 ・駒の移動の定義 ・Excel将棋の目次

※クラス名、プロシージャー名、変数名に日本語を使用しています。

将棋盤クラス

将棋盤クラスの設計

種別 名称 説明
プロパティ 現在盤面 駒オブジェクトが入っている2次元配列から表示名の2次元配列を作成する
プロパティ 盤面履歴 現在盤面をCollection
プロパティ 手数 現在手数を戻す
プロパティ 棋譜 1手ずつCollectionに追加
棋譜は、Ki2形式とします。
▲5ニ銀右上成
・先手▲後手△
・到達地点の筋
・到達地点の段
・駒の種類
・駒の相対位置(複数ある場合)
・駒の動作(複数ある場合)
・成・不成・打
プロパティ 棋譜履歴 最終手をCollection
プロパティ 先手 先手True、後手False
メソッド 着手 引数(元位置, 先位置, 駒名, Optional 手番)
元位置:-1,-1は初期配置
元位置:0,0は駒台から
手番省略時は、プロパティ手番に従う
以下を更新する。
最終手、2次元配列、盤面履歴、棋譜履歴
メソッド 終局判定 これはかなり難しい・・・
持駒を含めた全ての駒を使って受けがないかの判定が必要

2次元配列(1 To 9, 1 To 9)の各要素に駒オブジェクト(駒クラスのインスタンス)を入れて管理します。

将棋盤クラスのVBA

クラス名称:cls将棋盤

Option Explicit

Private pAry駒(1 To 9, 1 To 9) As cls駒
Private p先手 As Boolean

Private pCol盤面 As Collection
Private pCol棋譜 As Collection

Private Sub Class_Initialize()
  Me.先手 = True
  Set pCol盤面 = New Collection
  Set pCol棋譜 = New Collection
End Sub

Private Sub Class_Terminate()
  Set pCol盤面 = Nothing
  Set pCol棋譜 = Nothing
End Sub

'**********************************************************************
' 公開プロパティ
'**********************************************************************

Public Property Get 現在盤面() As String()
  Dim ary盤 As Variant
  If pCol盤面.Count = 0 Then
    ary盤 = pAry駒
  Else
    ary盤 = pCol盤面(pCol盤面.Count)
  End If
  
  Dim out盤面() As String
  ReDim out盤面(LBound(ary盤, 1) To UBound(ary盤, 1), _
         LBound(ary盤, 2) To UBound(ary盤, 2))
  
  Dim i As Long, j As Long
  For i = LBound(ary盤, 1) To UBound(ary盤, 1)
    For j = LBound(ary盤, 2) To UBound(ary盤, 2)
      If ary盤(i, j) Is Nothing Then
        out盤面(i, j) = "  "
      Else
        out盤面(i, j) = ary盤(i, j).表示名称 & _
                IIf(ary盤(i, j).先手, "↑", "↓")
      End If
    Next
  Next
  
  現在盤面 = out盤面
End Property

Public Property Get 盤面履歴() As Collection
  Set 盤面履歴 = pCol盤面
End Property

Public Property Get 棋譜() As String
  棋譜 = pCol棋譜(pCol棋譜.Count)
End Property

Public Property Get 棋譜履歴()
  Set 棋譜履歴 = pCol棋譜
End Property

Public Property Get 手数() As String
  手数 = pCol棋譜.Count
End Property

Public Property Let 先手(ByVal Value As Boolean)
  p先手 = Value
End Property
Public Property Get 先手() As Boolean
  先手 = p先手
End Property

'**********************************************************************
' 公開メソッド
'**********************************************************************

'駒が移動できる位置をg位置(行、列)のCollectionで返す
Public Function 駒移動可能位置(ByVal arg位置 As g位置) As Collection
  If pAry駒(arg位置.行, arg位置.列) Is Nothing Then Exit Function
  Set 駒移動可能位置 = pAry駒(arg位置.行, arg位置.列).駒移動可能位置(pAry駒)
End Function

Public Sub 着手(ByVal arg駒名 As String, _
        ByVal arg元位置 As g位置, _
        ByVal arg先位置 As g位置, _
        ByVal arg先手 As Boolean)
  Dim i元行 As Integer, i元列 As Integer
  Dim i先行 As Integer, i先列 As Integer
  If Not arg元位置 Is Nothing Then '初期配置
    i元行 = arg元位置.行: i元列 = arg元位置.列
  End If
  i先行 = arg先位置.行: i先列 = arg先位置.列
  
  '元位置:-1,-1は初期配置
  '位置:0,0は駒台の出し入れ
  Dim obj駒 As cls駒
  Select Case True
    Case arg元位置 Is Nothing '初期配置
      Set obj駒 = New cls駒
      Set pAry駒(i先行, i先列) = obj駒.駒作成(arg駒名, arg先手, arg先位置)
      '棋譜は不要
      Exit Sub
    Case i先行 = 0 '駒台へ
      Set pAry駒(i元行, i元列) = Nothing
      '棋譜は不要
      Exit Sub
    Case i元行 = 0 '駒台から
      Set obj駒 = New cls駒
      Set pAry駒(i先行, i先列) = obj駒.駒作成(arg駒名, arg先手, arg先位置)
    Case Else '駒移動
      Set pAry駒(i先行, i先列) = pAry駒(i元行, i元列)
      Set pAry駒(i先行, i先列).駒位置 = arg先位置
      pAry駒(i先行, i先列).成り = 成り判定(arg元位置, arg先位置)
  End Select
  
  '棋譜履歴
  pCol棋譜.Add create棋譜(pAry駒(i元行, i元列), pAry駒(i先行, i先列))
  '棋譜作成で元位置が必要なので、棋譜作成後のここで消す
  Set pAry駒(i元行, i元列) = Nothing
  
  '盤面履歴
  pCol盤面.Add pAry駒
  
  Call 手番交代
End Sub

'**********************************************************************
' 非公開メソッド
'**********************************************************************

Private Sub 手番交代()
  Me.先手 = Not Me.先手
End Sub

'棋譜の表記方法:https://www.shogi.or.jp/faq/kihuhyouki.html
'棋譜ほMi2形式で作成(▲5ニ銀右上成)
Private Function create棋譜(ByVal arg駒元 As cls駒, _
              ByVal arg駒先 As cls駒) As String
  Dim ary(1 To 7) As String
  ary(1) = IIf(Me.先手, "▲", "△")
  ary(2) = StrConv(10 - arg駒先.駒位置.列, vbWide)
  ary(3) = WorksheetFunction.Text(arg駒先.駒位置.行, "[DBNum1]0")
  ary(4) = arg駒先.表示名称
  ary(5) = get駒の相対位置(arg駒先)
  ary(6) = get駒の動作(arg駒先)
  ary(7) = IIf(arg駒元.成り = arg駒先.成り, "", "成")
  create棋譜 = Join(ary, "")
End Function

Public Function 終局判定() As Boolean
  '※※※これは難しいので後回し※※※
End Function

Private Function 成り判定(ByRef arg元位置 As g位置, _
             ByVal arg先位置 As g位置) As Boolean
  '※※※これは難しいので後回し※※※
End Function

Private Function get駒の相対位置(ByVal arg駒先 As cls駒) As String
  '※※※これは難しいので後回し※※※
End Function

Private Function get駒の動作(ByVal arg駒先 As cls駒) As String
  '※※※これは難しいので後回し※※※
End Function

将棋盤クラスVBAの解説

特段に難しいテクニックは使っていません。
クラスを配列にいれて処理する部分を理解していただければ十分です。

配列の要素がクラスになっています。
Private pAry駒(1 To 9, 1 To 9) As cls駒
この使い方を理解してください。

例えば、
Set 駒移動可能位置 = pAry駒(arg位置.行, arg位置.列).駒移動可能位置(pAry駒)
これは、
pAry駒(arg位置.行, arg位置.列)
これがcls駒の1つのオブジェクトになります。
そして、駒オブジェクトの駒移動可能位置メソッドを呼び出しています。

駒移動可能位置メソッドについて

将棋盤クラス作成して思ったのは、この駒移動可能位置メソッドは駒クラスと将棋盤クラスのどちらが良かったのかという事です。
駒の配置(他の駒の位置が必要)を持っているのは将棋盤クラスですが、駒の動きを管理しているのは駒クラスです。
つまり、将棋盤クラスと駒クラスの共同作業になります。
VBAとしては、どちらに記述しても可能ですが、将棋盤クラスに記述したことで、
pAry駒(arg位置.行, arg位置.列).駒移動可能位置(pAry駒)
このように、駒配置の配列から入って、最後にその配列を引数にいれるという、ちょっと変な感じになってしまっています。
しかし将棋盤クラスでやる場合は、駒クラスから駒の移動に関する情報をもらわないといけないので、どっちにしても面倒な記述になりそうです。
正解が決まっているわけではないので、とりあえずこれで進めていきます。

数値を漢数字に変換

WorksheetFunction.Text(arg駒先.駒位置.行, "[DBNum1]0")
これで算用数字を漢数字に変換しています。
これについては、ツイッターで問題を出した後に回答ページを作成しました。
VBAで数値を漢数字に変換する方法
・お題のツイート ・お題に対して頂いた回答 ・用意した回答 ・後日談ツイート:再帰でやってみる

※※※これは難しいので後回し※※※

以下については、ソフト全体がある程度出来上がった後に作成します。

終局判定
持駒を含めた全ての駒を使って受けがないかの判定
成り判定
駒が成れる条件
・敵陣に入った場合
・敵陣内で動いた場合
・敵陣から外に出た場合
成るか成らないかの確認が必要
get駒の相対位置
右:指す側から見て右側の駒を動かした場合
左:指す側から見て左側の駒を動かした場合
直:指す側から見て上に駒を動かした場合
打:持駒から打った場合
get駒の動作
上:1段以上、上に動く
寄:1マス以上、横に動く
引:1段以上、下に動く

一つ一つがかなり難しい処理になりそうなので、ゲーム全体の作成を先にすることにしました。

将棋盤クラスのテストVBAコード

Sub test3()
  Const 先手 As Boolean = True
  Const 後手 As Boolean = False
  Dim obj将棋盤 As New cls将棋盤
  '大橋流
  With obj将棋盤
    .着手 "玉", Nothing, 棋譜位置(5, 9), 先手
    .着手 "玉", Nothing, 棋譜位置(5, 1), 後手
    .着手 "金", Nothing, 棋譜位置(6, 9), 先手
    .着手 "金", Nothing, 棋譜位置(4, 1), 後手
    .着手 "金", Nothing, 棋譜位置(4, 9), 先手
    .着手 "金", Nothing, 棋譜位置(6, 1), 後手
    .着手 "銀", Nothing, 棋譜位置(7, 9), 先手
    .着手 "銀", Nothing, 棋譜位置(3, 1), 後手
    .着手 "銀", Nothing, 棋譜位置(3, 9), 先手
    .着手 "銀", Nothing, 棋譜位置(7, 1), 後手
    .着手 "桂", Nothing, 棋譜位置(8, 9), 先手
    .着手 "桂", Nothing, 棋譜位置(2, 1), 後手
    .着手 "桂", Nothing, 棋譜位置(2, 9), 先手
    .着手 "桂", Nothing, 棋譜位置(8, 1), 後手
    .着手 "香", Nothing, 棋譜位置(9, 9), 先手
    .着手 "香", Nothing, 棋譜位置(1, 1), 後手
    .着手 "香", Nothing, 棋譜位置(1, 9), 先手
    .着手 "香", Nothing, 棋譜位置(9, 1), 後手
    .着手 "角", Nothing, 棋譜位置(8, 8), 先手
    .着手 "角", Nothing, 棋譜位置(2, 2), 後手
    .着手 "飛", Nothing, 棋譜位置(2, 8), 先手
    .着手 "飛", Nothing, 棋譜位置(8, 2), 後手
    .着手 "歩", Nothing, 棋譜位置(5, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(5, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(6, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(4, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(4, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(6, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(7, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(3, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(3, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(7, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(8, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(2, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(2, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(8, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(9, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(1, 3), 後手
    .着手 "歩", Nothing, 棋譜位置(1, 7), 先手
    .着手 "歩", Nothing, 棋譜位置(9, 3), 後手
    
    Call PrintArray(.現在盤面)
    
    .着手 "歩", 棋譜位置(2, 7), 棋譜位置(2, 6), 先手
    .着手 "歩", 棋譜位置(3, 3), 棋譜位置(3, 4), 後手
    .着手 "歩", 棋譜位置(2, 6), 棋譜位置(2, 5), 先手
    .着手 "角", 棋譜位置(2, 2), 棋譜位置(3, 3), 後手
    .着手 "歩", 棋譜位置(7, 7), 棋譜位置(7, 6), 先手
    .着手 "歩", 棋譜位置(8, 3), 棋譜位置(8, 4), 後手
    .着手 "歩", 棋譜位置(6, 9), 棋譜位置(7, 8), 後手
    
    Debug.Print ""
    Call PrintCollection(.棋譜履歴)
    Debug.Print ""
    
    Call PrintArray(.現在盤面)
  End With
End Sub

Function 棋譜位置(ByVal arg列 As Integer, ByVal arg行 As Integer) As g位置
  Set 棋譜位置 = g位置(arg行, 10 - arg列)
End Function

Sub PrintArray(ByRef ary, Optional separator As String = "")
  Dim i As Long, j As Long, str As String
  For i = LBound(ary, 1) To UBound(ary, 1)
    str = ""
    For j = LBound(ary, 2) To UBound(ary, 2)
      If j > LBound(ary, 2) Then str = str & separator
      str = str & ary(i, j)
    Next
    Debug.Print str
  Next
End Sub

Sub PrintCollection(ByVal argCol As Collection)
  Dim v
  For Each v In argCol
    Debug.Print v
  Next
End Sub

駒を並べる順番は関係ありませんが、せっかくなので大橋流で並べてみました。
将棋の駒の並べ方に規則があるわけではありません。
どのような順番で並べても構いません。
趣味でちょっとやるくらいなら、皆さん適当に並べていると思います。
並べ方の流派というか、プロの先生方の駒の並べる順序はある程度決まっています。
それが、大橋流と伊藤流です。
プロの対局中継を見ていると、ほとんどの先生は大橋流で並べているようです。

並べ終わった時点で駒配置を出力し、
そこから7手指して、また、駒配置を出力しています。

棋譜位置

将棋の棋譜は、筋・段です。
筋は、右から1,2,3,…
段は、上から1,2,3,…
しかし配列は行・列の順で、列は左から数えます。
この変換を行い、位置クラスで返しています。
▲2六歩なら、
(2, 6)→ (4, 2)

将棋盤クラスのテストVBAの結果

イミディエイト ウィンドウの出力です。
VBEの使い方:イミディエイト ウィンドウ
・イミディエイト ウィンドウの表示 ・イミディエイト ウィンドウの基本的な使い方 ・イミディエイト ウィンドウの高度な使い方 ・イミディエイト ウィンドウの最後に
先手↑▲、後手↓△
香↓桂↓銀↓金↓玉↓金↓銀↓桂↓香↓
  飛↓          角↓  
歩↓歩↓歩↓歩↓歩↓歩↓歩↓歩↓歩↓
                  
                  
                  
歩↑歩↑歩↑歩↑歩↑歩↑歩↑歩↑歩↑
  角↑          飛↑  
香↑桂↑銀↑金↑玉↑金↑銀↑桂↑香↑
▲2六歩
△3四歩
▲2五歩
△3三角
▲7六歩
△8四歩
▲7八金
香↓桂↓銀↓金↓玉↓金↓銀↓桂↓香↓
  飛↓              
歩↓  歩↓歩↓歩↓歩↓角↓歩↓歩↓
  歩↓        歩↓    
              歩↑  
    歩↑            
歩↑歩↑  歩↑歩↑歩↑歩↑  歩↑
  角↑金↑        飛↑  
香↑桂↑銀↑  玉↑金↑銀↑桂↑香↑

Excel将棋の目次

№1. Excel将棋:マクロVBAの学習用
・Excel将棋の要件定義 ・Excel将棋のシート作成 ・Excel将棋の目次
№2. Excel将棋:クラスの設計
・作成するクラスの役割と作成順 ・作成するクラスのメンバー一覧 ・駒の移動の定義 ・Excel将棋の目次
№3. Excel将棋:駒クラスの作成
・位置クラス ・移動クラス ・駒クラス ・駒クラスVBAの解説 ・使用しているVBAの参考ページ ・Excel将棋の目次
№4. Excel将棋:駒クラスの単体テスト
・駒クラスのテスト内容 ・駒クラスのテストVBAコード ・駒クラスのテストVBAの結果 ・Excel将棋の目次
№5. Excel将棋:駒台クラスの作成&単体テスト
・駒クラス ・駒台クラスVBAの解説 ・駒台クラスのテストVBAコード ・駒クラスのテストVBAの結果 ・Excel将棋の目次
№6. Excel将棋:位置クラスをデフォルトインスタンスに変更
・デフォルトインスタンスに変更する理由と方法 ・位置クラスのインポート用ソース ・cls位置とg位置の使い方の違い ・位置クラス変更に伴う駒クラスの変更 ・Excel将棋の目次
№7. Excel将棋:将棋盤クラスの作成&単体テスト
・将棋盤クラス ・将棋盤クラスVBAの解説 ・将棋盤クラスのテストVBAコード ・将棋盤クラスのテストVBAの結果 ・Excel将棋の目次
№8. Excel将棋:将棋進行クラスの作成
・将棋進行クラス ・将棋進行クラスVBAの解説 ・将棋進行クラスの起動方法 ・将棋盤クラスのテストVBAの結果 ・Excel将棋の目次
№9. Excel将棋:駒を動かす
・駒クラスにプロパティ追加 ・将棋進行クラスの変更点 ・将棋進行クラスのVBA ・Excel将棋の実行動作 ・Excel将棋の目次
№10. Excel将棋:相手の駒を取る、持ち駒を打つ
・主な変更追加箇所 ・全体の構成 ・Excel将棋の動作 ・Excel将棋の全VBAコード ・Excel将棋の目次
№11. Excel将棋:駒を成る
・主な変更追加箇所 ・Excel将棋の動作 ・変更したクラスのVBA ・Excel将棋の目次
№12. Excel将棋:棋譜をユーザーフォームに表示する
・棋譜について ・主な変更内容 ・ユーザーフォームの作成 ・Excel将棋の動作 ・変更したクラスのVBA ・Excel将棋の目次
№13. Excel将棋:棋譜選択でその時点の盤面に戻す
・Excel将棋の動作 ・全体構成図 ・全プロシージャー・プロパティの一覧 ・クリックで着手した時の主なプロシージャーの流れ ・Excel将棋のダウンロード ・Excel将棋の目次
№14. Excel将棋:棋譜ファイルの出力と読込自動再生
・Excel将棋の動作 ・VBAの修正箇所について ・Excel将棋のダウンロード ・棋譜KIFファイルのサンプル ・Excel将棋の目次
№15. Excel将棋:反則(禁じ手)判定
・反則(禁じ手) ・Excel将棋の動作 ・各クラスの共通で持つプロパティを整理 ・VBAの修正箇所について ・Excel将棋のダウンロード ・Excel将棋の目次
№16. Excel将棋:終局(詰み)判定と打ち歩詰め
・反則(禁じ手) ・Excel将棋の動作 ・VBAの追加・修正箇所について ・Excel将棋のダウンロード ・Excel将棋の目次
№17. Excel将棋:千日手と連続王手の千日手
・反則(禁じ手) ・Excel将棋の動作 ・VBAの修正箇所について ・Excel将棋のダウンロード ・Excel将棋の目次
№18 Excel将棋:ひとまず完成、これまでとこれから
・Excel将棋のこれから ・Excel将棋の目次 ・Excel将棋のダウンロード ・当初のクラス設計 ・作成するクラスのメンバー一覧 ・全体構成図 ・全プロシージャー・プロパティの一覧 ・全VBAコード
№19 Excel将棋:棋譜ファイルから対局一覧作成
・対局一覧のシート ・Excel将棋の動作 ・全体構成図 ・Excel将棋のダウンロード ・新規追加したVBAコード ・Excel将棋の目次



新着記事NEW ・・・新着記事一覧を見る

抜けている数値を探せ|エクセル雑感(2022-07-01)
.Net FrameworkのSystem.Collectionsを利用|VBA技術解説(2022-06-29)
迷路ネコが影分身の術を体得したら…|エクセル雑感(2022-06-27)
迷路にネコが挑戦したら、どうなるかな…|エクセル雑感(2022-06-26)
サロゲートペアに対応した自作関数(Len,Left,Mid,Right)|エクセル雑感(2022-06-24)
「マクロの登録」で登録できないプロシージャーは?|エクセル雑感(2022-06-23)
オブジェクトのByRef、ByVal、Variant|エクセル雑感(2022-06-22)
コメントから特定形式の年月を取り出す|エクセル雑感(2022-06-19)
4,9を使わない連番作成|エクセル雑感(2022-06-17)
連番を折り返して出力|エクセル雑感(2022-06-16)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.繰り返し処理(For Next)|VBA入門
5.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
6.Excelショートカットキー一覧|Excelリファレンス
7.マクロって何?VBAって何?|VBA入門
8.並べ替え(Sort)|VBA入門
9.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
10.エクセルVBAでのシート指定方法|VBA技術解説




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ