Langage C Projet : Puissance 4olivier.coupelon.free.fr/files/katrof-dossier.pdf · Langage C Projet...

27
Langage C Projet : Puissance 4 Florent Brunet Olivier Coupelon Licence Informatique, 3` eme Ann´ ee, Groupe 2 14 d´ ecembre 2004 Table des mati` eres 1 Introduction 2 2 Th´ eorie 2 2.1 egamax ........................................... 2 2.2 Algorithme α - β ....................................... 2 2.3 Syst` emes experts ....................................... 2 3 Impl´ ementation 2 3.1 Structures de donn´ ees .................................... 2 3.2 Intelligence artificielle .................................... 3 3.2.1 ´ Evaluation d’un coup ................................ 3 3.2.2 Impl´ ementation de l’algorithme n´ egamax avec α - β ............... 3 3.3 Graphe des appels de fonctions ............................... 5 4 etails techniques 5 4.1 Glade & Libglade ....................................... 5 4.2 eroulement ´ ev` enementiel du jeu .............................. 6 4.3 Optimisations ......................................... 6 5 Utilisation 6 5.1 Compilation .......................................... 6 5.2 Liste des fichiers ....................................... 6 5.3 Utilisation du programme .................................. 7 6 Conclusion 7 A Code source 7 1

Transcript of Langage C Projet : Puissance 4olivier.coupelon.free.fr/files/katrof-dossier.pdf · Langage C Projet...

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