rose4 Projet C - IA de puissance 4

publicité
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
Téléchargement