【VBAリファレンス】Excel雑感将棋とプログラミングの深淵~隠された「型」の真実~

スポンサーリンク

はじめに:Excel雑感将棋とは何か?

「Excel雑感将棋」。この言葉を聞いて、皆さんは何を想像されるでしょうか?将棋盤をExcelシート上に再現し、駒の動きをVBAで実装する。そんなイメージをお持ちの方もいらっしゃるかもしれません。しかし、私がここで言う「Excel雑感将棋」は、単なるゲームの再現に留まりません。それは、Excelという限られた環境下で、プログラミングの根幹をなす「型」という概念を、いかに柔軟かつ創造的に活用できるかを探求する、一種の思考実験であり、実践的な開発手法なのです。

将棋は、駒の種類、盤上の位置、そしてそれらの関係性によって、無数の局面が生まれます。Excelもまた、セルの値、書式、数式、そしてVBAという強力なツールによって、無限の可能性を秘めたデータベースであり、アプリケーション開発プラットフォームです。この二つを結びつけることで、私たちはプログラミングにおける「型」の重要性と、その多様な表現方法を、より直感的に理解することができるのです。

本記事では、「Excel雑感将棋」というユニークな切り口から、プログラミングにおける「型」の概念を深く掘り下げていきます。なぜ「型」が重要なのか、Excelでどのように「型」を表現できるのか、そしてその知識が、より高度なVBAプログラミングへとどう繋がるのかを、具体的なサンプルコードと共に解説します。

「型」とは何か? プログラミングにおけるその重要性

プログラミングにおける「型」(Type)とは、データがどのような種類のものであるかを示すものです。例えば、数値、文字列、真偽値(True/False)、日付などが代表的な型です。変数に値を格納する際、その変数がどのような型のデータを保持できるのかを定義するのが「型宣言」です。

なぜ「型」が重要なのでしょうか?それは、主に以下の理由からです。

1. **データの整合性維持:** 型が定義されていることで、意図しない型のデータが変数に代入されることを防ぎ、プログラムの予期せぬエラーを回避できます。例えば、数値型の変数に文字列を代入しようとすると、コンパイルエラーや実行時エラーが発生します。
2. **メモリ効率の最適化:** 型によって、そのデータがメモリ上でどれだけの領域を占めるかが決まります。適切な型を選択することで、メモリの無駄遣いを減らし、プログラムのパフォーマンスを向上させることができます。
3. **コードの可読性と保守性の向上:** 型が明確であれば、その変数がどのようなデータを扱うのかが一目で理解できます。これにより、コードの可読性が高まり、他の開発者や将来の自分自身がコードを理解し、修正する際の負担を軽減できます。
4. **コンパイラ/インタプリタによる最適化:** 型情報があることで、コンパイラやインタプリタは、より効率的なコードを生成したり、実行時の処理を最適化したりすることが可能になります。

多くのプログラミング言語では、変数の型を明示的に宣言する「静的型付け言語」(Java, C++, C#など)と、実行時に型が決定される「動的型付け言語」(Python, JavaScript, Rubyなど)に大別されます。VBAは、基本的には動的型付け言語ですが、`Option Explicit` を宣言したり、変数に明示的に型を指定したりすることで、静的な型付け言語に近い堅牢なコードを書くことが可能です。

Excel雑感将棋で「型」を表現する

さて、本題の「Excel雑感将棋」で「型」をどのように表現できるかを見ていきましょう。将棋の駒は、それぞれ異なる性質を持っています。

* **歩兵(歩):** 前方に1マス進む、成ると金になる。
* **香車(香):** 前方に1マス以上進む。
* **桂馬(桂):** 前方に2マス進み、左右に1マス、飛び越えが可能。
* **銀将(銀):** 前方と、左右前方に1マス進む、成ると金になる。
* **金将(金):** 前方、左右、前後方に1マス進む。
* **角行(角):** 斜め方向に何マスでも進む、成ると龍になる。
* **飛車(飛):** 縦横方向に何マスでも進む、成ると龍になる。
* **王将(玉):** 全方向に1マス進む。

これらの駒の性質(動き方、成りの有無、元の駒に戻るかどうかなど)は、まさに「型」と捉えることができます。Excelのシート上で、これらの「型」を表現する方法はいくつか考えられます。

1. **セルの書式設定による表現:**
* 駒の種類によって、セルの背景色やフォントの色を変える。
* 駒の動きの制約を示すために、特定のセルにコメントやデータ検証を設定する。

2. **データ構造による表現:**
* 各駒を、その「型」を示す情報(駒の種類、色、現在の位置)を持つデータとして定義する。
* VBAのユーザー定義型(Type)やクラスモジュールを使用して、駒オブジェクトを作成し、それぞれの「型」に応じたプロパティとメソッドを持たせる。

3. **数式とVBAの連携:**
* Excelの数式を用いて、駒の移動可能範囲や、特定の条件下での挙動を計算させる。
* VBAで、これらの数式の結果を読み取ったり、数式を動的に生成したりして、駒の「型」に応じたロジックを実装する。

今回は、特にVBAのユーザー定義型(Type)とクラスモジュールを用いた「型」の表現に焦点を当てて解説します。

サンプルコード:VBAでの「型」の表現(ユーザー定義型)

まず、ユーザー定義型(Type)を使って、駒の基本的な「型」を定義してみましょう。

‘ 駒の情報を格納するユーザー定義型
Public Type Piece
Kind As String ‘ 駒の種類 (例: “歩”, “飛”, “王”)
Color As String ‘ 駒の色 (例: “先手”, “後手”)
Row As Integer ‘ 現在の行位置
Col As Integer ‘ 現在の列位置
IsPromoted As Boolean ‘ 成っているか否か
End Type

‘ 駒の動きを判定する関数(例:歩兵)
Function CanMovePawn(piece As Piece, targetRow As Integer, targetCol As Integer) As Boolean
Dim rowDiff As Integer
Dim colDiff As Integer

rowDiff = targetRow – piece.Row
colDiff = targetCol – piece.Col

‘ 駒の色によって進行方向を判定
If piece.Color = “先手” Then ‘ 先手は行が増える方向へ
If piece.IsPromoted Then
‘ 成銀と同じ動き(例)
If Abs(colDiff) = 1 And rowDiff = 1 Then
CanMovePawn = True
ElseIf colDiff = 0 And rowDiff = 1 Then
CanMovePawn = True
Else
CanMovePawn = False
End If
Else
‘ 通常の歩兵の動き
If colDiff = 0 And rowDiff = 1 Then
CanMovePawn = True
Else
CanMovePawn = False
End If
End If
Else ‘ 後手は行が減る方向へ
If piece.IsPromoted Then
‘ 成銀と同じ動き(例)
If Abs(colDiff) = 1 And rowDiff = -1 Then
CanMovePawn = True
ElseIf colDiff = 0 And rowDiff = -1 Then
CanMovePawn = True
Else
CanMovePawn = False
End If
Else
‘ 通常の歩兵の動き
If colDiff = 0 And rowDiff = -1 Then
CanMovePawn = True
Else
CanMovePawn = False
End If
End If
End If
End Function

‘ 駒の動きを判定する関数(例:飛車)
Function CanMoveRook(piece As Piece, targetRow As Integer, targetCol As Integer) As Boolean
Dim rowDiff As Integer
Dim colDiff As Integer

rowDiff = targetRow – piece.Row
colDiff = targetCol – piece.Col

‘ 縦横方向への移動のみ許可
If rowDiff <> 0 And colDiff <> 0 Then
CanMoveRook = False ‘ 斜め移動は不可
ElseIf rowDiff = 0 And colDiff = 0 Then
CanMoveRook = False ‘ その場に留まるのは不可
Else
‘ TODO: 駒の利き筋上の障害物チェックを実装する必要がある
‘ ここでは単純な方向判定のみ
CanMoveRook = True
End If
End Function

‘ 駒の動きを判定する関数(例:王将)
Function CanMoveKing(piece As Piece, targetRow As Integer, targetCol As Integer) As Boolean
Dim rowDiff As Integer
Dim colDiff As Integer

rowDiff = Abs(targetRow – piece.Row)
colDiff = Abs(targetCol – piece.Col)

‘ 全方向1マス移動のみ許可
If (rowDiff <= 1) And (colDiff <= 1) And Not (rowDiff = 0 And colDiff = 0) Then CanMoveKing = True Else CanMoveKing = False End If End Function Sub TestPieceMovement() Dim pawn As Piece pawn.Kind = "歩" pawn.Color = "先手" pawn.Row = 3 pawn.Col = 4 pawn.IsPromoted = False Dim rook As Piece rook.Kind = "飛" rook.Color = "後手" rook.Row = 7 rook.Col = 2 rook.IsPromoted = False Dim king As Piece king.Kind = "王" king.Color = "先手" king.Row = 5 king.Col = 5 king.IsPromoted = False ' 歩兵のテスト Debug.Print "歩兵(3,4) -> (4,4): ” & CanMovePawn(pawn, 4, 4) ‘ True
Debug.Print “歩兵(3,4) -> (3,4): ” & CanMovePawn(pawn, 3, 4) ‘ False
Debug.Print “歩兵(3,4) -> (2,4): ” & CanMovePawn(pawn, 2, 4) ‘ False (先手は上方向)
Debug.Print “歩兵(3,4) -> (3,5): ” & CanMovePawn(pawn, 3, 5) ‘ False (横移動不可)

‘ 飛車のテスト
Debug.Print “飛車(7,2) -> (7,5): ” & CanMoveRook(rook, 7, 5) ‘ True (横移動)
Debug.Print “飛車(7,2) -> (3,2): ” & CanMoveRook(rook, 3, 2) ‘ True (縦移動)
Debug.Print “飛車(7,2) -> (6,3): ” & CanMoveRook(rook, 6, 3) ‘ False (斜め移動不可)

‘ 王将のテスト
Debug.Print “王将(5,5) -> (5,6): ” & CanMoveKing(king, 5, 6) ‘ True
Debug.Print “王将(5,5) -> (4,4): ” & CanMoveKing(king, 4, 4) ‘ True
Debug.Print “王将(5,5) -> (6,5): ” & CanMoveKing(king, 6, 5) ‘ True
Debug.Print “王将(5,5) -> (7,7): ” & CanMoveKing(king, 5, 7) ‘ False (2マス移動不可)
Debug.Print “王将(5,5) -> (5,5): ” & CanMoveKing(king, 5, 5) ‘ False (その場に留まるのは不可)

End Sub

このコードでは、`Piece` というユーザー定義型に、駒の種類、色、位置、成っているかどうかの情報を格納しています。そして、それぞれの駒の「型」に応じた動きを判定する関数 (`CanMovePawn`, `CanMoveRook`, `CanMoveKing`) を作成しました。

重要なのは、これらの関数は `Piece` 型の引数を受け取ることで、汎用性を持たせている点です。これにより、駒の種類ごとにif文で分岐するのではなく、駒オブジェクト自体が持つ情報に基づいて、適切な動きの判定ロジックを呼び出すことができます。これは、オブジェクト指向プログラミングにおける「ポリモーフィズム(多態性)」の考え方に通じるものがあります。

サンプルコード:VBAでの「型」の表現(クラスモジュール)

よりオブジェクト指向的なアプローチを取るなら、クラスモジュールを使用するのが効果的です。クラスモジュールを使うと、データ(プロパティ)と、そのデータを操作する手続き(メソッド)を一つの「型」としてカプセル化できます。

まず、`clsPiece` という名前でクラスモジュールを作成し、以下のコードを記述します。

‘ clsPiece クラスモジュール

Private pKind As String
Private pColor As String
Private pRow As Integer
Private pCol As Integer
Private pIsPromoted As Boolean

‘ プロパティの設定 (Set) および取得 (Get)
Public Property Let Kind(value As String)
pKind = value
End Property
Public Property Get Kind() As String
Kind = pKind
End Property

Public Property Let Color(value As String)
pColor = value
End Property
Public Property Get Color() As String
Color = pColor
End Property

Public Property Let Row(value As Integer)
pRow = value
End Property
Public Property Get Row() As Integer
Row = pRow
End Property

Public Property Let Col(value As Integer)
pCol = value
End Property
Public Property Get Col() As Integer
Col = pCol
End Property

Public Property Let IsPromoted(value As Boolean)
pIsPromoted = value
End Property
Public Property Get IsPromoted() As Boolean
IsPromoted = pIsPromoted
End Property

‘ 駒の初期化
Public Sub Initialize(kind As String, color As String, startRow As Integer, startCol As Integer)
Me.Kind = kind
Me.Color = color
Me.Row = startRow
Me.Col = startCol
Me.IsPromoted = False ‘ 初期状態は成っていない
End Sub

‘ 駒の移動可否を判定するメソッド(抽象化)
‘ 各駒の具体的な動きは、このメソッドをオーバーライド(または実装)する派生クラスで定義する
Public Function CanMove(targetRow As Integer, targetCol As Integer) As Boolean
‘ 基本クラスでは、移動不可とするか、汎用的なチェックを行う
‘ ここでは、単純に移動不可とする
CanMove = False
MsgBox “この駒の動きは定義されていません。”, vbExclamation
End Function

‘ 駒の移動を実行するメソッド
Public Sub Move(targetRow As Integer, targetCol As Integer)
If Me.CanMove(targetRow, targetCol) Then
Me.Row = targetRow
Me.Col = targetCol
Debug.Print Me.Kind & “(” & Me.Color & “) が (” & Me.Row & “,” & Me.Col & “) へ移動しました。”
Else
Debug.Print Me.Kind & “(” & Me.Color & “) は (” & targetRow & “,” & targetCol & “) へ移動できません。”
End If
End Sub

‘ 駒の成りを判定するメソッド(必要に応じて)
Public Sub Promote()
If Me.CanPromote() Then ‘ 成れる条件をチェックするメソッドを別途定義
Me.IsPromoted = True
Debug.Print Me.Kind & ” が成りました!”
End If
End Sub

‘ 成れるかどうかを判定するメソッド(例:歩兵、香車、銀将、角行、飛車)
Public Function CanPromote() As Boolean
Select Case Me.Kind
Case “歩”, “香”, “銀”, “角”, “飛”
‘ 成れない条件(敵陣に入った、など)をここでチェック
‘ 簡単のため、ここでは常に成れるとする
CanPromote = True
Case Else
CanPromote = False
End Select
End Function

‘ 駒の情報を表示するメソッド
Public Function GetInfo() As String
Dim info As String
info = Me.Kind & “(” & Me.Color & “) [” & Me.Row & “,” & Me.Col & “]”
If Me.IsPromoted Then info = info & ” (成)”
GetInfo = info
End Function

次に、この `clsPiece` クラスを基底として、各駒の具体的な動きを実装する派生クラスを作成します。例えば、歩兵クラス `clsPawn` です。

‘ clsPawn クラスモジュール
‘ clsPiece を継承する

‘ clsPiece の型宣言を標準モジュール等で行う必要がある場合がある
‘ (VBAのバージョンや設定による)
‘ 例: Public Type Piece … End Type (これはクラスモジュールでは直接使えない)
‘ クラスモジュールでは、継承というより、基底クラスのメソッドを呼び出す形になる

‘ clsPiece のメソッドをオーバーライドする形で実装
Public Function CanMove(targetRow As Integer, targetCol As Integer) As Boolean
Dim rowDiff As Integer
Dim colDiff As Integer

rowDiff = targetRow – Me.Row
colDiff = targetCol – Me.Col

If Me.IsPromoted Then
‘ 成った時の動き(成銀と同様の動きを例とする)
If Abs(colDiff) = 1 And rowDiff = 1 Then
CanMove = True
ElseIf colDiff = 0 And rowDiff = 1 Then
CanMove = True
Else
CanMove = False
End If
Else
‘ 通常の歩兵の動き
If Me.Color = “先手” Then ‘ 先手は行が増える方向へ
If colDiff = 0 And rowDiff = 1 Then
CanMove = True
Else
CanMove = False
End If
Else ‘ 後手は行が減る方向へ
If colDiff = 0 And rowDiff = -1 Then
CanMove = True
Else
CanMove = False
End If
End If
End If
‘ TODO: 駒がいないかのチェック、敵陣での動きの制限などは別途実装
End Function

‘ 成れるかどうかを判定するメソッド(歩兵は成れる)
Public Overrides Function CanPromote() As Boolean
CanPromote = True ‘ 成れる条件は clsPiece で処理済みとする
End Function

‘ 成った時の駒の種類名を変更する(任意)
Public Overrides Sub Promote()
If Me.CanPromote() Then
Me.IsPromoted = True
‘ 必要であれば、駒の種類名を変更する
‘ Me.Kind = “と” ‘ 例:歩が成ると「と」になる
Debug.Print Me.Kind & ” が成りました!”
End If
End Sub

‘clsRook クラスモジュール
‘clsPiece を継承する

Public Function CanMove(targetRow As Integer, targetCol As Integer) As Boolean
Dim rowDiff As Integer
Dim colDiff As Integer

rowDiff = targetRow – Me.Row
colDiff = targetCol – Me.Col

If rowDiff <> 0 And colDiff <> 0 Then
CanMove = False ‘ 斜め移動は不可
ElseIf rowDiff = 0 And colDiff = 0 Then
CanMove = False ‘ その場に留まるのは不可
Else
‘ TODO: 駒の利き筋上の障害物チェックを実装する必要がある
CanMove = True
End If
End Function

‘ 成れるかどうかを判定するメソッド(飛車は成れる)
Public Overrides Function CanPromote() As Boolean
CanPromote = True
End Function

‘ 成った時の駒の種類名を変更する(任意)
Public Overrides Sub Promote()
If Me.CanPromote() Then
Me.IsPromoted = True
‘ 必要であれば、駒の種類名を変更する
‘ Me.Kind = “龍” ‘ 例:飛車が成ると「龍」になる
Debug.Print Me.Kind & ” が成りました!”
End If
End Sub

そして、これらのクラスを使ったテストコードです。

Sub TestClassPiece()
Dim pawn As clsPiece
Dim rook As clsPiece
Dim king As clsPiece

‘ 各駒オブジェクトを生成
Set pawn = New clsPawn ‘ clsPawn は clsPiece を継承していると仮定
pawn.Initialize “歩”, “先手”, 3, 4

Set rook = New clsRook ‘ clsRook は clsPiece を継承していると仮定
rook.Initialize “飛”, “後手”, 7, 2

Set king = New clsKing ‘ clsKing は clsPiece を継承していると仮定
king.Initialize “王”, “先手”, 5, 5

Debug.Print pawn.GetInfo()
Debug.Print rook.GetInfo()
Debug.

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