Limite du modèle échiquéen statique et introduction au type dynamique Collection.
Exportez ModBoard.bas : voir Gestion de la sauvegarde du .xls et de ses modules .bas
Exceptionnellement à la fin de cette session, vous pourrez sortir d’Excel sans sauvegarder les modifications suivantes dans le module ModBoard.
Considération d’occupation mémoire de tableau statique
Public boardMain As BoardType
Avec la déclaration globale boardMain, on a réservé 256 cases même si on n’en utilise que 64.
Du fait du nombre de Mo de RAM disponible sur les ordinateurs actuels cela importe peu.
Cependant avec la déclaration statique de tableaux, il faut prendre garde à la réservation mémoire si on multiplie les échiquiers.
Sur une feuille de calcul on veut l’échiquier principal boardMain mais aussi l’échiquier des variations boardVariant pour le joueur courant et également l’échiquier des variations pour le joueur adverse boardVarOpp : soit trois échiquiers par feuille de calcul.
Comme on cherche à analyser un duel de moteurs d’Echecs, compter jusqu’à dix feuilles de calcul pour un match, soit au total 30 échiquiers. On risque d’atteindre les limites de la mémoire en dimensionnant des tableaux trop gros par exemple pour le nombre maximum de coups dans une partie d’Echecs.
Critique du modèle statique
La signification de la flèche verte est un lien entre le type de la variable piece et la définition du type PieceType.
Pour la flèche rouge, la relation est plus complexe. On souhaiterait exprimer la tautologie suivante. Sur une case, on peut placer une pièce. On se doute que cette pièce est donc sur une case. Mais comment l’exprimer en VBA ?
Comme dans SquareType, on a le champ piece As PieceType, on aimerait avoir symétriquement dans PieceType, le champ square as SquareType.
Référence en avant
Dans la fenêtre édition du module ModBoard dans la déclaration de PieceType,
mettre temporairement en commentaire en ajoutant une simple quote "’" en début de ligne le dernier champ : "ordinal As Byte"
Type PieceType
typePiece As Byte
isWhite As Boolean
player As PlayerType
opponent As PlayerType
ordinal As Byte
End Type
La ligne "’ordinal As Byte" devient verte.
Ajoutez dessous, mais avant End Type, le nouveau champ : "square as SquareType"
Type PieceType
typePiece As Byte
isWhite As Boolean
player As PlayerType
opponent As PlayerType
'ordinal As Byte
square as SquareType
End Type
Jusque là VBA ne se plaint pas encore.
Dans la fenêtre d’Exécution immédiate, lancez l’initialisation de boardMain par NewGame :
NewGame
Compile error : Forward reference to user-defined type.
Dans l’ordre séquentiel de définition des types définis dans le module ModBoard, PieceType est défini avant SquareType mais utilise maintenant dans son dernier champ SquareType. Le problème est que SquareType n’est pas encore défini à ce moment là. Il le sera plus tard quelques lignes plus loin mais il est utilisé trop tôt. C’est une référence dite en avant par rapport à sa définition qui apparaît après celle de PieceType.
Si on inverse les définitions en plaçant SquareType avant PieceType, cela déplace le problème sans le résoudre car maintenant c’est PieceType qui ne sera pas encore défini.
Il n’y a pas d’autre solution que d’effacer le champ que l’on a ajouté et enlever la quote de commentaire pour restaurer le champ initial "ordinal As Byte".
A partir de l’ordinal de la case, on arrivera bien à retrouver la case où est la pièce mais le modèle statique est bancal. Il interdit les références en avant. Pour pouvoir réserver la taille totale du tableau ou de la structure statique, VBA a besoin de connaître la définition et donc la taille statique de chaque champ.
Le paradoxe de la gallinacée et de l’œuf
La référence en avant peut être illustrée par le paradoxe de l’oeuf et de la poule : au commencement y avait-il une poule ou un oeuf traduire par SquareType avant PieceType ou le contraire.
- On a choisi une approche centrée sur les cases (Echiquier 0x88).
- Le modèle centrée sur les pièces (bitboard) a été abordé succintement dans la discussion sur la Représentation numérique en base 2. Cherchez "1 bit = 1 case".
Introduction au type Collection
Comme on a restauré le code initial avec "ordinal As Byte", NewGame doit marcher à nouveau.
NewGame
Rien ne s’affiche.
? Mid(strAbbreviationPiecesEn, boardMain.arrSquare(a2).piece.typePiece, 1)
P
Il y a bien un pion Blanc en a2. Quand est-il de la collection de pièces du joueur Blanc ?
? boardMain.playerWhite.colPieces
Run-time error ’91’ : Object variable or With block variable not set.
Oops, testons si la collection de coups est Nothing. Voir Ces petits riens pour le mot-clé Nothing.
? boardMain.playerWhite.colPieces is Nothing
True
Il nous faut créer la liste des pièces avec l’opérateur New. Notez que l’on ne sait pas à l’avance combien d’objets seront ajoutés dans la collection. On parle également d’allouer de la mémoire pour la collection ou d’instanciation d’objet. Comme il s’agit d’objets, l’affectation d’un objet commence par Set.
Set boardMain.playerWhite.colPieces = New Collection
VBA n’affiche rien.
? boardMain.playerWhite.colPieces is Nothing
False
La collection existe. On peut ajouter (Add) un premier élément.
boardMain.playerWhite.colPieces.Add "Pa2"
Interrogeons le compteur d’éléments dans la liste.
? boardMain.playerWhite.colPieces.Count
1
Accédons au premier élément de la collection :
? boardMain.playerWhite.colPieces(1)
Pa2
Comme dans une String, le 1er élément d’une Collection commence à l’indice 1.
Ce n’est pas le cas des tableaux qui commencent par défaut à 0.
On peut ajouter des objets hétéroclites de types différents dans une même collection.
Cela n’est pas possible avec un tableau où chaque élément doit avoir le même type.
boardMain.playerWhite.colPieces.Add Cells(1,1)
On a ajouté la première cellule de la feuille de calcul dans la collection !
Plutôt que de copier-coller à nouveau l’instruction suivante, vous pouvez remonter le curseur dans la fenêtre d’Exécution immédiate avec les flèches et allez en fin de ligne que vous pouvez valider par ENTER.
? boardMain.playerWhite.colPieces.Count
2
Il y a bien maintenant deux éléments dans la collection.
On peut même utiliser une méthode de l’objet Cells qui est dans le 2eme élément de la Collection :
? boardMain.playerWhite.colPieces(2).Address
$A$1
Cependant le but est principalement d’ajouter une pièce telle que le pion blanc en a2 dans la liste :
boardMain.playerWhite.colPieces.Add boardMain.arrSquare(a2).piece
Compile error : ByRef argument type mismatch
Le type PieceType défini par l’utilisateur ne plaît pas à l’ajout d’élément d’une Collection.
Durée de vie d’un objet
La durée de vie d’un tableau statique comme BoardMain est infinie tant qu’on ne ferme pas Excel.
L’existence d’un objet dynamique comme la collection boardMain.playerWhite.colPieces peut s’arrêter dès qu’on l’affecte avec Nothing. Cela libère la mémoire allouée pour l’objet.
Set boardMain.playerWhite.colPieces = Nothing
En cas de bug ou de modification de code, tous les objets dynamiques créés sont perdus. Il faut alors les recréer dynamiquement après la correction du bug ou l’édition du code.
Collection vs. tableau
Les tableaux sont plus performants mais moins flexibles qu’une collection. Ils imposent de savoir à l’avance le nombre maximum d’éléments du tableau.
On aurait pu à la place d’une collection déclarer un tableau de pièces dont la taille est connue. Il y a 16 pièces par joueur. Cependant l’avantage de la collection est qu’au fur et à mesure de la partie d’Echecs, son nombre (Count) d’items diminue à chaque capture. Ceci explique pourquoi en finale les programmes d’Echecs peuvent analyser plus en profondeur puisqu’il y a moins de pièces sur l’échiquier.
La solution du tableau de 16 pièces complexifie le cas de la promotion d’un pion a7-a8:q en une pièce de valeur supérieure comme la Dame.
- Avec la solution de la collection, on déplace le pion de la collection colPieces dans la collection des captures colCaptures même si le pion promu n’a pas été capturé. Tout ce que l’on sait c’est qu’il disparaît de l’échiquier. Puis on crée la nouvelle Dame dans la collection colPieces.
- Dans une solution de tableau à taille fixe, la pièce de type Pion se transforme en pièce de type Dame mais la Dame promue n’aurait pas toute la valeur d’une Dame puisqu’on doit soustraire à sa valeur la valeur du pion qui a servi à sa promotion.
Pour le nombre de coups à générer dans une position donnée ou le nombre de coups d’une partie d’Echecs dans la collection moveHistory, la collection prend exactement la place nécessaire alors que dans un tableau on aurait dû prévoir un nombre maximum de coups arbitraire et surtout réserver toute la place même si on ne l’utilise pas.
En conclusion, le type défini par l’utilisateur ne permet pas de décrire complètement la dynamique de création de pièces et de coups dans une partie d’Echecs mais c’est une étape nécessaire pour aborder les champs des notions principales d’un jeu d’Echecs.
Précédent : Concevoir un jeu d’Echecs en VBA
Suivant : Architecture échiquéenne VBA orientée objet