Paradoxe de la gallinacée et de l’œuf en VBA

Limite du modèle échiquéen statique et introduction au type dynamique Collection.

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

Relation entre Case et Pièce dans le 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.

Référence en avant sur le type SquareType dans le champ square de PieceType

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.


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

Posté le 19 juillet 2011 par Matt