[Echecs et Mat] Pause des calculs en virgule flottantes

Bonsoir.
Vous savez probablement jouer au échecs.
Je m’attarde à programmer un jeu d’échecs avec la possibilité de jouer avec zéro joueur depuis une dixaine d’années.
Je suis occupé par d’autre sujet donc je m’y colle de temps à autre.
Actuellement je reprends un projet relatif au film Wargames et j’ai repris le problème de l’heuristique.
J’avais auparvant trouvé des algorithme relativement surprenant mais qui ne répondaient pas à mon besoin d’obtenir un mat calculé à zéro joueur.

Ce soir, j’arrive à un Echec et Mat des noirs en 30 secondes au premier niveau.

Comme je ne joue pas beaucoup au échecs je ne sais pas s’il s’agit de coup du berget auquel ressemble me semble - t - il l’issue de cette première parti satisfaisante pour moi dont je vous affiche une copie d’écran.

Dark_Mat

Oui c a y ressemble bien. Penses à changer le commentaire DARK par BLACK :slight_smile:

J’ai eu le problème du conflict avec le nom de la couleur qui n’en est pas une mais qui est quand même Black.

Du coup j’ai pensé à l’appeler Blacks, ça reste à faire.

Bonjour, ou re - bonjour.

Je suis partant pour faire un petit tutoriel de programmation de jeu d’échecs.
On pourrais s’y mettre à plusieurs, avec des contributions basées sur une première.
J’ai longtemps cherché comme je dis plus haut.
La plus grande difficulté à été d’obtenir une partie gagnante en faisant appel à l’algorithme minmax dont il existe plusieurs variantes me semble t - il dons l’algorithme alpha-beta qui opère un élagage visant une optimisation des calculs à entreprendre.
Avec minmax on parle de recherche en largeur d’abord. Alpha-beta une une variante de même type qui devrait être un peu plus rapide.

J’ai implémenté une classe abstraite d’un tablier, ce qui m’a obligé à utiliser un adressage dynamique.
Je n’utilise quasiment pas de mémoire.

Mes calculs sont assais gourmand en revanche.

Les valeurs des pieces mise en jeu sont primordiale dans le calcul d’une solution.
J’ai éssayé une dixaine de variantes mais je suis resté très proche des valeurs que l’on m’a induiqués il y a dix ans aujourd’hui afin de parvenir à mon objetif.

En effet, comme les calculs peuvent prendre du temps, j’ai eu la mauvaise idée de vouloir faire vite malgrès tout et pour ce, je testais mon jeu niveau zéro qui bien que faisant appel à l’heuristique, n’implique pas toute les fonctions nécéssaire à l’exploitation des algorithmes de calcul tel que minmax.

Jouer au premier niveau diffère de ce que j’ai posté précédemment en fait lequel était un niveau zéro, donc aucune recherche en profondeur.

Je suis finalement parvenu à un mat ce matin niveau 1, c’est à dire que j’appelle minmax avec une profondeur de 2.

Les calculs pour parvenir à un mat à ce niveau sont différant qu’au niveau zéro.

J’ai écris des heuristiques qui ont des résulats surprenants.

Certain sont invalides, d’autres tournent en boucle, avec des organisations plus que « systématiques », d’autres encore refusent de jouer, certain jouent puis rangent les pièces au fur et à mesure, bref, parfois c’est de l’anti - jeu.

Tout dépend des objectifs peut - être, obtenir un mat à zéro joueur est une chose, écrire une heuristique pour jouer contre l’ordinateur est je pense différent.

Je ne dirai pas que mon programme est un jeu éfficace, il est amusant pour moi pour le moment.

Ici donc, avec une profondeur de deux, j’obtien une mat assaie rapidement sur une machine de l’an dernier, un « Ryzen » 7 :

minmax-depth_2

Pour jouer aux échecs avec minmax il faut :

un jeu d’échecs
une condition d’arrêt
une fonction permettant d’obtenir l’ensemble des sucesseurs pour un état du jeu
une heuristique

Le jeu d’échecs est :

  • le tablier ou apron en english text
  • les pieces de même nom pour nos voisins
  • deux réservoirs pour stocker les pieces car la promotion permet d’en recupérer 8.

La fonction d’arrêt est une procédure qui établie une état comme en échec et en echec et mat ou en pat alors que ce dernier état est une fin de partie nulle.

Les successeurs sont l’ensemble des mouvement admis pour l’ensemble des pieces pour un joueur particulier, c’est à dire les movement des pione, des valeys (fous) ou bichop, les chavaliers, la dame, les tours, le roi.

L’heuristique est une fonction qui doit permettre d’obtenir la valeur de l’état d’un jeu pour le mesurer à d’autre et choisir le plus avantageux.

Je détallerai dans mon prochain message les caractéristique de mon heuristique caractérisée par mon amateurisme autant en programmation qu’aux échecs.

Bonjour,

Dans ce message, j’espere vous présenter correctement les caratéristiques de l’heuristique que j’ai utilisé pour jouer une partie de jeu d’échecs à zéro joueur.

D’abord je doit donner les valeurs de mes piece pour specifier l’ordre de grandeur des calculs pondérés de l’heuristique :
– Light values
W_Pawn_Val : Float := 1.0;
W_Tower_Val : Float := 4.5;
W_Chavalier_Val : Float := 2.55;
W_Valey_Val : Float := 2.5;
W_Queen_Val : Float := 16.0;
W_King_Val : Float := 250.0;

– Dark values
B_Pawn_Val : Float := 1.0;
B_Tower_Val : Float := 4.0;
B_Chavalier_Val : Float := 1.75;
B_Valey_Val : Float := 1.65;
B_Queen_Val : Float := 12.0;
B_King_Val : Float := 200.0;

Pour chaque set de valeurs auxquel on pourra donner des valeurs aprochantes est déterminée l’issue d’une partie.

Si j’avais donné les mêmes valeurs au blancs et aux noirs, l’issue de la partie serait différente.

Si l’on donne des valeurs démesurées aux pièces le programme risque de selectinner un coup invalide.

L’heuristique attends un paramètres en entrée qui est le jeu d’échecs et retourne un nombre en virgule flottante.

On peu évaluer de manière plus direct la valeurs d’un état du jeu en fournissant à une variante de cette heuristique l’état courant en plus du successeur à évaluer.

Pour évaluer la valeur du jeu j’ai d’abord utilisé deux variables qui pourrait être une constante nommées B_Count et W_Count contenant chacune la somme des valeurs des pièce pour chaque joueur.

En plus des données exprimer pour établir la nature d’un jeu d’échecs, le jeu doit maintenir deux autre valeurs qui sont le joueur courant et le coup courant ici nommés respectivement Current_Player mais non utilisé dans l’heuristique et Round.

Pour une raison de lisibilité, simplifier la lecture, bien que l’heuristique puisse être différente pour chanque joueur, ici l’algorithme est le même malgrès qu’il en faille deux car les observations sont relatives au joueur courant.

L’heuristique : (le connecteur « := » est utiliser pour indiquer l’affectation.

  1. on utlise deux variable de type « nombre à virgule flottante » permettant d’accumuler repectivement pour Hcost et Ucost, l’avantage et le handicap pour l’état

  2. Pour chaque joueur, on a une fonction retournant la somme des gains à l’état du successeur à évalluer nommées Black_Total et White_Total prenant deux paramettre dont le premier est le tableau des pièces prises et le second la position du dernier élément du tableau.
    (La taille de ces tableaux étant potentiellement de 15 places.

(Ici on prend l’exemple d’une évaluation pour les noirs)

I) on évalue les gains de l’état :

a) Ici seul le Total des blancs est pondéré avec une valeur dont l’ordre de grandeur correspond à celui de la valeur des pièces avec la relativité au enjeux du jeux d’échec tout au long d’une partie.

II) On va parcourir tous les successeurs du successeur courant nommé Usr pour évaluer le gain de l’adversaire qui deviens le handicap et le handicap pour les successeurs suivant du successeurs suivant à évaluer ; Ce qui constitu une évaluation avec 1 coup d’avance, ce qui est la moindre des chose me semble t - il si l’on veux espérer gagner.

Début heuristique :

   Hcost := (B_Count - (white_Total(Usr.white, Usr.white_last) * 666666.0 * Float(Usr.Round)) + (Black_Total(Usr.Black, Usr.Black_last)  * 3333333.0 * Float(Usr.Round)));

   Sucecsseurs := White_Successors (Usr);
    Pour Succ parcourant Successeurs faire
       Tmp := Successors(Succ);

       Si (white_Total(Tmp.white, Tmp.white_last) - white_Total(Usr.white, Usr.white_Last)) >= B_King_Val alors

       Retourner -((Ucost * 20.0) + (B_King_Val * 999_999_999_999_999.333));

       Si non
           Ucost := Ucost + (white_Total(Tmp.white, Tmp.white_last) - white_Total(Usr.white, Usr.white_Last));
                      Sum := 0.0;
                   
                      Pour Line dans Line_Range faire
                         Pour Colum dans Column_Enum faire
                            Si Tmp.Apron(Line, C2short(Colum)).Name in WK..WP alors

                                  Selon Usr.Apron(Line, C2short(Colum)).Name
                                     lorsque Empty =>
                                        null;
                                     lorsque WK =>
                                        Sum := Sum + W_King_Val/50.0;
                                     lorsque WQ =>
                                        Sum := Sum + W_Queen_Val/10.0;
                                     lorsque WC =>
                                        Sum := Sum + W_Chavalier_Val/10.0;
                                     lorsque WV =>
                                        Sum := Sum + W_Valey_Val/10.0;
                                     lorsque WT =>
                                        Sum := Sum + W_Tower_Val/10.0;
                                     lorsque WP =>
                                        Sum := Sum + W_Pawn_Val/2.0;
                                  Fin Selon;

                            Fin si;
                         Fin pour;
                      Fin pour;

                      Ucost := Ucost + Sum / Float(Successors.Successor);

          Fin si;
           Sum := 0.0;
           Tmp2 := Successors(Succ);
            Black_Succs : Successors_Type := Black_Successors(Tmp2);

           Pour Succ2 parcourant Black_Succ faire

                Black_Round := Black_Succs(Succ2);

                Sum := Sum + ((Black_Total(Black_round.Black, Black_round.Black_last) - Black_Total(Usr.Black, Usr.Black_Last)) * 10.0) - ((white_Total(Tmp.White, Tmp.White_last)) * 10000.0);

                Pour Line dans Line_Range faire
                            Pour Colum dans Column_Enum faire
                               Si Tmp.Apron(Line, C2short(Colum)).Name in BK..BP alors
                                     Selon Usr.Apron(Line, C2short(Colum)).Name
                                         lorsque Empty =>
                                           null;
                                        lorsque BK =>
                                           Sum := Sum + B_King_Val/5.0;
                                        lorsque BQ =>
                                           Sum := Sum + B_Queen_Val/10.0;
                                        lorsque BC =>
                                           Sum := Sum + B_Chavalier_Val/10.0;
                                        lorsque BV =>
                                           Sum := Sum + B_Valey_Val/10.0;
                                        lorsque BT =>
                                           Sum := Sum + B_Tower_Val/10.0;
                                        lorsque BP =>
                                           Sum := Sum + B_Pawn_Val/2.0;
                                     Fin selon;

                               Fin si;
                            Fin pour;
                         Fin pour;

              Fin pour;

             Hcost :=  Hcost + Sum / Float(Black_Succs.Nb_Successors);

       Fin pour;
       Ucost :=  (Ucost / Float(Successors.Nb_Successors));

    Retourner Hcost - Ucost;

    Fin heuristique;

a) La première évaluation selon le cas accumule dans la variable temporaire Sum le handicap relatif au déplacement possible des pieces sur l’état suivant, c’est à dire au mouvement suivant de l’adversaire.

b) La seconde évaluation selon le cas accumule dans la variable temporaire Sum l’avantage relatif au déplacement possible des pièces sur l’état suivant du coup suivant de l’adversaire.

C’est normal que l’issue soit différente si les valeurs sont différentes. Mais ne pourrait-on pas considéré de fait que les noirs sont diminués face aux blancs impactant ainsi leurs probabilités de gagner?
D’autant que les blancs commencent toujours.
Ton heuristiques implique directement une faveur à l’échange de pièces de valeurs différentes, ce qui va permettre de piéger facilement le camp qui a la valeur la plus forte (sauf s’il n’y aucun joueur humain bien sur).

Ma foi, c’est ce que je dis et j’ai fait exprès et je l’ai indiqué pour la clareté.

Ce qui rendrais le jeu caduc puisque les blanc aurais toujour la place des gagnats et c’est une question à concidérer avant de jouer.
J’ignore la réponse.

Comme c’est la règle, l’interrogation soulevée est de même nature que la précédante.

Non. L’heuristique est la même pour les blancs et les noirs.
C’est pas l’heuristique qui fera la distinction ; C’est bien en donnant des valeurs partuculière pour chacun des joueur qui implique que le résultat de l’heuristique differe.

Ici, je viens de tester, si j’affecte les blanc avec les valeurs des noirs, l’issue et strictement la même sauf pour les points et le temps de calcul qui se voir réduit d’un dizième de seconde.

Ce qui répond dans mon cas a la question évoquée plus haut. Non les blanc n’ont pas un avantage particulier.

Ah ah !

Extraordinaire ! Vraiement ! :clown_face:

Je fais une partie sur le serveur avec la précédente heuristique et je perd niveau zéro.

Pour l’histoire mon heuristique fonctionnait avec zéro de profondeur et pas dès que minmax se mettait à calculer.

Je me suis rendu compte qu’avec le nouvel algorithme qui est corrigé c’est au niveau zéro qu’il ne fonctionne pas.

Au niveau 1 avec deux de profondeur il fonctione.
Comme je suis en train de tester niveau 2 avec 4 de profondeur j’ai pas la preuve.

Et je me fais matter par le programme je pense !

Aidez moi s’il vous plait ?

Voila la situation.
J’ai les blanc, mon roi en vert est pris par les pions et menacé sur le côté par le fou.

Comme ça m’amuse …
chess-level_0-

Ouf !
)

Lorsqu’il a avancé le fou, j’ai pensé, je suis mauvais programmeur !

Pitiié ! :alarm_clock:

Je reviens avec un truc interressant.

L’algo précédent, qui a donc les problèmes énoncés, joue sur conseil du second, c’est à dire que je joue un coup au niveau 1 qui a le même effet à zéro.

Donc, en suivant les conseil du niveau 1, je devrais avoir un coup d’avance comme prétendu.
On va voir si c’est le cas !

C’est trop long / pas efficace, probablement inutile vu le nombre de moteurs d’echec existants et performants, et faits par des gens qui savent jouer aux échecs (ça semble la base), et connaissent « le coup du berger » que tout débutant connait.

Comment faire un tutoriel sans savoir jouer aux échecs ? Un tutoriel de quoi ?

Mon avis:
→ faire une pause, et prendre une grande respiration;
→ après la pause, se demander si ce bidouillage est vraiment utile;
apprendre à jouer aux echecs sérieusement pour estimer la beauté d’une partie, jauger la performance d’un moteur d’échecs / comparer;
→ analyser les moteurs d’echecs existants et disponibles sous linux (phalanx, gnuchess etc etc)
→ et conclure s’il n’est pas préférable de faire du jardinage par exemple.

Mais félicitations quand-même, parce-que ce n’est pas évident du tout de savoir comment concevoir un moteur d’échec, je ne sais pas comment faire, par quel bout prendre ça.
Donc bon exercice incontestablement, c’est courageux, mais sur 10 ans, sans savoir jouer aux échecs, il y a quelque-chose qui me chiffonne sérieusement.

C’est un tuto pour programmer un jeu d’échecs avec minmax peut être ?

Je pense pouvoir corriger un de mes propos, parce qu’avec 4 niveau de profondeur c’est à dire niveau 2 ça consomme dès le second tour.

Ah non c’est firefox !
Désolé !

Encore désolé, je me rends compte que l’algo qu’est sur le serveur est bouré d’erreur et que ça m’étonnerait qu’on vérifié quoi que ce soit sur cette partie.

Je vais essayer de poster un screen textuel…

Ca vous donne une estimation.
A droite les Best child values pour le coup des black au tour 2.
C’est un lien sur un ficher chess.txt sur mon site à 82.64.250.137 :
http://82.64.250.137/chess.txt

Je me suis trompé.

C’est 1 heure 26minutes le coup des noirs au tour 2.

En augmentant cosidérablement la valeur des rois et en factorisant par environ 10000.0 hcost dans le return de l’uristique et en réduisant 6.0 et 3.0 les facteur au produit avec le Round, c’est déjà plus interressant.

Quoi que, j’en sache rien.
J’ai upgrader le serveur mais j’obtien program error parce que le player joue mat.

Bonjour,

En vous priant de bien vousloir m’excuser d’un pat parce que l’exemple de partie postée au premier message est le fruits de multiple erreurs.

En suite, j’y ai passé la nuit, finalement l’heuristique devient :
Début heuristique :

   Hcost := (B_Count - (white_Total(Usr.white, Usr.white_last)) + (Black_Total(Usr.Black, Usr.Black_last) )));
Ucost := Ucost + (white_Total(Usr.white, Usr.white_last) );
Sucecsseurs := White_Successors (Usr);
    Pour Succ parcourant Successeurs faire
       Tmp := Successors(Succ);

       Si (white_Total(Tmp.white, Tmp.white_last) - white_Total(Usr.white, Usr.white_Last)) >= B_King_Val alors

       Retourner -((Ucost * 20.0) + (B_King_Val * 999_999_999_999_999.333));

       Si non

                      Sum := 0.0;
                   
                      Pour Line dans Line_Range faire
                         Pour Colum dans Column_Enum faire
                            Si Tmp.Apron(Line, C2short(Colum)).Name in WK..WP alors

                                  Selon Usr.Apron(Line, C2short(Colum)).Name
                                     lorsque Empty =>
                                        null;
                                     lorsque WK =>
                                        Sum := Sum + W_King_Val/10.0;
                                     lorsque WQ =>
                                        Sum := Sum + W_Queen_Val/10.0;
                                     lorsque WC =>
                                        Sum := Sum + W_Chavalier_Val/10.0;
                                     lorsque WV =>
                                        Sum := Sum + W_Valey_Val/10.0;
                                     lorsque WT =>
                                        Sum := Sum + W_Tower_Val/10.0;
                                     lorsque WP =>
                                        Sum := Sum + W_Pawn_Val/2.0;
                                  Fin Selon;

                            Fin si;
                         Fin pour;
                      Fin pour;

                      Ucost := Ucost + Sum / Float(Successors.Successor);

          Fin si;
           Sum := 0.0;
           Tmp2 := Successors(Succ);
            Black_Succs : Successors_Type := Black_Successors(Tmp2);

           Pour Succ2 parcourant Black_Succ faire

                Black_Round := Black_Succs(Succ2);

                Pour Line dans Line_Range faire
                            Pour Colum dans Column_Enum faire
                               Si Tmp.Apron(Line, C2short(Colum)).Name in BK..BP alors
                                     Selon Usr.Apron(Line, C2short(Colum)).Name
                                         lorsque Empty =>
                                           null;
                                        lorsque BK =>
                                           Sum := Sum + B_King_Val/10.0;
                                        lorsque BQ =>
                                           Sum := Sum + B_Queen_Val/10.0;
                                        lorsque BC =>
                                           Sum := Sum + B_Chavalier_Val/10.0;
                                        lorsque BV =>
                                           Sum := Sum + B_Valey_Val/10.0;
                                        lorsque BT =>
                                           Sum := Sum + B_Tower_Val/10.0;
                                        lorsque BP =>
                                           Sum := Sum + B_Pawn_Val/2.0;
                                     Fin selon;

                               Fin si;
                            Fin pour;
                         Fin pour;

              Fin pour;

             Hcost :=  Hcost + Sum / Float(Black_Succs.Nb_Successors);

       Fin pour;
       Ucost :=  (Ucost / Float(Successors.Nb_Successors));

    Retourner Hcost - Ucost;

    Fin heuristique;

Et les valeurs de mes pièces deviennent :

Chess Values

A une parenthèse près si ça match pas la syntaxe.

Pour un Dark echec and Darck mat.

Mais avec le jeu en erreur car les blanc ont mangé le roi.
Chess_on_Error