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

Excelで将棋を作ってみましょう。
人vs人で動かしてゲームとして成立するところまでが当面の目標です。
駒クラスを2次元配列(1 To 9, 1 To 9)に入れて将棋盤全体を管理します。
作成するクラス全体の設計は、№2. 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
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駒)
このように、駒配置の配列から入って、最後にその配列を引数にいれるという、ちょっと変な感じになってしまっています。
しかし将棋盤クラスでやる場合は、駒クラスから駒の移動に関する情報をもらわないといけないので、どっちにしても面倒な記述になりそうです。
正解が決まっているわけではないので、とりあえずこれで進めていきます。
数値を漢数字に変換
これで算用数字を漢数字に変換しています。
これについては、ツイッターで問題を出した後に回答ページを作成しました。
※※※これは難しいので後回し※※※
・敵陣に入った場合
・敵陣内で動いた場合
・敵陣から外に出た場合
成るか成らないかの確認が必要
左:指す側から見て左側の駒を動かした場合
直:指す側から見て上に駒を動かした場合
打:持駒から打った場合
寄: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の結果
香↓桂↓銀↓金↓玉↓金↓銀↓桂↓香↓
飛↓ 角↓
歩↓歩↓歩↓歩↓歩↓歩↓歩↓歩↓歩↓
歩↑歩↑歩↑歩↑歩↑歩↑歩↑歩↑歩↑
角↑ 飛↑
香↑桂↑銀↑金↑玉↑金↑銀↑桂↑香↑
▲2六歩
△3四歩
▲2五歩
△3三角
▲7六歩
△8四歩
▲7八金
香↓桂↓銀↓金↓玉↓金↓銀↓桂↓香↓
飛↓
歩↓ 歩↓歩↓歩↓歩↓角↓歩↓歩↓
歩↓ 歩↓
歩↑
歩↑
歩↑歩↑ 歩↑歩↑歩↑歩↑ 歩↑
角↑金↑ 飛↑
香↑桂↑銀↑ 玉↑金↑銀↑桂↑香↑
Excel将棋の目次
新着記事NEW ・・・新着記事一覧を見る
TRIMRANGE関数(セル範囲をトリム:端の空白セルを除外)|エクセル入門(2024-08-30)
正規表現関数(REGEXTEST,REGEXREPLACE,REGEXEXTRACT)|エクセル入門(2024-07-02)
エクセルが起動しない、Excelが立ち上がらない|エクセル雑感(2024-04-11)
ブール型(Boolean)のis変数・フラグについて|VBA技術解説(2024-04-05)
テキストの内容によって図形を削除する|VBA技術解説(2024-04-02)
ExcelマクロVBA入門目次|エクセルの神髄(2024-03-20)
VBA10大躓きポイント(初心者が躓きやすいポイント)|VBA技術解説(2024-03-05)
テンキーのスクリーンキーボード作成|ユーザーフォーム入門(2024-02-26)
無効な前方参照か、コンパイルされていない種類への参照です。|エクセル雑感(2024-02-17)
初級脱出10問パック|VBA練習問題(2024-01-24)
アクセスランキング ・・・ ランキング一覧を見る
1.最終行の取得(End,Rows.Count)|VBA入門
2.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.RangeとCellsの使い方|VBA入門
5.繰り返し処理(For Next)|VBA入門
6.ブックを閉じる・保存(Close,Save,SaveAs)|VBA入門
7.メッセージボックス(MsgBox関数)|VBA入門
8.セルのクリア(Clear,ClearContents)|VBA入門
9.条件分岐(Select Case)|VBA入門
10.ブック・シートの選択(Select,Activate)|VBA入門
- ホーム
- マクロVBA応用編
- マクロVBAサンプル集
- Excel将棋:将棋盤クラスの作成&単体テスト(№7)
このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。