rose4 Projet C - IA de puissance 4 Etienne Moutot 10 janvier 2015 Table des matières 1 2 3 4 5 Jeu du puissance 4 . . . . . . . Partie aléatoire . . . . . . . . . 2.1 decode(), to_file() . . . 2.2 play() . . . . . . . . . . Fonction de score . . . . . . . . 3.1 Fonction eval() . . . . . Algorithme minimax . . . . . . Raffinements . . . . . . . . . . 5.1 Fonction de score . . . . 5.2 Construction de l’arbre 5.3 Parallélisme . . . . . . . 5.4 En plus : master_ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 2 2 2 3 3 3 4 4 4 1 Jeu du puissance 4 Le jeu du puissance 4 est extrêmement connu et très simple. Deux joueurs jouent à tour de rôle en faisant tomber des pions dans une grille. Leur but est d’aligner verticalement, horizontalement ou en diagonale quatre pions de leur couleur, avant que l’adversaire ne fasse de même. Le but de ce projet a été de programmer en langage C un intelligence artificielle (IA) jouant au puissance 4. Dans une première partie, une IA jouant aléatoirement a permis d’implémenter les fonctions de jeu "basiques". Dans un second temps, une fonction de score a été crée pour évaluer la valeur d’un coup. En utilisant cette fonction et l’algorithme minimax, une IA a pu être programmée. Enfin, quelques modifications ont pu la rendre meilleure. 2 Partie aléatoire Dans cette version du programme, l’IA se contente de jouer aléatoirement dans une des 7 colonnes. Pour permettre ça, il a fallu implémenter les fonctions de décodage/encodage de la grille et de "jeu dans une colonne". Ces fonctions sont dans le fichier game.h, game.c. Une structure game contient le jeu ainsi que le tour du joueur en cours (c’est donc la représentation du fichier jeu en mémoire). 2.1 decode(), to_file() Ces deux fonctions ont comme rôle de charger la grille d’un fichier texte (decode()) et de la réécrire à partir de ce qui est en mémoire du programme (to_file()). Pour la lecture, on récupère le tour du joueur ayant joué en dernier, en première ligne (et on l’inverse). On parcourt ensuite la grille et on convertit tout sous forme d’entiers (0 étant une case vide, 1 un X et 2 un O). Pour l’écriture, c’est exactement le même principe, on écrit d’abord le joueur ayant joué, puis on reconvertit toute la grille en _, X et O qu’on écrit dans le fichier. 2.2 play() La fonction play() joue un coup dans la colonne donnée. Elle retourne 0 si tout s’est bien passé, -2 si le coup est hors de la grille et -1 si la colonne est pleine. Pour jouer ce coup, elle regarde juste quelle est la première ligne vide dans la colonne donnée, elle modifie ensuite le jeu directement. 3 Fonction de score Dans cette seconde partie, l’IA choisit la colonne maximisant la fonction eval(), donnant le score d’un coup. 3.1 Fonction eval() Cette fonction est implémentée dans les fichiers eval.h, eval.c Dans cette première version de la fonction, elle évalue le coup même, et pas la valeur de la grille. Le but de faire une fonction aussi simple est qu’elle ne nécessite pas de parcourir toute la grille et est plus rapide à exécuter (notamment pour la construction de l’arbre ensuite). 2 Elle prend en argument le jeu, et la case précise ou le jeton serait potentiellement posé. De là, elle compte le nombre de pions alignés autour de cette position donnée. Et c’est grâce à ça qu’elle ne parcourt pas tout le jeu : elle ne regarde que les 8 ∗ 3 cases autour de la positon donnée. Elle retourne le nombre de pions alignés par le coup ajouté au nombre qu’aurait le joueur adverse jouant au même endroit (c’est une sorte de valeur de "blocage"). Afin de rendre plus important un grand nombre de jetons alignés, on renvoie la somme des puissances pour induire un non-linéarité. 4 Algorithme minimax On ajoute maintenant un autre couple de fichiers : minimax.h, minimax.c, qui construirons l’arbre de l’algorithme minimax. Cet algorithme consiste, à chaque étape, à regarder tous les coups possibles de chaque joueurs, et à considérer soit celui qui maximise le score (si c’est au joueur de jouer) soit qui le minimise (si c’est à l’adversaire). On crée donc une structure d’arbre, qui contient la valeur d’un coup ainsi que la colonne à jouer pour obtenir cette valeur. En construisant récursivement l’arbre à l’aide de l’algorithme minimax, le coup à jouer sur la racine sera le meilleur coup à jouer pour l’IA. Pour construire ces arbres, il faut pouvoir les évaluer. Avec la première fonction d’évaluation, on ajoutait les valeurs des coups successifs pour avoir une idée de l’évaluation d’une suite de coup. On verra plus bas que suite au changement de fonction d’évaluation, l’algorithme a également un peu changé. 5 Raffinements Suite à la première version, de nombreuses petites choses ont été ajoutées et modifiées pour améliorer l’intelligence de cette IA. 5.1 Fonction de score Le problème de l’ancienne fonction d’évaluation est qu’elle ne prenait en compte que les pions juste autour du coup joué, pas la globalité de la grille. Elle pouvait donc ne pas voir un "ancien" alignement de trois pions un peu plus loin que le coup considéré à ce moment. La fonction d’évaluation a donc été modifiée, afin d’évaluer non pas la valeur d’un coup donné, mais celle de la grille en entier. Pour cela, on compte tous les alignements de chaque joueurs, et on les additionne, toujours avec une fonction puissance pour renforcer l’importance des nombreux pions alignés. Les pions alignés par je joueur comptent positivement, et ceux par l’adversaire négativement. Pour la construction de l’arbre minimax, on ne calcule donc plus que la fonction sur les feuilles, et on choisi pour les feuilles le max ou le min de ses fils en fonction du joueur considéré. Cela ressemble donc plus à l’algorithme minimax "normal". A été également ajouté un cas particulier pour forcer l’IA à jouer en cas d’alignement potentiel de 4 pions. Enfin, après des tests à la main de l’IA, elle semblait trop peu défensive, la valeur des pions de l’adversaire a donc été augmentée, afin de la forcer à jouer plus défensif qu’offensif. 3 5.2 Construction de l’arbre Un problème se posait en cas d’égalité de score entre deux coups, l’IA choisissait juste la première colonne. Cela posait problème notamment lorsque l’IA était certaine d’avoir gagné, et que tous ses coups pouvaient l’amener à la victoire, mais parfois dans de plus nombreux coups que d’autres, elle attendait le joueur pouvait la contrer. Il a donc fallu ajouter un test, et en cas d’égalité de score entre plusieurs configurations, on teste la valeur du coup (ancienne fonction d’évaluation), et on prend le coup ayant la plus haute. Une autre amélioration a été faite : si jamais un nœud possède un fils détectant la victoire ou défaite assurée (4 pions alignés pour un des joueurs), il n’est pas nécessaire de calculer les autres fils, on a déjà la valeur de ce noeud. 5.3 Parallélisme Afin d’augmenter la vitesse d’exécution, il est possible de paralléliser la construction de l’arbre, en utilisant la bibliothèque pthread. Cependant, créer un thread pour chaque nœud serait beaucoup trop important et contre-productif. On ne crée donc que 7 threads lorsque l’on construit la racine de l’arbre. Avec ces 7 threads indépendants, il faut s’assurer de toujours tous les arrêter, notamment dans le cas où on stoppe la construction pour cause de 4 alignés (voir plus haut), il faut les récupérer même s’ils n’ont pas tous les 7 été lancés. 5.4 En plus : master_ Afin de tester le programme, il était utile de le tester contre lui-même à différentes version, et contre un humain. Il a donc fallu créer deux petits programmes (master_human et master_IA) qui gèrent l’alternance des coups, et qui détecte la victoire d’un des deux joueurs. Note : le fichier généré par la compilation par le script sh est turn, cela correspond à un tour joué par l’IA. Le projet dispose d’un dépôt git disponible à l’adresse suivante : https://bitbucket.org/coma94/rose4 4