Upload
lamphuc
View
249
Download
1
Embed Size (px)
Citation preview
Langage C
Projet : Puissance 4
Florent Brunet
Olivier Coupelon
Licence Informatique, 3eme Annee, Groupe 2
14 decembre 2004
Table des matieres
1 Introduction 2
2 Theorie 2
2.1 Negamax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.2 Algorithme α − β . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.3 Systemes experts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3 Implementation 2
3.1 Structures de donnees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23.2 Intelligence artificielle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.2.1 Evaluation d’un coup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33.2.2 Implementation de l’algorithme negamax avec α − β . . . . . . . . . . . . . . . 3
3.3 Graphe des appels de fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4 Details techniques 5
4.1 Glade & Libglade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54.2 Deroulement evenementiel du jeu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64.3 Optimisations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
5 Utilisation 6
5.1 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65.2 Liste des fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65.3 Utilisation du programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
6 Conclusion 7
A Code source 7
1
1 Introduction
On se propose dans ce projet de realiser en langage C un jeu de Puissance 4. Nous etudierons dansune premiere partie les principes theoriques d’une intelligence artificielle dediee a ce jeu. Nous verronsdans une seconde partie comment l’implementation des differents algorithmes. Enfin nous expliqueronscomment le programme s’utilise.
2 Theorie
2.1 Negamax
L’intelligence artificielle de notre programme repose sur l’utilisation d’un algorithme de typenegamax. En voici le principe :A partir d’une grille de jeu donnee, on genere l’ensemble des positions que l’ordinateur peut atteindre.Pour chacunes de ces positions, on genere l’ensemble des posistions que l’adversaire pourrait a sontour atteindre. On recommence l’operation aussi lontemps que le permet la puissance de calcul del’ordinateur. On construit ainsi un arbre representant l’ensemble des configurations de la grille dujeu pour un certain nombre de coups a l’avance. Il est clair que par cette technique, on ne peut pasconstruire l’arbre complet de la partie, c’est a dire un arbre dont toutes les feuilles seraient des coupsterminaux (gagnant ou match nul). Puisqu’on est oblige de limiter la profondeur de l’arbre, il estnecessaire d’evaluer une position non terminale pour un joueur donne.
2.2 Algorithme α − β
Tel que presente ci-dessus, negamax calcule sans distonctions toutes les positions jouables de chaquegrille. Afin de limiter le nombre de noeuds de l’arbre, l’algorithme α−β empeche le calcul des positionsjugees ininteressantes.
2.3 Systemes experts
Nous avions initialement envisage d’implementer un systeme expert, c’est a dire une strategieimbattable. En effet il a ete demontrer par Victor Allis1 qu’il existe des regles implementables assurantla victoire du premier joueur. Ces regles sont basees sur une analyse directe de la grille avec seulementhuit regles permettant de determiner a coup sur le prochain coup devant etre joue.
Cependant, ces regles se sont averees trop difficiles a mettre en place dans le temps imparti. C’estpourquoi nous avons prefere utilise l’algorithme negamax, moins precis mais plus rapide a implementer.
3 Implementation
3.1 Structures de donnees
Voici la structure de donnee proposee pour representer une grille de jeu. On peut constater qu’elleest dynamque, ce qui permettrai de jouer avec des grilles de tailles differentes.
typedef struct grille
{
int x;
int y;
int *tab;
} grille;
1Victor Allis, A Knowledge-based Approach of Connect-Four : http ://www.connectfour.net/Files/connect4.pdf
2
Pour representer l’etat de la partie en mode graphique, nous utilisons une structure globale conte-nant toutes les informations necessaires au deroulement du jeu. Ce n’est pas necesssaire dans le modetexte puisque tout le deroulement du jeu s’effectue dans la boucle principale.
3.2 Intelligence artificielle
Nous avons vu dans la partie theorique qu’une grille devait pouvoir etre evaluee. Ceci est realiseau moyen d’une fonction determinant l’amelioration apportee par un certain coup joue. L’utilisationd’un arbre permet de detecter le coup permettant de s’assurer les meilleures chances de gagner par lasuite.
3.2.1 Evaluation d’un coup
On utilise en premier lieu une structure permettant de determiner pour une case donnee l’ensembledes alignements de quatres pions auquelle elle participe. Par exemple, pour la case en bas a gauche, troisalignements sont possibles : horizontalement, verticalement et diagonalement, remarquons que pourd’autres cases, il est possible d’avoir plusieurs aligements de toute sorte. Voici un exemple montrantles alignements possibles autour du pion grises :
Dans le cas actuel du Puissance 4 a 7 colonnes, 6 lignes et des alignements gagnants de 4 pions, ily a un total de :
4 × 7 × 6 − 3 × 7 × 4 − 3 × 6 × 4 + 3 × 7 + 3 × 6 − 4 × 4 + 2 × 42 + 2 = 69
On introduit alors pour chaque joueurs un tableau de statistiques de 69 cases. Ce tableau contient,pour chaque alignement, le nombre de pions qu’il y possede, ou bien 0 si des pions de l’adversaire sontaussi dans cet alignement. On peut alors determiner rapidement la qualite d’un coup en observantl’evolution des valeures de ces statistiques. C’est le propos de notre fonction d’evaluation.
3.2.2 Implementation de l’algorithme negamax avec α − β
Notre implementation de negamax l’algorithme est une traduction en langage C de l’algorithmesuivant :
3
Algorithme 1 : Evaluation(joueur,alpha,beta) : Algorithme negamax avec α − β
Resultat : Valeur estimee de la grille testee recursivementdebut
si on est a la profondeur maximum alorsretourner Estimation de la grille
sinon
pour chaque coups jouables dans la colonne C faireSauvegarder la grille couranteJouer en Cscore = -Evaluation(joueur,-beta,-alpha)si score > alpha alors
alpha = score
si alpha ≥ beta alorsRestaurer l’ancienne grilleretourner alpha
Restaurer l’ancienne grille
retourner Meilleur score
fin
4
3.3 Graphe des appels de fonctions
ai_evaluation_grille
autrepile_etat_restaure
pile_etat_sauve
score_update
test_play
grille_cpy
stats_cpy
lst_suivant
poss_read
stats_set
get_grilleis_playable
is_valid
set_grille
ai_test_play
display_grid
grille_free
grille_init
jeu_free
jeu_init
pile_etat_init
poss_init stats_init
lst_init
lst_insere
lst_libere
lst_nbelem
lst_reset
main
pile_etat_libere
poss_free
stats_free
stats_to_string
4 Details techniques
4.1 Glade & Libglade
Nous avons realise l’interface grace au logiciel Glade. Celui permet de realiser rapidement les inter-faces graphiques, et de les stocker au format XML. Ces ficheirs peuvent etre incorpores au programmepar l’intermediaire de la librairie libglade. Ce systeme introduit une certaine souplesse car il n’est pasnecessaire de recompiler le programme lors d’une modification esthetique de l’interface graphique. Deplus, le code source est allege de toute la gestion de la creation et de l’affichage des Widgets.
5
4.2 Deroulement evenementiel du jeu
Le changement de joueurs s’effectue de la maniere suivante :– Creation d’un Objet de type GObject dont le but est de capte les evenements que nous allons
creer par la suite.– Creation de l’evenement switch qui va etre appele chaque fois qu’un joueur a fini de jouer. Son
role est d’effectuer les changements necessaires d’un joueur a un autre.– Creation de l’evenement IA declenche par l’evenement switch quand c’est a l’ordinateur de jouer.Nous avons choisi d’utiliser les possibilites evenementielles de GTK car elle permettent une gestion
plus adaptee du changement de joueur dans un environnement graphique. Ainsi, nous n’avons pas eubesoin de bloquer l’interface afin d’attendre les actions de l’utilisateur. En cours de jeu, l’utilisateurest libre d’utiliser les menus. Lorsque l’ordinateur calcule son prochaine coup, l’utilisateur ne peut pasjouer, car la grille ne recoit plus l’evenement clic, bloquee prealablement.
4.3 Optimisations
Nous avons vu dans la partie theorique que l’algorithme utilise pour l’intelligence artificielle etaitrecursif. Afin de minimiser le nombre d’allocations memoires, nous allouons au debut du programmeune quantite de memoire fixe qui est par la suite utilisee en tant que pile. Pour ce faire, nous avonsutilises un syteme d’etats empilables. Un etat contient toutes les informations necessaires au calculd’un coup, et peut etre empile et depile a tout moment. Ceci nous permet de ne stocker en memoireq’une seule branche de l’arbre recursif.
5 Utilisation
5.1 Compilation
Pour compiler notre programme il faut executer la commande make all dans le repertoire conte-nant les fichiers sources. Cela produit un fichier katrof que l’on peut alors lancer par la commande./katrof, ou ./katrof texte pour la version texte.
Remarque Il est possible de ”nettoyer” le dossier racine c’est a dire de supprimer les fichiers tem-poraires et executables crees lors de la compilation en utilisant la commande make clean.
5.2 Liste des fichiers
Voici la liste des fichiers necessaires a la compilation de ce projet. Chaque fichier est accompagned’une courte description et indique si un fichier entte lui est associe.
Fichier .h Description
main.c Fichier principal contenant le point d’entree (fonction main)du programme
main texte.c Fichier principal pour la version texte.interface.c × Gestion de l’interface graphique.grille.c × Gestion des grilles (initilisation, copie, . . . ).intlist.c × Gestion de listes simplement chaınees.ia.c × Gestion de l’intelligence artificielle et de tout ce dont elle
depend.jeu.c × Fonctions basiques de jeu aussi bien utilisees par l’ordinateur
que par le joueur.
Le projet comprend aussi un sous-dossier nomme interface qui contient les differents fichiers XML.
6
5.3 Utilisation du programme
L’interface se reduit a une seule fenetre contenant la grille et un menu qui permet d’acceder atoutes les options et commandes du programme.
Les actions suivantes sont disponibles :– Nouvelle partie Cette action permet de demarrer une nouvelle partie.– Preferences En cliquant sur ce menu, une fenetre s’affiche permettant de choisir quels sont les
joueurs et le niveau de difficulte.– Quitter Permet de quitter le jeu.– Conseil Lorsqu’un le joueur courant est un humain, il a la possibilite de demander a l’ordinateur
de l’aider. Pour cela, il suffit de cliquer sur la commande Conseil.– About Affiche une fenetre d’informations.– Jouer dans une colonne Pour cela un simple clic sur la colonne desiree suffit.
6 Conclusion
Meme si l’utilisation d’un systeme expert aurait rendu notre programme infaillible, les algorithmesutilises pour cette version donnent des resultats parfaitement acceptables.
A Code source
7
Listing 1 – "main.c"
1 # include <stdio.h>
2 # include <gtk/gtk.h>
3 # include <glade/glade.h>
4 # include "ia.h"
5 # include "interface.h"
6
7 int main(int argc , char *argv[])
8 {
9 gtk_init(&argc , & argv);
10 init_main_win();
11 return 0;
12 }
Listing 2 – "main texte.c"
1 # include <stdio.h>
2 # include "grille.h"
3 # include "jeu.h"
4 # include "ia.h"
5
6 #define ETAT_COURANT jeu.pile_etat[jeu.pile_prof]
7
8 int main(int argc , char *argv[])
9 {
10 int joueur = 1;
11 int coup = 0;
12 int ret = 1;
13 int ligne;
14 t_jeu jeu;
15 int tab_joueur[2];
16
17 tab_joueur[0] = 1;
18 tab_joueur[1] = 2;
19
20 jeu_init(&jeu ,7 ,6);
21 /* stats_to_string(ETAT_COURANT.s); */
22
23 fflush(NULL);
24
25 while (ret < 4 && ret > 0)
26 {
27 display_grid(ETAT_COURANT.gr);
28 if(tab_joueur[joueur -1] == 1)
29 {
30 do {
31 printf("Joueur %d : ",joueur );
32 scanf("%d" ,&coup);
33 } while ( ( ret = test_play(ETAT_COURANT.gr ,joueur ,coup ,&ligne ,PLAY))
34 == 0);
35 }
36 else if ( tab_joueur[joueur -1] == 2)
37 {
38 printf("Joueur %d : \n",joueur );
39 ai_test_play(&jeu ,joueur ,&coup ,&ligne);
40 ret = test_play(ETAT_COURANT.gr ,joueur ,coup ,&ligne ,PLAY);
41 }
8
42
43 score_update(&jeu ,joueur,coup ,ligne);
44 /*stats_to_string(ETAT_COURANT.s);*/
45
46 if ( joueur == 1)
47 {
48 joueur = 2;
49 }
50 else
51 {
52 joueur = 1;
53 }
54 }
55
56 pile_etat_libere(jeu.pile_etat);
57 poss_free(jeu.p);
58 jeu_free(&jeu);
59 return 0;
60 }
Listing 3 – "grille.c"
1 /*
2 Gestion de la grille de jeu
3 */
4
5 # include <stdlib.h>
6 # include <stdio.h>
7 # include <string.h>
8 # include "grille.h"
9
10 int grille_init(grille *g,int x,int y)
11 {
12 int ret = 1;
13 if ( ( x > 0 )
14 && ( x < GRILLE_MAX_X)
15 && ( y > 0 )
16 && ( y < GRILLE_MAX_Y ) )
17 {
18 g->tab = calloc(x*y,sizeof(int));
19 if (g->tab != NULL)
20 {
21 g->x = x;
22 g->y = y;
23 }
24 else
25 {
26 ret = 0;
27 }
28 }
29 else
30 {
31 ret = 0;
32 }
33 return ret;
34 }
35
9
36 void grille_free(grille *g)
37 {
38 free(g->tab);
39 g->tab = NULL;
40 g->x = 0;
41 g->y = 0;
42 }
43
44 void grille_cpy(grille g1 , grille g2)
45 {
46 memcpy(g1.tab ,g2.tab ,sizeof(int)*g1.x*g1.y);
47 }
48
49
50 void set_grille(grille g, int x, int y, int val)
51 {
52 g.tab[g.x*y+x] = val;
53 }
54
55 int get_grille(grille g, int x, int y)
56 {
57 return g.tab[g.x*y+x];
58 }
59
60 void display_grid(grille gr)
61 {
62 int i,j;
63 int value;
64 for(i=0; i<gr.y; ++i)
65 {
66 for (j=0;j<gr.x;++j)
67 {
68 value = get_grille(gr ,j,i);
69 if (1 == value)
70 printf("| %s1 \033[0;m ",YELLOW );
71 else if (2 == value)
72 printf("| %s2 \033[0;m ",RED);
73 else printf("| %d " , get_grille(gr ,j,i));
74 }
75 printf("|\n");
76 }
77 for (j=0;j<gr.x;++j)
78 {
79 printf("+---");
80 }
81 printf("+\n");
82 }
Listing 4 – "jeu.c"
1 # include "grille.h"
2 # include "jeu.h"
3
4 /* Renvoi vrai si la case eteste est valide */
5 int is_valid(grille gr,int x, int y)
6 {
7 return (x >= 0) && (x < gr.x) && ( y >= 0) && ( y < gr.y);
10
8 }
9
10 /* Renvoi vrai si la colonne epasse
11 * en eparamtres n’est pas pleine */
12 int is_playable(grille gr,int x)
13 {
14 int ret = 0;
15 if ( (x >= 0) && (x < gr.x) )
16 {
17 ret = !( get_grille(gr ,x ,0));
18 }
19 return ret;
20 }
21
22 /* Teste et joue eventuellement un coup.
23 * Renvoi le nombre de pions ealigns par ce coup */
24 int test_play(grille gr ,int player , int x, int *ligne ,int play)
25 {
26 int i,j;
27 int tab[3][3] ,values [3];
28 int coeff = 0;
29 int ret = 0;
30 int y=0;
31
32 if ( is_playable(gr ,x))
33 {
34 /* Fait chuter le pion en y */
35 while ( ( y < gr.y ) && (!( get_grille(gr ,x,y)))) y++;
36
37 y--;
38
39 /* Test des cases :
40 * Principe : Teste des cases alentour , puis propagation
41 * dans chaque direction ou la couleur est celle du joueur.
42 * */
43
44 for (i=-1;i <2;++i)
45 {
46 for (j=-1;j <2;++j)
47 {
48 if ( i || j )
49 {
50 coeff = 1;
51 while ( is_valid(gr ,x+i*coeff ,y+j*coeff)
52 && ( get_grille(gr ,x+i*coeff ,y+j*coeff)
53 == player ))
54 {
55 coeff ++;
56 }
57 tab[i+1][j+1] = coeff - 1;
58 }
59 }
60 }
61
62 ret = tab[0][0] + tab[2][2];
63 values [0] = tab [1][0] + tab[1][2];
11
64 values [1] = tab [0][1] + tab[2][1];
65 values [2] = tab [2][0] + tab[0][2];
66
67 if (ret < values [0])
68 ret = values [0];
69 if (ret < values [1])
70 ret = values [1];
71 if (ret < values [2])
72 ret = values [2];
73
74 if (play)
75 set_grille(gr,x,y,player );
76 ret++;
77 *ligne = y;
78 }
79 return ret;
80 }
Listing 5 – "ia.c"
1 # include <stdio.h>
2 # include <stdlib.h>
3 # include <string.h>
4
5 # include <time.h>
6
7 # include <assert.h>
8
9 /* Pour INT_MAX */
10 # include <limits.h>
11
12 # include "ia.h"
13 # include "intlist.h"
14 # include "jeu.h"
15
16 void jeu_init(t_jeu *jeu ,int x,int y)
17 {
18 int i,k,j;
19 poss_init(&(jeu ->p),x, y , 4);
20 pile_etat_init(jeu ->pile_etat ,x,y,jeu ->p.z);
21 jeu ->pile_prof = 0;
22
23 jeu ->ordre_chute = ( int *) malloc(sizeof(int)*x);
24 /* Il est toujours preferable de jouer au milieu,
25 ce sont donc les coups du milieu qui seront teste en premiers. */
26 i=x/2;
27 k=1;
28 for(j=0;j<x;j++)
29 {
30 i+=j*k;
31 k = -k;
32 jeu ->ordre_chute[j] = i;
33 }
34 srand(time(NULL));
35 }
36
37 void jeu_free(t_jeu *jeu)
12
38 {
39 free(jeu ->ordre_chute);
40 }
41
42 void pile_etat_init(etat *pile ,int x,int y,int nb_stat)
43 {
44 int i;
45 for(i=0;i<RECURSION_MAX;++i)
46 {
47 grille_init(&pile[i].gr ,x,y);
48 stats_init(&pile[i].s,nb_stat);
49 pile[i].score [0] = pile[i].score [1] = 0;
50 pile[i]. nb_coups = 0;
51 }
52 }
53
54 void pile_etat_sauve(etat *pile , int *pile_prof ,int line)
55 {
56
57 grille_cpy(pile[(*pile_prof) + 1].gr,pile[* pile_prof].gr);
58 stats_cpy(pile[(*pile_prof) + 1].s,pile[*pile_prof].s);
59 pile[(*pile_prof) + 1]. score [0] = pile[* pile_prof].score [0];
60 pile[(*pile_prof) + 1]. score [1] = pile[* pile_prof].score [1];
61 pile[(*pile_prof) + 1]. nb_coups = pile[* pile_prof].nb_coups;
62 (* pile_prof)++;
63 }
64
65 void pile_etat_restaure(int * pile_prof)
66 {
67 /* fprintf(stderr ," Restauration : %d\n",* pile_prof); */
68 assert ((* pile_prof) > 0);
69 (* pile_prof)--;
70 }
71
72 void pile_etat_libere(etat *pile)
73 {
74 int i;
75 for(i=0;i<RECURSION_MAX;++i)
76 {
77 grille_free(&pile[i].gr);
78 stats_free(pile[i].s);
79 }
80 }
81
82 void stats_init(stats *s,int nb_stat)
83 {
84 int i;
85 s->j[0] = malloc(nb_stat*(sizeof (int)));
86 s->j[1] = malloc(nb_stat*(sizeof (int)));
87 /* Memset difficile puisquil agit sur des octets , et non sur des entiers */
88 for(i=0;i<nb_stat;++i)
89 {
90 s->j[0][i] = 1;
91 s->j[1][i] = 1;
92 }
93 s->size = nb_stat;
13
94 }
95
96 void stats_cpy(stats s1 , stats s2)
97 {
98 memcpy(s1.j[0],s2.j[0],sizeof(int)*s2.size);
99 memcpy(s1.j[1],s2.j[1],sizeof(int)*s2.size);
100 }
101
102 void stats_free(stats s)
103 {
104 free(s.j[0]);
105 free(s.j[1]);
106 }
107
108 void stats_set(stats s,poss p,int x, int y, int jr)
109 {
110 Liste lst;
111 int value = -1;
112 int aj;
113 int * read;
114
115 jr = jr -1;
116 aj = ( jr == 1)?0:1;
117
118 lst = poss_read(p,x,y);
119 while ( ( read = ( int *) lst_suivant(&lst)) )
120 {
121 value = * read;
122 s.j[jr][value ] *= 2;
123 s.j[aj][value ] = 0;
124 }
125 /*if ( value != -1)
126 {
127 s.j[jr][value ] *= 2;
128 s.j[aj][value ] = 0;
129 }*/
130 }
131
132 void stats_to_string(stats s)
133 {
134 int i;
135 for(i=0;i<s.size;++i)
136 {
137 printf("%d - >\033[1;33m%d\033[0;m|",i,s.j[0][i]);
138 }
139 printf("\n");
140 for(i=0;i<s.size;++i)
141 {
142 printf("%d - >\033[1;33m%d\033[0;m||",i,s.j[1][i]);
143 }
144 printf("\n");
145 }
146
147 void poss_init(poss *p,int x, int y, int n)
148 {
149 int i,j,k,count =0;
14
150
151 p->x = x;
152 p->y = y;
153 p->z = 4*x*y - 3*x*n - 3*y*n + 3*x + 3*y - 4*n + 2*n*n + 2;
154 p->p = ( Liste *) calloc(x*y, sizeof(Liste ));
155 for (i=0;i<y*x;++i)
156 lst_init(p->p+i,NULL ,sizeof(int));
157
158 /* printf ("Horizontal : %d",count ); */
159 /* Possibilitees en lignes */
160 for (i=0;i<y;++i)
161 for (j=0;j<=x-n;++j)
162 {
163 for (k=0;k<n;++k)
164 lst_insere(p->p+x*i+j+k,&count);
165 count++;
166 }
167
168 /* printf ("-%d\ nVertical : %d",count -1,count ); */
169 /* Possibilitees en colonnes */
170 for (i=0;i<x;++i)
171 for (j=0;j<=y-n;++j)
172 {
173 for (k=0;k<n;++k)
174 lst_insere(p->p+x*(j+k)+i,&count);
175 count++;
176 }
177
178 /* printf ("-%d\ nAntislash : %d",count -1,count ); */
179 /* Possibilitees en antislash */
180 for (i=0;i<=x-n;++i)
181 for (j=0;j<=y-n ;++j)
182 {
183 for (k=0;k<n;++k)
184 lst_insere(p->p+x*(j+k)+i+k,&count);
185 count++;
186 }
187
188 /* printf ("-%d\nSlash : %d",count -1,count ); */
189 /* Possibilitees en slash */
190 for (i=0;i<=x-n;++i)
191 for (j=y-n+1;j<y ;++j)
192 {
193 for (k=0;k<n;++k)
194 lst_insere(p->p+x*(j-k)+i+k,&count);
195 count++;
196 }
197 /* printf ("-%d\n",count -1); */
198 }
199
200 Liste poss_read(poss p,int x,int y)
201 {
202 return *(p.p+p.x*y+x);
203 }
204
205 void poss_free(poss p)
15
206 {
207 int i;
208 for (i=0;i<p.x*p.y;++i)
209 lst_libere(p.p+i);
210 free(p.p);
211 }
212
213 int autre(int joueur)
214 {
215 return ((joueur == 1)?2:1);
216 }
217
218 #define x jeu ->pile_etat[0].gr.x
219 #define y jeu ->pile_etat[0].gr.y
220 #define ETAT_COURANT jeu ->pile_etat[jeu ->pile_prof]
221
222 void score_update(t_jeu *jeu ,int joueur ,int col ,int ln)
223 {
224 int autre_joueur = autre(joueur );
225 int *read ,value;
226 Liste lst;
227
228 lst = poss_read(jeu ->p,col ,ln);
229 while ( ( read = ( int *) lst_suivant(&lst)) )
230 {
231 value = * read;
232
233 ETAT_COURANT.score[joueur -1] += ETAT_COURANT.s.j[joueur -1][ value];
234 ETAT_COURANT.score[autre_joueur -1] -=
235 ETAT_COURANT.s.j[autre_joueur -1][ value];
236 }
237
238 stats_set(ETAT_COURANT.s,jeu ->p,col , ln , joueur );
239 }
240
241
242 void ai_test_play(t_jeu *jeu ,int joueur ,int *col ,int *ln)
243 {
244 int i;
245 int colonne ,meilleure_colonne=-INT_MAX;
246 int score , meilleure_score=-INT_MAX;
247 int colonne_defaut = -1;
248 int valeur;
249
250 for (i=0;i<x;++i)
251 {
252 /* fprintf(stderr ,"TEST@ prof %d\n",jeu.pile_prof); */
253 pile_etat_sauve(jeu ->pile_etat ,&jeu ->pile_prof ,__LINE__);
254 colonne = jeu ->ordre_chute[i];
255
256 if (( valeur = test_play(ETAT_COURANT.gr ,joueur ,colonne ,ln,PLAY )) != 0)
257 {
258 colonne_defaut = colonne;
259 score_update(jeu ,joueur ,colonne ,*ln);
260
261 if ( valeur == 4)
16
262 {
263 meilleure_colonne = colonne;
264 pile_etat_restaure(&jeu ->pile_prof);
265 break;
266 }
267
268 score = - ai_evaluation_grille(jeu ,autre(joueur),-INT_MAX ,INT_MAX);
269
270 if ( ( score > meilleure_score) /* || */
271 /* ( ( score == meilleure_score) && ( rand () & 1) ) */)
272 {
273 meilleure_score = score;
274 meilleure_colonne = colonne;
275 }
276 }
277 pile_etat_restaure(&jeu ->pile_prof);
278 }
279 /* printf ("Colonne a jouer : %d\n",meilleure_colonne); */
280 if ( meilleure_score == - INT_MAX)
281 *col = colonne_defaut;
282 else
283 *col = meilleure_colonne;
284 }
285
286
287
288 /* Cette fonction evalue l’etat du jeu actuel et renvoi le pire
289 score qu’il est possible d’y obtenir */
290 int ai_evaluation_grille(t_jeu *jeu ,int joueur ,int alpha ,int beta)
291 {
292 int i;
293 int score ,meilleur_score=-INT_MAX;
294 int colonne ,valeur;
295 int ln;
296
297 /* Si on a atteint une feuille , on renvoie sa valeure */
298 if ((jeu ->pile_prof + 1) >= jeu ->recursion)
299 {
300 /* printf ("Score %d - %d\n", ETAT_COURANT.score[joueur -1],
301 ETAT_COURANT.score[autre(joueur ) -1]); */
302 return ETAT_COURANT.score[joueur -1] -
303 ETAT_COURANT.score[autre(joueur )-1];
304 }
305
306 /* Sinon on recherche la valeure maximale renvoyee par recusion
307 sur chaque noeuds successeurs */
308 for (i=0;i<x;++i)
309 {
310 pile_etat_sauve(jeu ->pile_etat ,&jeu ->pile_prof ,__LINE__);
311 colonne = jeu ->ordre_chute[i];
312 /* Si le coup est jouable */
313 if (( valeur = test_play(ETAT_COURANT.gr ,joueur ,colonne ,&ln ,PLAY )) != 0)
314 {
315 score_update(jeu ,joueur ,colonne ,ln);
316
317 if ( valeur == 4)
17
318 {
319 pile_etat_restaure(&jeu ->pile_prof);
320 meilleur_score = INT_MAX - jeu ->pile_prof;
321 break;
322 }
323
324 score = - ai_evaluation_grille(jeu ,autre(joueur),-beta ,-alpha);
325
326 if ( score > meilleur_score)
327 meilleur_score = score;
328 if ( score > alpha)
329 alpha = meilleur_score;
330 if ( alpha >= beta)
331 {
332 pile_etat_restaure(&jeu ->pile_prof);
333 break;
334 }
335 }
336 pile_etat_restaure(&jeu ->pile_prof);
337 }
338 return meilleur_score;
339 }
340
341 #undef x
342 #undef y
343 #undef ETAT_COURANT
Listing 6 – "interface.c"
1 # include <stdlib.h>
2 # include <stdio.h>
3
4 # include <gtk/gtk.h>
5 # include <glade/glade.h>
6
7 # include "ia.h"
8 # include "grille.h"
9 # include "jeu.h"
10 # include "interface.h"
11
12 #define ETAT_COURANT Global.jeu.pile_etat[Global.jeu.pile_prof]
13
14 p4 Global;
15
16 void quitter()
17 {
18 gtk_main_quit();
19 pile_etat_libere(Global.jeu.pile_etat);
20 poss_free(Global.jeu.p);
21 jeu_free(&(Global.jeu));
22 }
23
24 void on_new1_activate(GtkMenuItem *menuitem ,
25 gpointer user_data)
26 {
27 Global.joueur = 1;
28 Global.last_score = 0;
18
29 Global.coup = 0;
30 if ( Global.game_started == 1)
31 jeu_free(&(Global.jeu));
32 Global.game_started = 1;
33 jeu_init(&(Global.jeu),7,6);
34
35 /* Emission du signal switch sur Global.ordonanceur */
36 g_signal_emit ( Global.ordonanceur , Global.signal_id_switch ,
37 0 /* details */,
38 NULL);
39 }
40
41 void on_quit1_activate(GtkMenuItem *menuitem ,
42 gpointer user_data)
43 {
44 quitter();
45 }
46 void on_about1_activate(GtkMenuItem * menuitem ,
47 gpointer user_data) {
48 Global.win_dialog = glade_xml_new(FILENAME , " about_win" , NULL);
49 glade_xml_signal_autoconnect (Global.win_dialog);
50 }
51
52 void on_preferences1_activate(GtkMenuItem *menuitem ,
53 gpointer user_data) {
54 Global.win_dialog = glade_xml_new(FILENAME , " pref_win" , NULL);
55 glade_xml_signal_autoconnect (Global.win_dialog);
56 gtk_range_set_value (( GtkRange *) glade_xml_get_widget(Global.win_dialog ,
57 "slide_difficult"),Global.jeu.recursion);
58 if ( Global.players[1] == 2)
59 {
60 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
61 Global.win_dialog ,"rdo_h1"),FALSE);
62 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
63 Global.win_dialog ,"rdo_pc1"),TRUE);
64 }
65 else
66 {
67 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
68 Global.win_dialog ,"rdo_pc1"),FALSE);
69 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
70 Global.win_dialog ,"rdo_h1"),TRUE);
71 }
72 if ( Global.players[0] == 2)
73 {
74 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
75 Global.win_dialog ,"rdo_h2"),FALSE);
76 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
77 Global.win_dialog ,"rdo_pc2"),TRUE);
78 }
79 else
80 {
81 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
82 Global.win_dialog ,"rdo_pc2"),FALSE);
83 gtk_toggle_button_set_active (( GtkToggleButton *) glade_xml_get_widget(
84 Global.win_dialog ,"rdo_h2"),TRUE);
19
85 }
86 }
87
88 void on_pref_win_ok(GtkWidget * pWidget , gpointer pData)
89 {
90 if ( gtk_toggle_button_get_active (( GtkToggleButton *)
91 glade_xml_get_widget(Global.win_dialog ,"rdo_pc1")))
92 Global.players[1] = 2;
93 else
94 Global.players[1] = 1;
95 if ( gtk_toggle_button_get_active (( GtkToggleButton *)
96 glade_xml_get_widget(Global.win_dialog ,"rdo_pc2")))
97 Global.players[0] = 2;
98 else
99 Global.players[0] = 1;
100 Global.game_started = 0;
101 jeu_free(&(Global.jeu));
102 gtk_widget_queue_draw ( glade_xml_get_widget(Global.xml ,"dg"));
103 Global.jeu.recursion = gtk_range_get_value (( GtkRange *)
104 glade_xml_get_widget(Global.win_dialog ,"slide_difficult"));
105
106 affiche_etat("Cliquez sur Nouveau pour commencer.");
107 gtk_widget_destroy(glade_xml_get_widget(Global.win_dialog ,"pref_win"));
108 }
109
110 void on_pref_win_show ( GtkWidget * pWidget , gpointer pData ) {}
111 void on_pref_win_destroy_event(GtkWidget *pWidget , gpointer pData ) {}
112
113 void on_about_win_close(GtkWidget *pWidget , gpointer pData)
114 {
115 gtk_widget_destroy(glade_xml_get_widget(Global.win_dialog ,"about_win"));
116 }
117
118 void on_about_win_destroy_event(GtkWidget *pWidget , gpointer pData ) {}
119
120 void on_conseil1_activate(GtkMenuItem *menuitem ,
121 gpointer user_data) { }
122
123 void on_main_win_destroy(GtkWidget *pWidget , gpointer pData)
124 {
125 quitter();
126 }
127
128 void on_dg_button_press_event(GtkWidget *widget ,
129 GdkEventButton *event ,
130 gpointer pData)
131 {
132 int ln;
133 if (( Global.last_score = test_play(ETAT_COURANT.gr ,
134 Global.joueur ,
135 ((int) event ->x/50),
136 &ln ,
137 PLAY )) != 0)
138 {
139 g_signal_handler_block(glade_xml_get_widget(Global.xml ,"dg"),
140 Global.signal_handler_human);
20
141 Global.coup +=1;
142 g_signal_emit ( Global.ordonanceur , Global.signal_id_switch ,
143 0 /* details */,
144 NULL);
145 }
146 }
147
148 void on_switch_player(GObject * gobject ,
149 GParamSpec *arg1 ,
150 gpointer user_data)
151 {
152 char string [255];
153
154 /* printf ("Coup :%d\n",Global.coup); */
155
156 if ( Global.game_started == 0)
157 return;
158
159 gtk_widget_queue_draw ( glade_xml_get_widget(Global.xml ,"dg"));
160 while ( gtk_events_pending ())
161 gtk_main_iteration ();
162
163 if ( Global.last_score > 3)
164 {
165 sprintf(string,"Le joueur %d a gagne",Global.joueur );
166 affiche_etat(string );
167 Global.game_started = 0;
168 }
169 else if ( Global.coup == ( ETAT_COURANT.gr.x*ETAT_COURANT.gr.y))
170 {
171 display_grid(ETAT_COURANT.gr);
172 affiche_etat("Partie nulle");
173 }
174 else
175 {
176 if ( Global.joueur == 1)
177 {
178 Global.joueur = 2;
179 affiche_etat("Joueur rouge , a toi de jouer");
180 }
181 else
182 {
183 Global.joueur = 1;
184 affiche_etat("Joueur jaune , a toi de jouer");
185 }
186 if ( Global.players[Global.joueur -1] == 2)
187 {
188 g_signal_emit ( Global.ordonanceur , Global.signal_id_IA ,
189 0 /* details */,
190 NULL);
191 }
192 else
193 {
194 g_signal_handler_unblock(glade_xml_get_widget(Global.xml ,"dg"),
195 Global.signal_handler_human);
196 }
21
197 }
198 }
199
200 void on_se_faire_conseiller1_activate (GtkMenuItem *menuitem ,
201 gpointer user_data) {
202 char buff[100];
203 int coup ,ligne;
204
205 if (( Global.players[Global.joueur -1] == 2) ||
206 (Global.game_started == 0))
207 return;
208 affiche_etat("L’ordinateur va vous conseiller...");
209 while ( gtk_events_pending ())
210 gtk_main_iteration ();
211 ai_test_play(&(Global.jeu),Global.joueur ,&coup ,&ligne);
212 sprintf(buff ,"Essayez de jouer en colonne %d",coup + 1);
213 affiche_etat(buff);
214 }
215
216 void on_IA_play(GObject *gobject ,
217 GParamSpec *arg1 ,
218 gpointer user_data)
219 {
220 int coup ,ligne;
221 affiche_etat("L’ordinateur calcule une solution...");
222 while ( gtk_events_pending ())
223 gtk_main_iteration ();
224
225 ai_test_play(&(Global.jeu),Global.joueur ,&coup ,&ligne);
226 /* printf ("Coup :%d ligne :%d ",coup ,ligne ); */
227 Global.last_score = test_play(ETAT_COURANT.gr ,
228 Global.joueur,
229 coup ,
230 &ligne ,
231 PLAY);
232 /* printf ("PC : %d ",Global. last_score); */
233 Global.coup +=1;
234 g_signal_emit ( Global.ordonanceur , Global.signal_id_switch ,
235 0 /* details */,
236 NULL);
237
238 }
239
240 void affiche_etat(char *str)
241 {
242 gtk_statusbar_push ( Global.etat ,gtk_statusbar_get_context_id (
243 Global.etat ,"etat"),str);
244 }
245
246 void on_dg_expose_event(GtkWidget *pWidget ,
247 GdkEventExpose *event ,
248 gpointer pData)
249 {
250 int i,j;
251 int value;
252 if (! Global.game_started)
22
253 return;
254
255 for(i=0; i<ETAT_COURANT.gr.y; ++i)
256 {
257 for (j=0;j<ETAT_COURANT.gr.x;++j)
258 {
259 value = get_grille(ETAT_COURANT.gr ,j,i);
260 if (1 == value)
261 {
262 gdk_pixbuf_render_to_drawable (Global.skin ,
263 pWidget ->window ,
264 pWidget ->style ->fg_gc[ GTK_STATE_NORMAL],
265 0 , 200 , 50*j , 50*i, 50 , 50 ,
266 GDK_RGB_DITHER_NORMAL ,
267 0 , 0);
268 }
269 else if (2 == value)
270 {
271 gdk_pixbuf_render_to_drawable (Global.skin ,
272 pWidget ->window ,
273 pWidget ->style ->fg_gc[ GTK_STATE_NORMAL],
274 0 , 250 , 50*j , 50*i, 50 , 50 ,
275 GDK_RGB_DITHER_NORMAL ,
276 0 , 0);
277 }
278 else
279 {
280 gdk_pixbuf_render_to_drawable (Global.skin ,
281 pWidget ->window ,
282 pWidget ->style ->fg_gc[ GTK_STATE_NORMAL],
283 0 , 150 , 50*j , 50*i, 50 , 50 ,
284 GDK_RGB_DITHER_NORMAL ,
285 0 , 0);
286 }
287 }
288 }
289 }
290
291 int init_main_win()
292 {
293 /* try to load the interface and verify it happened */
294 Global.xml = glade_xml_new(FILENAME , " main_win",NULL);
295 if (! Global.xml) {
296 g_warning("something bad happened while creating the interface");
297 return 1;
298 }
299
300 /* Initialise la structure de jeu aux valeures eegnrales */
301 Global.skin = gdk_pixbuf_new_from_file(
302 "./ default.png",
303 NULL);
304 Global.etat = ( GtkStatusbar *) glade_xml_get_widget ( Global.xml ,"etat");
305 Global.game_started = 0;
306 Global.players[0] = 1;
307 Global.players[1] = 2;
308 Global.jeu.recursion = 5;
23
309 affiche_etat("Cliquez sur Nouveau pour commencer.");
310
311 /* Connexion aux signaux */
312 glade_xml_signal_autoconnect (Global.xml);
313
314 /* La variable Global. ordonanceur est l’objet par lequel
315 * va passer notre signal
316 * Elle est du type GObject , c’est un objet qui n’a pas de ereprsentation
317 * graphique , primitive de base des autres objets */
318 Global.ordonanceur = g_object_new( g_type_from_name("GObject"),NULL);
319
320 /* On ecre un nouveau signal qui se rapporte aux GObject.
321 * On nomme ce signal switch , c’est lui qui nous permettra de
322 * passer d’un joueur a un autre dans l’application */
323 Global.signal_id_switch = g_signal_newv ("switch",
324 g_type_from_name("GObject"),
325 G_SIGNAL_RUN_LAST |
326 G_SIGNAL_NO_RECURSE |
327 G_SIGNAL_NO_HOOKS ,
328 NULL /* class closure */,
329 NULL /* accumulator */,
330 NULL /* accu_data */,
331 g_cclosure_marshal_VOID__VOID ,
332 G_TYPE_NONE /* return_type */,
333 0 /* n_params */,
334 NULL /* param_types */);
335 Global.signal_id_IA = g_signal_newv ("IA",
336 g_type_from_name(" GObject"),
337 G_SIGNAL_RUN_LAST |
338 G_SIGNAL_NO_RECURSE |
339 G_SIGNAL_NO_HOOKS ,
340 NULL /* class closure */,
341 NULL /* accumulator */,
342 NULL /* accu_data */,
343 g_cclosure_marshal_VOID__VOID ,
344 G_TYPE_NONE /* return_type */,
345 0 /* n_params */,
346 NULL /* param_types */);
347
348 /* On connecte le nouveau signal a notre instance de GObject
349 * et on defini la fonction de callback. */
350 g_signal_connect(Global.ordonanceur ,"switch" , G_CALLBACK( on_switch_player)
351 , NULL);
352 g_signal_connect(Global.ordonanceur ,"IA" , G_CALLBACK(on_IA_play), NULL);
353 Global.signal_handler_human = g_signal_handler_find(
354 glade_xml_get_widget(Global.xml ,"dg"),
355 G_SIGNAL_MATCH_ID ,
356 g_signal_lookup("button_press_event",
357 g_type_from_name("GtkDrawingArea")),
358 0,NULL ,NULL ,0);
359 g_signal_handler_block(glade_xml_get_widget(Global.xml ,"dg"),
360 Global.signal_handler_human);
361
362 gtk_main();
363
364 return 0;
24
365 }
Listing 7 – "intlist.c"
1 # include <stdlib.h>
2 # include <stdio.h>
3 # include <string.h>
4 # include "intlist.h"
5
6 void lst_init(Liste *lst , void (* displayer)(Objet *), int size_of_objet)
7 {
8 lst ->premier = NULL;
9 lst ->dernier = NULL;
10 lst ->courant = NULL;
11 lst ->nb_elem = 0;
12 lst ->afficheur = displayer;
13 lst ->size_obj = size_of_objet;
14 }
15
16
17 void lst_insere(Liste *lst , Objet *o)
18 {
19 Element * nouveau;
20 Objet *obj;
21
22 nouveau = ( Element *) malloc(sizeof(Element));
23 obj = ( Objet *) malloc(lst ->size_obj);
24 memcpy(obj ,o,lst ->size_obj);
25
26 nouveau ->obj = obj;
27 nouveau ->suivant = NULL;
28 if (lst ->dernier == NULL)
29 {
30 lst ->premier = nouveau;
31 lst ->courant = nouveau;
32 }
33 else
34 lst ->dernier ->suivant = nouveau;
35 lst ->dernier = nouveau;
36 lst ->nb_elem++;
37 }
38
39 void lst_libere(Liste *lst)
40 {
41 Element * parcours;
42 Element * prec;
43 parcours = lst ->premier;
44
45 while(NULL != parcours)
46 {
47 prec = parcours;
48 parcours = parcours ->suivant;
49 free(prec ->obj);
50 free(prec);
51 }
52 lst ->premier = NULL;
53 lst ->courant = NULL;
25
54 lst ->dernier = NULL;
55 lst ->nb_elem = 0;
56 }
57
58 void lst_supprime(Liste *lst , Objet *o)
59 {
60 Element * parcours;
61 Element * prec=NULL;
62 parcours = lst ->premier;
63 while ((NULL != parcours) && (! lst_compare(*lst ,parcours ->obj ,o)))
64 {
65 prec = parcours;
66 /*printf ("%d\n",*((int *)parcours ->obj));*/
67 parcours = parcours ->suivant;
68 }
69
70 if (NULL != parcours)
71 {
72 if ( parcours == lst ->dernier)
73 lst ->dernier = prec;
74
75 if ( parcours == lst ->premier)
76 {
77 lst ->premier = parcours ->suivant;
78 }
79 else
80 prec ->suivant = parcours ->suivant;
81
82 if ( parcours == lst ->courant)
83 lst ->courant = lst ->premier;
84
85 free(parcours ->obj);
86 free(parcours);
87 lst ->nb_elem --;
88 }
89 }
90
91 int lst_vide(Liste lst)
92 {
93 return (lst.nb_elem == 0);
94 }
95
96 int lst_nbelem(Liste lst)
97 {
98 return (lst.nb_elem);
99 }
100
101 Objet * lst_suivant(Liste *lst)
102 {
103 Element *tmp;
104 if (lst ->courant == NULL)
105 {
106 lst_reset(lst);
107 return NULL;
108 }
109 else
26
110 {
111 tmp = lst ->courant ->obj;
112 lst ->courant = lst ->courant ->suivant;
113 return tmp;
114 }
115
116 }
117
118 void lst_reset(Liste *lst)
119 {
120 lst ->courant = lst ->premier;
121 }
122
123 void lst_toString(Liste lst)
124 {
125 Element * parcours;
126 parcours = lst.premier;
127 while(NULL != parcours)
128 {
129 lst.afficheur(parcours ->obj);
130 parcours = parcours ->suivant;
131 printf("\n");
132 }
133 }
134
135 int lst_compare(Liste lst ,Objet *o1,Objet *o2)
136 {
137 int i;
138 for(i=0;i<lst.size_obj;++i)
139 if ((( char *)o1)[i] != (( char *)o2)[i])
140 return 0;
141 return 1;
142 }
27