compte rendu tetris
Transcription
compte rendu tetris
PROJET : INTELLIGENCE ARTIFICIELLE D’UN TETRIS Présenté par : Olivier FOULT Guillaume CRESTA Groupe G 21 TABLE DES MATIERES Ce compte rendu comporte plusieurs parties comme indiqué ci – dessous. Description de l’objet du projet Les algorithmes de principe Les fichiers utilisés Quelques Programmes commentés Makefile Impressions générales et conclusion p. 3 p. 4 p. 8 p. 10 p.11 p.12 L’objet du projet est de réaliser un tétris à intelligence artificielle. Cela va permettre au tétris de jouer tout seul. Nous avons tenté de réaliser un programme d’intelligence artificielle chargée de tester différents poids, pour chaque position possible, afin de jouer de mieux en mieux au tétris. Par conséquent, afin de jouer seul, on fait intervenir des poids fixé aléatoirement au départ (mais positif) ; ces poids pouvant être le nombre de lignes, la hauteur maximale du tétris, les trous, les lignes où est posé la pièce, l’homogénéité du tétris, la moyenne des hauteurs de la pièce et la plus haute ligne de la pièce. Le but de ce tétris est que l’ordinateur « apprenne » et s’améliore au fur et à mesure de la partie. Pour cela, il va faire varier d’un delta aléatoire (positif ou négatif) les poids fixés au départ. Si le nombre de lignes obtenu avec ce nouveau jeu de poids est supérieur au meilleur score que l’on avait obtenu, alors le nouveau jeu de poids est conservé, on recommence ainsi jusqu’à ce que le nombre de lignes n’augmente plus. Le résultat après chaque test, est retourné sous forme texte. Algo d’apprentissage Nous avons choisis de prendre un maximum de 20 itérations maximales pour chaque jeu de poids. Apres quoi le tetris s’arrêtera si il n’a pas trouve de meilleur solution. Tant que le compteur d’itérations est inférieur a 20 Choix de 3 voisins parmi les 7 poids présents. Choix de l’implémentation entre -40 et 40 (a l’aide de la fonction alea) Pour chaque poids faire Si la valeur du poids est positive alors On joue au tetris avec 10000 pièces. On sauvegarde le score total (nombre de lignes) Remise a 0 de score total Le tableau est remis dans sa forme initiale Si l’un des score total est supérieur au score max alors Score max=Score total Si le score max a été implémente on implémente le tableau des poids avec le poids correspondant On remet le nombre d’itération a 0 On incrémente le compteur d’itérations Lorsque 20 itérations successives sont jouées sans aucune améliorations, on stop la boucle et on sauvegarde les poids. On réitère le procédé 3 fois en repartant sur des tetris aléatoires à chaque fois A la fin des 3 tetris on peut jouer avec l’un des différents jeux de poids. Algo de jeu tetris Tant qu’aucune piece n’est en haut du tetris et que le nombre de pièces jouées est inférieur a 10000 faire Choix d’une pièce aléatoire Pour chaque rotation de la pièce faire Appelle de la fonction recherche_piece qui recherche la meilleure place pour la pièce Sauvegarde du score s’ il est supérieur à tous les scores précédents De la ligne De la colonne Et des positions de descente de la pièce Si le score est différent de 0 c' est-à-dire qu’une place valide a été trouvé pour une pièce au moins alors On place la pièce à l’emplacement ligne, colonne On recherche le nombre de ligne(s) et on les supprime le cas échéant Incrémentation du nombre de pièces jouées Renvoie le nombre de lignes faites et modifie le nombre de pièces jouées Algo de test de collisions Pour chaque ligne Pour chaque colonne Si la case est remplie à la fois par la pièce et par une des pieces déjà présente dans le tetris booleen = 1 Retourne la valeur inverse du booleen, c’est à dire si une case est déjà remplie, donc qu’on ne peut pas insérer la pièce à cet endroit. Algo de vérification de la position de la pièce Tant que non bool et ligne inferieur à 4 Colonne = 0 Tant que colonne inférieur à 4 et non bool Si la case est remplie par la pièce Si la case d’en dessous est remplie par une pièce du tetris Bool = 1 colonne=4 Incrementation de colonne Incrementation de ligne On retourne la valeur de bool, c' est-à-dire que la place est valide (du moins elle est posée sur une ancienne pièce) Algo pour vérifié que la pièce n’est pas dans un trou non accessible Tant que ligne est inférieur à 13 (on n’est pas en haut du tetris) et compteur inférieur à 10 Si la pièce est valide pour la ligne d’au dessus et la même colonne Compteur =0 Incrémentation de la ligne On place 0 (la différence entre la colonne de départ et la colonne actuelle) dans la liste chaînée Sinon Cas suivant la valeur de k Cas ou k = 0: Si la pièce est valide pour la colonne de droite On incrémente la colonne On place 1 dans la liste chaînée Sinon K=non k Cas ou k=1 : Si la pièce est valide pour la colonne de gauche On décrémente la colonne On place -1 dans la liste chaînée Sinon K=non k On incrémente le compteur On place la dernière valeur de la colonne dans la liste chaînée (la première des valeurs à afficher lors de la descente de la pièce) Si le compteur est différent de 0 (la pièce ne peut pas remonter jusqu’en haut) On libère la liste chaînée On retourne un pointeur sur la liste chaînée (qui sera à null si la pièce n’a pas pu « remonter ») Algo de recherche de trous Pour chaque ligne (du tetris) Pour chaque colonne (du tetris) Si l’emplacement est vide Compteur = ligne Tant que compteur < 16 (hauteur du tetris) Incrémentation du compteur Si l’emplacement ligne : ligne, colonne : compteur est occupé Incrémentation du nombre de trous Compteur = 17 (pour sortir) On retourne le nombre de cases du tetris moins le nombre de trous (plus il y a de trous moins le score est élevé) Makefile C’est le makefile du projet qui comporte la commande « make » qui compile le projet et permet d’enlever le moindre warning : grâce à la commande : CC= gcc -ansi -Wall -pedantic -02). alea.h/alea.c Ici se trouve uniquement la fonction alea qui permet de générer un nombre aléatoire allant de 1 à N. main.h C’est ici que figure les différents types de structures utilisées. Il regroupe de plus tous les autres .h . modif_tab.h/ modif_tab.c Ici se trouve la fonction recherche_ligne qui comme son nom l’indique permet de rechercher les lignes dans le tétris, elle peut appeler si on lui demande la fonction supprime_ligne pour la suppression des lignes dans le tétris, la fonction homogénéité permettant de calculer l’homogénéité de la surface du tétris, la fonction hauteur qui permet de rechercher la hauteur maximale du tétris, la fonction trou permettant de rechercher le nombre de « trous » dans le tétris, la fonction moy_piece permettant de calculer de la moyenne des hauteurs de la pièce et la fonction haut_max_piece qui recherche la plus haute ligne de la pièce. piece.c/piece.h Dans ce fichier, on peut trouver creation_piece permettant de créer une pièce du tétris (comme dans le jeu initial) et on crée toutes les pièces représentant ses rotations. Chaque pièce est représentée par un tableau de 16 cases. Chaque case est remplie à 1 si la case appartient à la pièce, et à 0 sinon. La fonction affichage_piece permet d’afficher une pièce du tétris, la fonction placement nous permet de placer la pièce à l’endroit : ligne, colonne, la fonction recherche_piece permet de rechercher la meilleur place d’une pièce dans le tétris en calculant le score de chaque pièce pour tous les endroits valide du tetris. Et la fonction supprime_piece de supprimer la pièce à l’endroit : ligne, colonne (utile si on a trouvé une meilleure solution). pro1.c / pro1.h Ici se trouve la fonction affichage permettant l’affichage du tableau contenant le tétris, la fonction creation pour créer et initialiser le tableau tétris et la fonction princ pour le tétris en lui-même. tetris.h / tetris.c C’est ici que figure la procédure d’affichage du tableau affichage_tab des poids qui sont le nombre de lignes, la hauteur maximale du tétris, les trous, les lignes où est posé la pièce, l’homogénéité du tétris, la moyenne des hauteurs de la pièce et la plus haute ligne de la pièce ; la procédure random qui donne au tableau de poids des valeurs aléatoires ; et, le main du programme qui appelle le tetris avec des poids qu’il aura choisi au préalable. C’est cette fonction qui détermine l’ « intelligence » du tétris, elle choisit les voisins, l’implémentation, et vérifie que 10000 pièces sont jouées pour chaque jeu de poids. verif.h / verif.c Dans ce fichier se situent les fonctions valide qui permet de tester les collisions entre la pièce et les pièces déjà présentes dans le tétris, verif1 qui permet de vérifier que la pièce est posée sur une autre pièce, verif2 permet de voir si la pièce ne figure pas dans un trou bouché, allocation qui insère un élément dir_t en tête de la liste chaînée et lui affecte la valeur val, et lib qui permet de libérer la liste chainée de données dir_t pointée par la tête. La structure direction typedef struct direction { int val; struct direction *suiv; }dir_t; La structure direction sert pour l’affichage de la descente de la pièce (si le choix de l’utilisateur l’a demandé). La liste chaînée indique pour chaque ligne si la colonne a été modifiée. Elle permet nottamment de pouvoir insérer des pièces dans des « trous ». La structure place typedef struct place { int piece; int ligne; int colonne; int score; dir_t *dir; }place_t; Cette structure est renvoyée par la fonction recherche_piece, elle sert a donné à la fonction tetris la meilleur valeur de score trouvée ainsi que la colonne et la ligne qui lui corresponde. Elle renvoie aussi la liste chaînée permettant de faire descendre la pièce. La stucture voisinage typedef struct voisinage { int voisin; int score; int implementation; }voisinage_t; Cette structure permet de sauvegarder pour chaque voisin choisi le score atteint et l’implémentation effectuée si jamais ce voisinage était concluant et que le programme devait ré implémenter le poids. CC = gcc -ansi -Wall -pedantic -O2 tetris: pro1.o piece.o modif_tab.o alea.o verif.o tetris.o $(CC) pro1.o piece.o modif_tab.o alea.o verif.o tetris.o -o tetris pro1.o: main.h pro1.c $(CC) -c pro1.c piece.o: main.h piece.c $(CC) -c piece.c modif_tab.o: main.h modif_tab.c $(CC) -c modif_tab.c alea.o: main.h alea.c $(CC) -c alea.c verif.o: main.h verif.c $(CC) -c verif.c tetris.c: main.h tetris.c $(CC) -c tetris.c Les jeux de poids semblent toujours converger vers une solution plus ou moins identique. Néanmoins pour aboutir à ce résultat l’implémentation est longue et laborieuse. Le hasard ne se dirigeant pas toujours vers la bonne solution. C’est pour cela que nous avons choisi de pouvoir implémenter chaque valeur de poids dans une large bande (à savoir -40,40) ainsi on peut avoir des sauts importants. Cela permet de se sortir parfois de solutions ou tous les voisins « proches » sont moins bons mais qui sont de « mauvaises » solutions. Le tetris ne joue pas un nombre de jeux déterminés mais un nombre de pièces déterminé ce qui limite la part de hasard dans le jeu. Cela n’exclut pas pour autant la part de hasard donnée aux pièces qui sont tirées aléatoirement. Des écarts peuvent subsister sur le nombre de lignes finales sur des jeux de poids identiques. On constate qu’avec les poids finaux du tetris permette au programme de jouer de façon très impressionnante, lui permettant de faire facilement plusieurs milliers de lignes. Nous avons rencontré un léger problème de gestion de mémoire dans la structure dir_t malgré les différentes et nombreuses libérations que nous avons pu effectuées. Poids obtenu avec le programme tetris : Nombre de lignes Hauteur max du tetris Trous Ligne ou est posée la pièce Homogénéité du Tetris Moyenne des hauteurs de la pièce Plus haute ligne de la pièce tab[0] tab[1] tab[2] tab[3] tab[4] tab[5] tab[6] ************ tetris final ************ tetris initial : (poids aléatoires) tab[0]= 53 tab[1]= 83 tab[2]= 71 tab[3]= 36 tab[4]= 73 tab[5]= 70 tab[6]= 97 *** fin *** ************ score max= 2856 ************ Iteration 20 tab[0]= 139 tab[1]= 13 tab[2]= 171 tab[3]= 43 tab[4]= 14 tab[5]= 28 tab[6]= 36 Remarque : Le score max est obtenu avec 10000 pièces jouées. Le score pourrait être meilleur si on le laissait finir sa partie. Le score représente le nombre de lignes faites (et non le score réel du tetris). Nous n’avons pas eu assez de temps pour faire une bonne partie graphique du tétris. Nous avons tenté de nous rapprocher des poids les mieux adapté sans pour cela arriver à des résultats super. Cependant, on a pu s’apercevoir que lorsqu’il joue, l’ordinateur progresse de plus en plus.