Aymeric Lesert 28 mars 2003 Lines Of Action I. Les règles du jeu Lines Of Action est un jeu de plateau 8x8 opposant 2 adversaires. Chaque joueur dispose de 12 pions. Le joueur qui a gagné est le joueur qui a réussi à connecter tous ses pions. Les joueurs jouent chacun leur tour et déplacent un seul pion. Un pion se déplace en ligne droite (horizontal vertical ou diagonal). Il peut sauter les pions de son camp mais pas ceux de son adversaire. Il peut s’arrêter sur un pion adverse, dans ce cas, le pion adverse est capturé. Il se déplace du nombre de cases égal au nombre de pions de son camp ou adverse sur la ligne de déplacement. Il doit toujours s’arrêter dans le plateau et si il n’y a pas assez de cases pour le déplacement, le déplacement est impossible. Situation initiale Le blanc doit jouer Situation terminale II. L’algorithme Min-Max A. Principe de base On suppose que chaque joueur choisira son meilleur coup, le meilleur coup de l'adversaire correspondant au moins bon coup du joueur courant. On "remonte" de proche en proche les valeurs des feuilles (fins de parties) jusqu'à la racine (position courante) ; on peut ainsi choisir le coup qui mène à la position la plus favorable. Cela implique, pour un coup optimal, de parcourir tout l’arbre des solutions. Exemple Lines Of Action : Stratégie et Optimisation Aymeric Lesert 28 mars 2003 Lines Of Action B. Les optimisations 1. L’élagage Alpha-Beta L'algorithme minimax effectue une exploration complète de l'arbre de recherche jusqu'à un niveau donné, alors qu'une exploration partielle de l'arbre pourrait suffire. Il suffit en effet, dans l'exploration en profondeur d'abord et de gauche à droite, d'éviter d'examiner des sous-arbres qui conduiront à des configurations dont la valeur ne contribuera sûrement pas au calcul du gain à la racine de l'arbre. Plus généralement, lorsque dans le parcours de l'arbre minimax, il y a modification de la valeur courante d'un nœud, si cette valeur franchit un certain seuil, il devient inutile d'explorer la descendance encore inexplorée de ce nœud. On distingue deux seuils, appelés α (pour les nœuds Min) et β (pour les nœuds Max) : le seuil α, pour un nœud Min s, est égal à la plus grande valeur (déjà déterminée) de tous les nœuds Max ancêtres de s ; si la valeur de s devient inférieure ou égale à α, l'exploration de sa descendance peut être arrêtée ; le seuil β, pour un nœud Max s, est égal à la plus petite valeur (déjà déterminée) de tous les nœuds Min ancêtres de s. Si la valeur de s devient supérieure ou égale à β, l'exploration de sa descendance peut être arrêtée. L'algorithme Alpha-Beta peut être décrit informellement par la fonction suivante, qui maintient ces deux seuils pendant le parcours de l'arbre. Une invocation de alphabeta(s, -∞, +∞) détermine une évaluation du jeu issu de la configuration s. Exemple : 2. Le killer-move Cette optimisation est utilisée pour couper une branche plus tôt et, ainsi, éviter un parcours inutile dans une branche. En général, le coup qui a coupé dans la sous-branche à gauche aura une plus grande probabilité de couper la sous-branche courante. Cela veut dire que le coup qui a coupé une sous-branche à un niveau est mémorisé pour ensuite être proposé en premier dans la sous-branche de même niveau immédiatement après, dans le cas où le coup est jouable. Ici, j’ai pris la peine d’élargir la définition du killer-move (coup-qui-tue) au coup ayant eu le poids le plus fort (pour Max) ou le poids le moins fort (pour Min). De même, il est possible d’étendre le nombre de coup-qui-tue par profondeur et, dans ce cas, le KillerMove se comporte comme une pile. Il devient un ensemble de coups jouables avant les coups possibles (du plus récent au plus ancien). Lines Of Action : Stratégie et Optimisation Aymeric Lesert 28 mars 2003 Lines Of Action 3. La table d’historique Cette optimisation est utilisée pour attribuer un poids à un coup. Plus le coup est retenu comme meilleur coup à une profondeur faible, plus son poids est élevé. Et, plus il est retenu, plus il a d’importance. Pour que cette optimisation puisse être utilisée, il faut avoir une fonction qui attribue un indice à un coup. Cet indice doit être unique par coup. La table d’historique est un tableau d’entier mémorisant la somme des 2p (où p = profondeur maxi – profondeur courante) quand un coup est retenu comme meilleur coup. 4. La table de transposition a) Principe Un arbre de parcours des coups aux échecs peut être vu comme un graphe, où les transpositions peuvent retenir un cheminement déjà exploré. Une Hash-table de transposition peut être utilisée pour détecter une situation précise et, ainsi, éviter de développer un arbre déjà parcouru. Exemple aux échecs : La situation après 1. e4 d6 2. d4 est la même que la situation après 1. d4 d6 2. e4. Il s’avère que la combinatoire pour détecter ces situations est extrêment élevée. Il faudrait pour chaque position parcourue mémoriser le plateau et à chaque nouveau coup, regarder si le nouveau plateau correspond ou non à une plateau déjà parcouru. La hash-table de transposition permet de répondre à cette problématique avec un temps optimal en accédant directement à une situation. Elle est également très souvent utilisée avec l’algorithme AlphaBeta avec profondeur itérative. Cela permet de réduire de manière très importante le recalcul de certaines situations. b) La clé de Zobrist Pour se faire, il faut définir une clé de Zobrist pour chaque pion différent, sur chaque case et pour chaque joueur. Exemple aux échecs : Le damier fait 8x8, il y a 2 joueurs (les noirs et les blancs) et 6 pions par joueur (Tour, Fou, Pion, Roi, Reine, Cavalier) et un pion neutre (la case vide). Il faut alors définir 8*8+8*8*2*6=832 clés de Zobrist. Une clé de Zobrist est un nombre aléatoire sur 64 bits (si votre architecture le permet) ou 32 bits. Une situation correspond alors au XOR de 8*8=64 clés de Zobrist représentant les pions sur le plateau. c) La hash-table Une hash-table peut alors mémoriser un grand nombre de situations. Mais, il faut que cette taille soit suffisamment grande pour être significative. d) Utilisation de la table de transposition Un coup joué ou déjoué correspond à une seule opération le XOR de la position initiale et le XOR de la position finale. L’accès en lecture de la table de transposition est effectué avec la lecture des coups possibles afin de vérifier si la situation n’a pas déjà été évaluée. Remarque : Si deux situations ont la même valeur, il est probable que cela puisse être à l’origine d’un malentendu car, imaginons qu’une des deux situations soit une situation perdante et que l’autre est une situation gagnante, l’algorithme risque de jouer le coup perdant en croyant qu’il est un coup gagnant. Dans le cas où le meilleur coup évalué pour une situation n’est pas une situation terminale, le coup évalué doit être proposé comme un KillerMove. Il est même conseillé de le positionner en tête. 5. Le Null Move Le null-move consiste à faire comme si l’adverse ne peut pas jouer, pour vérifier si nous sommes proches d’une situation terminale ou favorable. Lines Of Action : Stratégie et Optimisation Aymeric Lesert 28 mars 2003 Lines Of Action 6. La recherche de quiescence La recherche de quiescence consiste à éliminer le phénomène d’horizon. En effet, comme la profondeur de l’algorithme est limitée, il est impossible de voir si la branche non-terminale que nous venons d’évaluer est une branche menant à la victoire ou à la défaite : seule l’évaluation peut le faire. Mais, dans certain cas, cette évaluation est trompeuse, surtout si nous sommes sur une position de prise comme aux échecs. Dans ce cas, il faut pouvoir limiter la valeur d’une situation à la valeur stable de cette dernière. Si la situation évaluée n’est pas stable, il est peut être intéressant de rechercher une situation plus stable pour l’évaluer. Cette stratégie dépend également du jeu. C. L’algorithme Min/Max itératif Cet algorithme fait appel à l’algorithme AlphaBeta décrit ci-dessus en faisant varier la profondeur maxi de manière croissante sans réinitialiser la table de transposition, les killer-moves ou la table d’historique afin de réutiliser à chaque fois les données calculées avant (évite de recalculer des situations peut-être déjà évaluées). Il permet aussi de mieux maîtriser les temps de parcours. III. Les différentes versions A. Version 1 : Min/Max Pour cette première version, 15 secondes est laissé à l’algorithme pour trouver un coup à jouer. Les optimisations utilisées sont : une table de transposition, le killer-move, l’algorithme itératif et le tri des coups à jouer. La quiescence et le Null-move n’ont pas été implémentés. Nous matérialisons le plateau comme étant une matrice 8x8 dont chaque case peut prendre 3 états (VIDE, PION1 ou PION2). Pour faciliter le test de situation terminale, nous mémorisons pour chaque joueur la liste de ses segments (ensemble de pions connectés). Une situation terminale est un joueur ayant un seul segment. La liste des coups jouables est triée selon l’ordre suivant : le coup proposé par Zobrist, le coup proposé comme killer-move, puis les coups dans l’ordre de calcul. La fonction d’évaluation d’une situation est la suivante : Nb _ segmentsNb _ pionsi C * i 1 Max( X ij Cx , Yij C y ) j 1 Nb _ pions _ joueurs 8 8 (Pij*Cij) où (Cx,Cy) = Centre de gravité du joueur i 1 j 1 Remarques : Cette version est un piètre adversaire. Ses coups sont prévisibles et facilement déjouables. De plus, elle est peut optimisée et est gourmande en temps de calcul. Lines Of Action : Stratégie et Optimisation Aymeric Lesert 28 mars 2003 Lines Of Action B. Version 2 : Apprentissage L’idée qui a été exploitée dans cette version est la possibilité pour le jeu d’apprendre à jouer face à un joueur humain. A chaque partie, il mémorise les coups menant à la victoire et à la défaite. Pour chacune des coups, on mesure la profondeur la plus courte vers la défaite et vers la victoire. Quand l’ordinateur doit jouer, il calcule les coups possibles et consulte sa base de données des situations pour évaluer le meilleur coup à jouer. Le meilleur coup à jouer est celui qui est le plus proche de sa victoire et les plus éloignés de sa défaite. J’avais posé comme hypothèse que plus l’écart était grand, plus il était probable que la solution était bonne. Remarques : Après quelques tests, il s’est avéré que cette stratégie était mauvaise. La grande quantité d’informations à retenir et le manque de visibilité ne permettait pas d’aboutir à une solution convenable. Elle évitait de faire deux fois la même erreur mais n’était pas futée pour trouver la victoire ! C. Version 3 : Min/Max Cette version est une reprise de la version 1. Lors de la liste des coups jouables, nous attribuons un poids à chaque coup pour faciliter l’ordonnancement : Nombre de pions adverses – Nombre de pions du joueur courant autour de la case à jouer. La fonction d’évaluation du plateau est plus complexe que la fonction de la version 1 : Nb _ segmentsNb _ pionsi C jc * i 1 Max( X ij Nb _ pions _ joueurs Nb _ segmentsNb _ pionsi C ja * Cx , Yij C y ) j 1 i 1 Max( X ij Cx , Yij C y ) j 1 Nb_ pions _ joueurs 8 8 (Pij*Cij) pour le joueur courant i 1 j 1 8 8 (Pij*Cij) pour le joueur adverse i 1 j 1 P K1*(C ja C jc) K2*(Nb_ segments ja Nb_ segments jc) K3*( Nb_ pions ja Nb_ pions jc ) Nb_ segments ja Nb_ segments jc Remarques : Cette version est un adversaire intéressant. Ses coups sont moins prévisibles. Mais, il n’est pas encore un adverse de taille. A vrai-dire, je pense que cette fonction n’est pas mauvaise. L’enjeu est de déterminer les coefficients à attribuer à chacun des composants du calcul du plateau. D. Version 4 : Algorithme Génétique Dans cette version, l’idée est de laisser faire le hasard. On dispose d’un certain nombre d’indicateurs (ici, 14). Il faut également disposer d’un générateur aléatoire de fonctions mathématiques, de fonctions de mutations et de croisements de ces fonctions. Pour un joueur, nous posons deux fonctions, la première fonction est celle de l’évaluation d’un coup et la deuxième est l’évaluation d’un plateau. Ensuite, il suffit de générer une population d’une dizaine d’individus et de les confronter sous forme de tournoi (en utilisant l’algorithme Min/Max). A la fin de chaque tournoi (une génération), nous gardons les meilleurs individus. Nous les croisons ou les mutons et nous injectons de temps en temps de nouveaux individus. En théorie, cette technique doit permettre de converger vers un individu de plus en plus fort. Puis, après quelques générations, nous pouvons nous y confronter pour voir si elle est pas trop mauvaise. Remarques : Malheureusement, je n’ai pas pu montrer la validité de ce genre de procéder. Lines Of Action : Stratégie et Optimisation Aymeric Lesert 28 mars 2003 Lines Of Action IV. Références Les régles du jeu : http://www.andromeda.com/people/ddyer/loa/ Lines Of Action : http://games.cs.ualberta.ca/webgames/loa/ Explications de la table de transposition : http://www.seanet.com/~brucemo/topics/hashing.htm Explications de la clé de Zobrist : http://www.seanet.com/~brucemo/topics/zobrist.htm Pour l’algorithme MinMax et AlphaBeta : http://www-poleia.lip6.fr/~ralaivo/dauphine/projets/node23.html Lines Of Action : Stratégie et Optimisation