VBA & l’échiquier 0x88

Représentation interne de l’échiquier linéaire &H88 : ordinal et offset.

Représentation interne de l’échiquier linéaire &H88 : ordinal et offset.


Quelle représentation interne de l’échiquier pour optimiser le générateur de coups ?

On discute des précédents modèles : 2D du GUI d’Excel, 1D dans une String, puis on introduit le nouveau modèle linéaire dit 0x88.

L’échiquier 2D

Echiquier physique (GUI) en 2D

Alors que la 1ere représentation qui peut venir à l’esprit est de prendre comme modèle l’échiquier physique 2D en colonne et rangée, on a pu constater dans l’échiquier graphique du GUI d’Excel l’augmentation de complexité en particulier l’imbrication des boucles For Next par rangée puis par colonne.

? (indFile >= 8) or (indRank >= 8) or (indFile < 0) or (indRank < 0)

De plus la détection des cases en dehors de l’échiquier en fin de balayage de diagonale, rangée, et colonne pour les pièces à longue portée (Dame,Tour, Fou) nécessite des conditions multiples pour tester les limites.


La représentation linéaire dans une chaîne

Echiquier linéaire de type String

Il n’y a plus qu’un indice de 1 à 64 dans la représentation mono-dimensionnelle de la chaîne de caractère BoardBuild(). Cependant l’extraction du contenu d’une case par Mid() n’est pas assez performante. Le modèle une case = un caractère, c-a-d l’initiale de la pièce, n’est pas expansible. Le test des cases en dehors d’une rangée, colonne, diagonale n’est pas simple.


L’échiquier &H88

L’idée est de représenter les cases de l’échiquier toujours dans un tableau à une seule dimension mais plus grande qu’un échiquier. Le n° de case appelé ordinal est structuré pour avoir des propriétés qui facilitent la détection quand on sort de l’échiquier et les tests d’alignements indispensables pour savoir par exemple si une pièce est clouée.

Echiquier 0x88 linéaire 16 * 16 représenté dans une matrice 2D

Il existe plusieurs version de l’échiquier 0x88. On a choisi un tableau à une dimension 16*16 = 256. L’échiquier est centré dans la matrice. Ceci évite d’avoir un ordinal négatif.

Le nom de l’échiquier 0x88 = &H88 en VBA vient de la constante hexadécimale en langage C où le préfixe hexa est "0x". C’est un masque que l’on appliquera prochainement sur l’ordinal avec l’opérateur And pour savoir immédiatement si on est en dehors de l’échiquier.

Pour optimiser le générateur de coups, on n’hésite pas à avoir de nombreuses déclarations ou initialisations à faire une seule fois au début de l’exécution du programme d’Echecs. C’est ainsi que l’on va déclarer chaque case, colonne et rangée par des constantes même s’il n’y a pas d’usage immédiat.


Gestion de la sauvegarde du .xls et de ses modules .bas

Archivez Chess.xls en Chess01.xls et exportez le module ModChess dans le fichier ModChess.bas :

  • Sélectionner ModChess dans la fenêtre projet du VBE.
  • Bouton droit de la souris > Menu contextuel "Exporter le fichier" (Export File).

En phase de développement il est recommandé avant de sauver ou d’exécuter une feuille de calcul .xls d’exporter manuellement ses modules .bas. En cas de problème de sauvegarde ou lors d’un bug par exemple de saturation mémoire, Excel peut planter (c’est rare mais cela peut arriver). On risque de perdre les dernières modifications dans un module.

De plus pour limiter l’expansion de la taille du .xls (à partir de 300 ko), il peut être utile d’exporter chaque module, de les effacer, puis de les réimporter.

La gestion de la sauvegarde en Chess01.xls, Chess02.xls, etc... par fenêtre glissante de l’indice de sauvegarde (effacer une des anciennes versions obsolètes quand on en ajoute une nouvelle version) permet d’avoir un historique et de revenir en arrière si nécessaire ou pour comparaison.


Les constantes de ModChess

Maintenant que Chess.xls a été archivé, vous pouvez sélectionner l’ensemble des lignes de code du module ModChess et les effacer car on repart sur une nouvelle base. Bien sûr on conserve la feuille de calcul avec son échiquier graphique.

Définissons les constantes du nouveau module ModChess dans la fenêtre d’édition (scroller verticalement pour voir les nouvelles !) :

Option Explicit

Public Const colBoardMain = 14
Public Const rowBoardMain = 9

Public Const countRank As Byte = 8
Public Const countFile As Byte = 8

Public Const ascRank0 As Byte = 48
Public Const ascRank1 As Byte = 49
Public Const ascFileA As Byte = 97
Public Const ascFileH As Byte = 104

Public Const colorFillBeige = 40

Public Const strGameStartPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"

Public Const a8 As Byte = &HB4, b8 As Byte = &HB5, c8 As Byte = &HB6, d8 As Byte = &HB7
Public Const a7 As Byte = &HA4, b7 As Byte = &HA5, c7 As Byte = &HA6, d7 As Byte = &HA7
Public Const a6 As Byte = &H94, b6 As Byte = &H95, c6 As Byte = &H96, d6 As Byte = &H97
Public Const a5 As Byte = &H84, b5 As Byte = &H85, c5 As Byte = &H86, d5 As Byte = &H87
Public Const a4 As Byte = &H74, b4 As Byte = &H75, c4 As Byte = &H76, d4 As Byte = &H77
Public Const a3 As Byte = &H64, b3 As Byte = &H65, c3 As Byte = &H66, d3 As Byte = &H67
Public Const a2 As Byte = &H54, b2 As Byte = &H55, c2 As Byte = &H56, d2 As Byte = &H57
Public Const a1 As Byte = &H44, b1 As Byte = &H45, c1 As Byte = &H46, d1 As Byte = &H47

Public Const e8 As Byte = &HB8, f8 As Byte = &HB9, g8 As Byte = &HBA, h8 As Byte = &HBB
Public Const e7 As Byte = &HA8, f7 As Byte = &HA9, g7 As Byte = &HAA, h7 As Byte = &HAB
Public Const e6 As Byte = &H98, f6 As Byte = &H99, g6 As Byte = &H9A, h6 As Byte = &H9B
Public Const e5 As Byte = &H88, f5 As Byte = &H89, g5 As Byte = &H8A, h5 As Byte = &H8B
Public Const e4 As Byte = &H78, f4 As Byte = &H79, g4 As Byte = &H7A, h4 As Byte = &H7B
Public Const e3 As Byte = &H68, f3 As Byte = &H69, g3 As Byte = &H6A, h3 As Byte = &H6B
Public Const e2 As Byte = &H58, f2 As Byte = &H59, g2 As Byte = &H5A, h2 As Byte = &H5B
Public Const e1 As Byte = &H48, f1 As Byte = &H49, g1 As Byte = &H4A, h1 As Byte = &H4B

Public Const fileA As Byte = &H4, fileB As Byte = &H5, fileC As Byte = &H6
Public Const fileD As Byte = &H7, fileE As Byte = &H8, fileF As Byte = &H9
Public Const fileG As Byte = &HA, fileH As Byte = &HB

Public Const rank1 As Byte = &H4, rank2 As Byte = &H5, rank3 As Byte = &H6
Public Const rank4 As Byte = &H7, rank5 As Byte = &H8, rank6 As Byte = &H9
Public Const rank7 As Byte = &HA, rank8 As Byte = &HB

Public Const pawnWhiteAttackRightOffset = 17
Public Const pawnWhiteAttackLeftOffset = 15
Public Const pawnBlackAttackRightOffset = -15
Public Const pawnBlackAttackLeftOffset = -17

Les fonctions de conversion de ModChess

Pour convertir l’ordinal ordSqr (ordinal of the square) en File et Rank, on retrouve les mêmes opérations que l’on a vues dans l’article précédent : Partir sur une nouvelle base en VBA.

Rank est le poids fort de l’ordinal ordSqr. On n’a pas pu éviter la division entière de l’ordinal car le shift de 4 bits vers la droite n’existe pas de base en VBA.

File est le poids faible de l’ordinal ordSqr. On applique le And logique plutôt que le modulo.

Function RankFromOrdinal(ByVal ordSqr As Byte) As Byte
    RankFromOrdinal = ordSqr \ 16
End Function

Function FileFromOrdinal(ByVal ordSqr As Byte) As Byte
    FileFromOrdinal = ordSqr And &HF
End Function

Function OrdinalFromFileRank(ByVal indFile As Byte, ByVal indRank As Byte) As Byte
    OrdinalFromFileRank = indRank * 16 Or indFile
End Function

Function OrdinalToString(ByVal ordSqr As Byte) As String
Dim indRank As Byte, indFile As Byte

    indRank = RankFromOrdinal(ordSqr)
    indFile = FileFromOrdinal(ordSqr)
    OrdinalToString = Chr(ascFileA + indFile - FileA) + CStr(indRank + 1 - Rank1)

End Function

Public Function FileFromName(ByVal strNameFile As String) As Byte
Dim indFile As Byte

    indFile = Asc(strNameFile) - ascFileA + fileA
    If indFile < fileA Or indFile > fileH Then
        FileFromName = 0
    Else
        FileFromName = indFile
    End If
End Function

Public Function RankFromName(ByVal strNameRank As String) As Byte
Dim indRank As Byte

    indRank = Asc(strNameRank) - ascRank1 + rank1
    If indRank < rank1 Or indRank > rank8 Then
        RankFromName = 0
    Else
        RankFromName = indRank
    End If
End Function

Function FileToString(ByVal indFile As Byte) As String
    FileToString = Chr(ascFileA + indFile - fileA)
End Function

Function RankToString(ByVal indRank As Byte) As String
    RankToString = Chr(ascRank1 + indRank - rank1)
End Function

Dans la fenêtre d’Exécution immédiate :

? "0x" + Hex(e5)

0x88

La case centrale e5 est également le nom de la représentation de l’échiquier 0x88 !


Conversion vers File

? FileFromOrdinal(e5)

8

Représentation interne de fileA à fileH.

? FileFromName ("e")

8

Cette conversion sera utile pour lire la liste de coups dans un fichier .pgn

? FileFromOrdinal(e5) - fileA

4

Représentation interne indFile de 0 à 7.


Conversion vers Rank

? RankFromOrdinal(e5)

8

Représentation interne de rank1 à rank8.

? RankFromName("5")

8

Cette conversion complémentaire à FileFromName() servira pour lire la liste de coups dans un fichier .pgn

? RankFromOrdinal(e5) - rank1

4

Représentation interne indRank de 0 à 7


Conversion de l’ordinal en coordonnées pour l’utilisateur final (GUI)

? FileToString(FileFromOrdinal(e5))

e

Coordonnée de colonne pour l’affichage sur le bord de l’échiquier ou dans un coup.

? RankToString(RankFromOrdinal(e5))

5

Coordonnée de rangée pour l’affichage sur le bord de l’échiquier ou dans un coup.

? OrdinalToString(e5)

e5

Coordonnée d’une case centrale pour l’affichage d’un coup.


Offset et différence d’ordinaux

Les huit offsets signés

Un offset est ce qu’on ajoute à un ordinal pour se déplacer d’une case dans une des huit directions comme le ferait un Roi.

Déplacement vertical d’un tour de 7 cases de a1 à a8

? OrdinalToString (a1 + 7 * 16)

a8

On ajoute à l’ordinal a1 la multiplication du nombre de cases par l’offset +16 de déplacement vertical.

Calculons la différence de deux ordinaux :

? (g8 - a1) mod pawnWhiteAttackRightOffset = 0

False

Les cases a1 et g8 ne sont pas alignées sur une même diagonale à 45°.

Une propriété intéressante de l’échiquier 0x88 est que la différence de deux ordinaux alignés est un multiple d’un offset.

? (h8 - a1) mod pawnWhiteAttackRightOffset = 0

True

Les cases a1 et h8 sont alignées sur une même diagonale à 45°.


Précédent : Partir sur une nouvelle base en VBA

Suivant : Concevoir un jeu d’Echecs en VBA

Posté le 17 juillet 2011 par Matt