Première architecture en terme d’échiquier, joueur, pièce et case : Type, With, "."
Comment aborder la conception d’un jeu d’Echecs ?
On peut concevoir une architecture autour de quatre notions : l’échiquier, la case, la pièce et le joueur.
Architecture du jeu d’Echecs : approche statique
Partons de l’échiquier Board qui sera la racine de notre architecture. Détaillons chacun de ses champs :
Un Board c’est d’abord un tableau de cases : arrSquare(). On utilise le préfixe "arr" (array) pour le distinguer d’une Function qui a également des parenthèses en fin de nom mais pour la liste de ses paramètres.
Il y a deux joueurs playerWhite et playerBlack. Le joueur qui a le trait est playerToPlay.
Sur la feuille de partie papier, on trouve le n° de coup turnNo et la liste des coups movesHistory.
En VBA on parle de collection plutôt que de liste.
La collection movesRedoList servira pour sauvegarder les coups supprimés de movesHistory quand on navigue en sens inverse vers le début de la partie.
isGameOver pose la question la partie est-elle terminée ?
Le résultat final de la partie est dans resultGame.
La position de départ de la partie est au format FEN dans le cas où le PGN commencerait dans une position donnée.
Revenons sur le tableau de cases : arrSquare(256) :
On indique la dimension maximum (16 * 16 voir L’échiquier &H88) entre parenthèses. Le 1er élément du tableau a pour indice 0.
Une case a un n° appelé ordinal dont on déduit les coordonnées fileSqr et rankSqr.
La couleur de la case ne sert pas que pour l’échiquier GUI. Elle est utile pour identifier la couleur des Fous.
Sur une case, on place une piece.
Une piece a un type : Roi, Dame, Tour, Fou, Cavalier, Pion.
Une piece a une couleur isWhite = True pour Blanche, False pour Noire.
Une piece appartient au joueur player qui a pour adversaire opponent.
Une piece est sur une case que l’on identifie par un ordinal.
Finalement un joueur Player a une couleur isWhite, la collection de ses pièces colPieces et des pièces adverses capturées colCaptures.
La caractéristique d’un joueur est le déplacement de ses pions en terme d’offset à ajouter à l’ordinal de la case de départ du pion.
Finalement le dernier champ d’un Player est : Le Roi de ce joueur a-t’il roqué hasCastled ?
Type de pièces, leurs valeurs, type de coups et résultat du jeu
Ajoutons de nouvelles constantes dans le module ModChess montrant que le projet se concrétise progressivement :
Public Const typeKing As Byte = 1
Public Const typeQueen As Byte = typeKing + 1
Public Const typeRook As Byte = typeQueen + 1
Public Const typeBishop As Byte = typeRook + 1
Public Const typeKnight As Byte = typeBishop + 1
Public Const typePawn As Byte = typeKnight + 1
Public Const valuePawn = 100
Public Const valueKnight = 300
Public Const valueBishop = 325
Public Const valueRook = 500
Public Const valueQueen = 975
Public Const valueKing = 1000
Public Const moveTypeNull As Byte = 0
Public Const moveStandard As Byte = moveTypeNull + 1
Public Const movePromotionQueen As Byte = moveStandard + 1
Public Const movePromotionRook As Byte = movePromotionQueen + 1
Public Const movePromotionBishop As Byte = movePromotionRook + 1
Public Const movePromotionKnight As Byte = movePromotionBishop + 1
Public Const moveEnPassant As Byte = movePromotionKnight + 1
Public Const moveCastleQueenSide As Byte = moveEnPassant + 1
Public Const moveCastleKingSide As Byte = moveCastleQueenSide + 1
Public Const statusNormal As Byte = 1
Public Const statusInCheck As Byte = statusNormal + 1
Public Const statusDrawStaleMate As Byte = statusInCheck + 1
Public Const statusInCheckMate As Byte = statusDrawStaleMate + 1
Public Const strAbbreviationPiecesEn = "KQRBNP"
Public Const strAbbreviationPiecesFr = "RDTFCP"
Définir une constante par incrément de la précédente constante nommée permet d’insérer une nouvelle constante où on veut sans devoir renuméroter les anciennes.
Implémentation VBA du modèle statique du jeu d’Echecs
Le codage de l’architecture statique du jeu d’Echecs peut se faire avec des types utilisateurs (user defined type), c-a-d définis par le développeur grâce au mot-clé Type ... End Type.
Dans la fenêtre projet du VBE, cliquez avec le bouton droit de la souris sur "Modules" ou "ModChess", contextuel menu "Insérer" > "Module".
Dans la fenêtre "Propriétés", renommer "Module1" en "ModBoard".
Dans la fenêtre d’Edition de ModBoard :
Option Explicit
Const board0x88Height = 16
Const board0x88Width = 16
Type PlayerType
isWhite As Boolean
colPieces As Collection
colCaptures As Collection
PawnForwardOffset As Integer
PawnForward2Offset As Integer
PawnAttackRightOffset As Integer
PawnAttackLeftOffset As Integer
hasCastled As Boolean
End Type
Type PieceType
typePiece As Byte
isWhite As Boolean
player As PlayerType
opponent As PlayerType
ordinal As Byte
End Type
Type SquareType
ordinal As Byte
fileSqr As Byte
rankSqr As Byte
isWhite As Boolean
piece As PieceType
End Type
Type BoardType
arrSquare(board0x88Height * board0x88Width) As SquareType
playerWhite As PlayerType
playerBlack As PlayerType
playerToPlay As PlayerType
turnNo As Integer
movesHistory As Collection
movesRedoList As Collection
isGameOver As Boolean
resultGame As Integer
strFENStartPosition As String
End Type
Public boardMain As BoardType
Après la déclaration des nouveaux types, on déclare la variable boardMain de type BoardType.
C’est la première fois que l’on déclare une variable à l’extérieur d’une Function ou Sub.
Qui dit à l’extérieur dit que la variable n’est pas locale à une procédure. La variable est donc globale ce qui nous permettra de l’utiliser dans la fenêtre d’Exécution immédiate même après l’exécution de la procédure d’initialisation NewGame() :
Initialisation de la structure globale boardMain
Sub NewGame()
Dim ordSqr As Byte ' ordinal
' Le joueur des Blancs
boardMain.playerWhite.isWhite = True
boardMain.playerWhite.PawnForwardOffset = 16 ' e2-e3
boardMain.playerWhite.PawnForward2Offset = 32 ' e2-e4
boardMain.playerWhite.PawnAttackRightOffset = pawnWhiteAttackRightOffset
boardMain.playerWhite.PawnAttackLeftOffset = pawnWhiteAttackLeftOffset
' Initialisation des 64 cases
For ordSqr = a1 To h8
If ((ordSqr - a1) And &H88) = 0 Then
With boardMain.arrSquare(ordSqr)
.ordinal = ordSqr
.fileSqr = FileFromOrdinal(ordSqr)
.rankSqr = RankFromOrdinal(ordSqr)
.isWhite = (.fileSqr And 1) Xor (.rankSqr And 1)
End With
End If
Next
' Création des huit pions des Blancs
For ordSqr = a2 To h2
With boardMain.arrSquare(ordSqr).piece
.ordinal = ordSqr
.isWhite = True
.player = boardMain.playerWhite
.opponent = boardMain.playerBlack
.typePiece = typePawn
End With
Next
End Sub
On constate la répétition du préfixe "boardMain.playerWhite" pour chacun des champs du joueur des Blancs tel que sa couleur, l’avance d’un pion d’une case puis de deux cases, etc. Il est plus rapide en terme d’exécution de factoriser ce préfixe grâce à l’instruction With .. End With.
Dans la fenêtre d’Edition, remplacez l’initialisation du joueur Blanc par :
With boardMain.playerWhite
.isWhite = True
.PawnForwardOffset = 16 ' e2-e3
.PawnForward2Offset = 32 ' e2-e4
.PawnAttackRightOffset = pawnWhiteAttackRightOffset ' e4xf5
.PawnAttackLeftOffset = pawnWhiteAttackLeftOffset ' e4xd5
End With
A l’intérieur du bloc With ... End With, chaque champ commence maintenant directement par le "." séparateur.
Aller à la fin du commentaire ’ e4xd5 et tapez ENTER pour créer une ligne vide.
Tapez deux TAB, pour s’aligner sur les précédentes instructions.
Expérimentez l’autocomplétion en ajoutant manuellement avant End With : le Roi des Blancs n’a pas encore roqué :
.hasCastled = False
End With
Saisir le "." plutôt que de faire un copier-coller.
Puis la première lettre "h" : hasCastled est sélectionné dans l’info-bulle.
Puis "=" le symbole d’affectation. HasCastled s’affiche tout seul suivi du "=".
L’autocomplétion propose False / True en info-bulle.
Choisir False avec flèche vers le bas. Valider par ENTER ou SPACE.
Notez que VBA va automatiquement insérer un espace blanc séparateur autour du "=".
Test hors rangée avec l’échiquier &H88
Dans l’initialisation des 64 cases dans le tableau boardMain.arrSquare(ordSqr), on retrouve le And avec la constante &H88 de l’échiquier 0x88.
Dans la fenêtre d’Exécution immédiate :
? ((h1 - a1) And &H88) = 0
True
h1 est bien sur l’échiquier.
? ((h1 + 1 - a1) And &H88) = 0
False
Dans le balayage horizontal d’une case vers la droite (offset +1), la Tour ne doit pas sortir de la première rangée au-delà de h1. Le test ((ordSqr - a1) And &H88) = 0 est peut-être moins parlant que (indFile >= 8) or (indRank >= 8) or (indFile < 0) or (indRank < 0) mais il est plus efficace.
Le premier pion blanc en a2
Lors de la création des huit pions dans la boucle For ordSqr = a2 To h2, dans le bloc par pièce With boardMain.arrSquare(ordSqr).piece, l’appartenance à un joueur .player = boardMain.playerWhite pose question. Car cette affectation va copier tous les champs du playerWhite. Une fois que l’on aura initialisé toute la structure globale boardMain, que peut-on en faire ?
Dans la fenêtre d’Exécution immédiate :
NewGame
Aucun résultat ne s’affiche car c’est une Sub.
? boardMain.arrSquare(a2).piece.typePiece
6
C’est le type d’un pion. Retrouvons son initiale :
? Mid(strAbbreviationPiecesEn, boardMain.arrSquare(a2).piece.typePiece, 1)
P
L’intérêt pédagogique est plus de saisir manuellement l’expression plutôt que de la copier-coller pour apprendre à utiliser l’autocomplétion quand on tape le séparateur "." avec ses propres types utilisateurs. Voir ci-dessus la saisie de hasCastled et, au tout début, Initiation à Excel VBA : la fenêtre Exécution.
En conclusion, on a décrit un modèle statique du jeu d’Echecs. L’utilisation de Types utilisateurs a permis de définir une première approche des champs de l’échiquier, joueur, pièce et case.
Pour rendre dynamique le modèle, il faudrait pouvoir lui intégrer des actions comme générer la collections des coups dans une position donnée ou jouer un coup et donc gérer la collection des pièces du joueur et des pièces capturées.
Précédent : VBA & l’échiquier 0x88
Suivant : Paradoxe de la gallinacée et de l’œuf en VBA