Browse Source

TP2 version fonctionnelle

Nabzzz 3 years ago
parent
commit
7e31c799b2
1 changed files with 428 additions and 0 deletions
  1. 428
    0
      TP2.pl

+ 428
- 0
TP2.pl View File

@@ -0,0 +1,428 @@
1
+/*********************************
2
+	DESCRIPTION DU JEU DU TIC-TAC-TOE
3
+	*********************************/
4
+
5
+	/*
6
+	Une situation est decrite par une matrice 3x3.
7
+	Chaque case est soit un emplacement libre (Variable LIBRE), soit contient le symbole d'un des 2 joueurs (o ou x)
8
+
9
+	Contrairement a la convention du tp precedent, pour modeliser une case libre
10
+	dans une matrice on n'utilise pas une constante speciale (ex : nil, 'vide', 'libre','inoccupee' ...);
11
+	On utilise plut�t un identificateur de variable, qui n'est pas unifiee (ex : X, A, ... ou _) .
12
+	La situation initiale est une "matrice" 3x3 (liste de 3 listes de 3 termes chacune)
13
+	o� chaque terme est une variable libre.	
14
+	Chaque coup d'un des 2 joureurs consiste a donner une valeur (symbole x ou o) a une case libre de la grille
15
+	et non a deplacer des symboles deja presents sur la grille.		
16
+	
17
+	Pour placer un symbole dans une grille S1, il suffit d'unifier une des variables encore libres de la matrice S1,
18
+	soit en ecrivant directement Case=o ou Case=x, ou bien en accedant a cette case avec les predicats member, nth1, ...
19
+	La grille S1 a change d'etat, mais on n'a pas besoin de 2 arguments representant la grille avant et apres le coup,
20
+	un seul suffit.
21
+	Ainsi si on joue un coup en S, S perd une variable libre, mais peut continuer a s'appeler S (on n'a pas besoin de la designer
22
+	par un nouvel identificateur).
23
+	*/
24
+%:- use_module(library(clpfd)).
25
+
26
+situation_initiale([ [_,_,_],
27
+                     [_,_,_],
28
+                     [_,_,_]]).
29
+
30
+	% Convention (arbitraire) : c'est x qui commence
31
+
32
+joueur_initial(x).
33
+
34
+cpu_time(Goal, Elapsed_Time) :-
35
+    statistics(process_cputime,Start),
36
+    call(Goal),
37
+    statistics(process_cputime,Finish),
38
+    Elapsed_Time is (Finish-Start)*1000.
39
+
40
+	% Definition de la relation adversaire/2
41
+
42
+adversaire(x,o).
43
+adversaire(o,x).
44
+
45
+
46
+	/****************************************************
47
+	 DEFINIR ICI a l'aide du predicat ground/1 comment
48
+	 reconnaitre une situation terminale dans laquelle il
49
+	 n'y a aucun emplacement libre : aucun joueur ne peut
50
+	 continuer a jouer (quel qu'il soit).
51
+	 ****************************************************/
52
+
53
+situation_terminale(_Joueur, Situation) :-  ground(Situation).
54
+
55
+	/***************************
56
+	DEFINITIONS D'UN ALIGNEMENT
57
+	***************************/
58
+
59
+alignement(L, Matrix) :- ligne(    L,Matrix).
60
+alignement(C, Matrix) :- colonne(  C,Matrix).
61
+alignement(D, Matrix) :- diagonale(D,Matrix).
62
+
63
+	/********************************************
64
+	 DEFINIR ICI chaque type d'alignement maximal 
65
+ 	 existant dans une matrice carree NxN.
66
+	 ********************************************/
67
+	
68
+ligne(L, M) :- member(L,M).
69
+
70
+colonne_aux(_,[],[]).
71
+
72
+colonne_aux(N,[X|Cr],[L|Mr]):-
73
+    nth1(N,L,X),
74
+    colonne_aux(N,Cr,Mr).
75
+
76
+colonne(C,M) :-
77
+    colonne_aux(_,C,M).
78
+
79
+%colonne(C,M) :- transpose(M,T), member(C,T).
80
+
81
+	/* Definition de la relation liant une diagonale D a la matrice M dans laquelle elle se trouve.
82
+		il y en a 2 sortes de diagonales dans une matrice carree(https://fr.wikipedia.org/wiki/Diagonale) :
83
+		- la premiere diagonale (principale)  : (A I)
84
+		- la seconde diagonale                : (Z R)
85
+		A . . . . . . . Z
86
+		. \ . . . . . / .
87
+		. . \ . . . / . .
88
+		. . . \ . / . . .
89
+		. . . . X . . .
90
+		. . . / . \ . . . 
91
+		. . / . . . \ . .
92
+		. / . . . . . \ .
93
+		R . . . . . . . I
94
+	*/
95
+		
96
+diagonale(D, M) :- 
97
+    premiere_diag(1,D,M).
98
+
99
+% deuxieme definition A COMPLETER
100
+
101
+diagonale(D, M) :- seconde_diag(3,D,M).
102
+
103
+    
104
+premiere_diag(_,[],[]).
105
+premiere_diag(K,[E|D],[Ligne|M]) :-
106
+    nth1(K,Ligne,E),
107
+    K1 is K+1,
108
+    premiere_diag(K1,D,M).
109
+
110
+seconde_diag(_,[],[]).
111
+seconde_diag(K,[E|D],[Ligne|M]) :- 
112
+    nth1(K,Ligne,E),
113
+    K1 is K-1,
114
+    seconde_diag(K1,D,M).
115
+
116
+
117
+
118
+	/*****************************
119
+	 DEFINITION D'UN ALIGNEMENT 
120
+	 POSSIBLE POUR UN JOUEUR DONNE
121
+	 *****************************/
122
+
123
+possible([X|L], J) :- unifiable(X,J), possible(L,J).
124
+possible(  [],  _).
125
+
126
+	/* Attention 
127
+	il faut juste verifier le caractere unifiable
128
+	de chaque emplacement de la liste, mais il ne
129
+	faut pas realiser l'unification.
130
+	*/
131
+
132
+% A FAIRE 
133
+unifiable(X,J) :- ( var(X) ->  true ; J=X) .
134
+	
135
+	/**********************************
136
+	 DEFINITION D'UN ALIGNEMENT GAGNANT
137
+	 OU PERDANT POUR UN JOUEUR DONNE J
138
+	 **********************************/
139
+	/*
140
+	Un alignement gagnant pour J est un alignement
141
+possible pour J qui n'a aucun element encore libre.
142
+	*/
143
+	
144
+	/*
145
+	Remarque : le predicat ground(X) permet de verifier qu'un terme
146
+	prolog quelconque ne contient aucune partie variable (libre).
147
+	exemples :
148
+		?- ground(Var).
149
+		no
150
+		?- ground([1,2]).
151
+		yes
152
+		?- ground(toto(nil)).
153
+		yes
154
+		?- ground( [1, toto(nil), foo(a,B,c)] ).
155
+		no
156
+	*/
157
+		
158
+	/* Un alignement perdant pour J est un alignement gagnant pour son adversaire. */
159
+
160
+% A FAIRE
161
+
162
+alignement_gagnant(Ali, J) :- possible(Ali,J), ground(Ali).
163
+
164
+alignement_perdant(Ali, J) :- adversaire(J,A), alignement_gagnant(Ali, A). 
165
+
166
+
167
+	/* ****************************
168
+	DEFINITION D'UN ETAT SUCCESSEUR
169
+	****************************** */
170
+
171
+	/* 
172
+	Il faut definir quelle operation subit la matrice
173
+	M representant l'Etat courant
174
+	lorsqu'un joueur J joue en coordonnees [L,C]
175
+	*/	
176
+
177
+% A FAIRE
178
+successeur(J, Etat,[L,C]) :- nth1(L,Etat,Lig), nth1(C,Lig,X), var(X), X=J.
179
+
180
+	/**************************************
181
+   	 EVALUATION HEURISTIQUE D'UNE SITUATION
182
+  	 **************************************/
183
+
184
+	/*
185
+	1/ l'heuristique est +infini si la situation J est gagnante pour J
186
+	2/ l'heuristique est -infini si la situation J est perdante pour J
187
+	3/ sinon, on fait la difference entre :
188
+	   le nombre d'alignements possibles pour J
189
+	moins
190
+ 	   le nombre d'alignements possibles pour l'adversaire de J
191
+*/
192
+
193
+
194
+heuristique(J,Situation,H) :-		% cas 1
195
+   H = 10000,				% grand nombre approximant +infini
196
+   alignement(Alig,Situation),
197
+   alignement_gagnant(Alig,J), !.
198
+	
199
+heuristique(J,Situation,H) :-		% cas 2
200
+   H = -10000,				% grand nombre approximant -infini
201
+   alignement(Alig,Situation),
202
+   alignement_perdant(Alig,J), !.	
203
+
204
+
205
+% on ne vient ici que si les cut precedents n'ont pas fonctionne,
206
+% c-a-d si Situation n'est ni perdante ni gagnante.
207
+
208
+% A FAIRE 					cas 3
209
+
210
+heuristique(J,Situation,H) :-
211
+    findall(X, (alignement(X,Situation),possible(X,J)),Lgagnant),
212
+    adversaire(J,A),
213
+    findall(Y, (alignement(Y,Situation),possible(Y,A)),Lperdant),
214
+    length(Lgagnant, LG),
215
+    length(Lperdant, LP),
216
+    H is LG-LP.
217
+    
218
+test_heuristique :-
219
+    (   (   S1= [ [_,_,_], [_,_,_], [_,_,_] ], 
220
+        heuristique(x,S1,0), 
221
+        S2 = [ [x,o,_], [_,_,_], [_,_,_] ], 
222
+        heuristique(x,S2,1),
223
+        S3 = [ [_,o,_], [_,x,_], [_,_,_] ], 
224
+        heuristique(x,S3,2),
225
+        S4 = [ [o,_,_], [_,x,_], [_,_,_] ], 
226
+        heuristique(x,S4,1),
227
+        S5 = [ [x,_,_], [_,o,_], [_,_,_] ], 
228
+        heuristique(o,S5,1),
229
+        S6 = [ [x,_,_], [_,x,_], [_,_,x] ], 
230
+        heuristique(o,S6,-10000),
231
+        S7 = [ [x,_,_], [_,x,_], [_,_,x] ], 
232
+        heuristique(x,S7,10000)
233
+    ) -> 
234
+        		write('test heuristique successful')
235
+         	;   
236
+                write('error heuristique test')
237
+        ).
238
+
239
+
240
+
241
+/*******************************************
242
+ * 
243
+ * 
244
+ * 
245
+ * 
246
+ * NEGAMAX
247
+ * 
248
+ * 
249
+ * 
250
+ * *****************************************/
251
+ 	/*
252
+	Ce programme met en oeuvre l'algorithme Minmax (avec convention
253
+	negamax) et l'illustre sur le jeu du TicTacToe (morpion 3x3)
254
+	*/
255
+	
256
+/*:- [tictactoe].*/
257
+
258
+
259
+	/****************************************************
260
+  	ALGORITHME MINMAX avec convention NEGAMAX : negamax/5
261
+  	*****************************************************/
262
+
263
+	/*
264
+	negamax(+J, +Etat, +P, +Pmax, [?Coup, ?Val])
265
+
266
+	SPECIFICATIONS :
267
+
268
+	retourne pour un joueur J donne, devant jouer dans
269
+	une situation donnee Etat, de profondeur donnee P,
270
+	le meilleur couple [Coup, Valeur] apres une analyse
271
+	pouvant aller jusqu'a la profondeur Pmax.
272
+
273
+	Il y a 3 cas a decrire (donc 3 clauses pour negamax/5)
274
+	
275
+	1/ la profondeur maximale est atteinte : on ne peut pas
276
+	developper cet Etat ; 
277
+	il n'y a donc pas de coup possible a jouer (Coup = rien)
278
+	et l'evaluation de Etat est faite par l'heuristique.
279
+
280
+	2/ la profondeur maximale n'est pas  atteinte mais J ne
281
+	peut pas jouer ; au TicTacToe un joueur ne peut pas jouer
282
+	quand le tableau est complet (totalement instancie) ;
283
+	il n'y a pas de coup a jouer (Coup = rien)
284
+	et l'evaluation de Etat est faite par l'heuristique.
285
+
286
+	3/ la profondeur maxi n'est pas atteinte et J peut encore
287
+	jouer. Il faut evaluer le sous-arbre complet issu de Etat ; 
288
+
289
+	- on determine d'abord la liste de tous les couples
290
+	[Coup_possible, Situation_suivante] via le predicat
291
+	 successeurs/3 (deja fourni, voir plus bas).
292
+
293
+	- cette liste est passee a un predicat intermediaire :
294
+	loop_negamax/5, charge d'appliquer negamax sur chaque
295
+	Situation_suivante ; loop_negamax/5 retourne une liste de
296
+	couples [Coup_possible, Valeur]
297
+
298
+	- parmi cette liste, on garde le meilleur couple, c-a-d celui
299
+	qui a la plus petite valeur (cf. predicat meilleur/2);
300
+	soit [C1,V1] ce couple optimal. Le predicat meilleur/2
301
+	effectue cette selection.
302
+
303
+	- finalement le couple retourne par negamax est [Coup, V2]
304
+	avec : V2 is -V1 (cf. convention negamax vue en cours).
305
+
306
+A FAIRE : ECRIRE ici les clauses de negamax/5
307
+.....................................
308
+	*/
309
+	negamax(J, Etat, P, P, [nil, Val]) :- heuristique(J,Etat,Val).
310
+
311
+	negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
312
+    									   heuristique(J,Etat,Val),
313
+    									   Val = 10000.	 
314
+	
315
+    negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
316
+    									   heuristique(J,Etat,Val),
317
+    									   Val = -10000.	 
318
+
319
+	negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
320
+    									situation_terminale(J,Etat),
321
+    									heuristique(J,Etat,Val).
322
+	
323
+	
324
+	negamax(J,Etat, P, Pmax, [Coup,Val]) :- P < Pmax ,
325
+                             				successeurs(J,Etat,Successeurs), 
326
+    										loop_negamax(J,P,Pmax,Successeurs,Liste_Couples),
327
+    										meilleur(Liste_Couples,[Coup,V1]),
328
+    										Val is -V1.
329
+    
330
+    										
331
+	
332
+
333
+	/*******************************************
334
+	 DEVELOPPEMENT D'UNE SITUATION NON TERMINALE
335
+	 successeurs/3 
336
+	 *******************************************/
337
+
338
+	 /*
339
+   	 successeurs(+J,+Etat, ?Succ)
340
+
341
+   	 retourne la liste des couples [Coup, Etat_Suivant]
342
+ 	 pour un joueur donne dans une situation donnee 
343
+	 */
344
+
345
+successeurs(J,Etat,Succ) :-
346
+	copy_term(Etat, Etat_Suiv),
347
+	findall([Coup,Etat_Suiv],
348
+		    successeur(J,Etat_Suiv,Coup),
349
+		    Succ).
350
+
351
+	/*************************************
352
+         Boucle permettant d'appliquer negamax 
353
+         a chaque situation suivante :
354
+	*************************************/
355
+
356
+	/*
357
+	loop_negamax(+J,+P,+Pmax,+Successeurs,?Liste_Couples)
358
+	retourne la liste des couples [Coup, Valeur_Situation_Suivante]
359
+	a partir de la liste des couples [Coup, Situation_Suivante]
360
+	*/
361
+
362
+loop_negamax(_,_, _  ,[],                []).
363
+loop_negamax(J,P,Pmax,[[Coup,Suiv]|Succ],[[Coup,Vsuiv]|Reste_Couples]) :-
364
+	loop_negamax(J,P,Pmax,Succ,Reste_Couples),
365
+	adversaire(J,A),
366
+	Pnew is P+1,
367
+	negamax(A,Suiv,Pnew,Pmax, [_,Vsuiv]).
368
+
369
+	/*
370
+
371
+A FAIRE : commenter chaque litteral de la 2eme clause de loop_negamax/5,
372
+	en particulier la forme du terme [_,Vsuiv] dans le dernier
373
+	litteral ?
374
+	*/
375
+
376
+	/*********************************
377
+	 Selection du couple qui a la plus
378
+	 petite valeur V 
379
+	 *********************************/
380
+
381
+	/*
382
+	meilleur(+Liste_de_Couples, ?Meilleur_Couple)
383
+
384
+	SPECIFICATIONS :
385
+	On suppose que chaque element de la liste est du type [C,V]
386
+	- le meilleur dans une liste a un seul element est cet element
387
+	- le meilleur dans une liste [X|L] avec L \= [], est obtenu en comparant
388
+	  X et Y,le meilleur couple de L 
389
+	  Entre X et Y on garde celui qui a la petite valeur de V.
390
+
391
+A FAIRE : ECRIRE ici les clauses de meilleur/2
392
+	*/
393
+	/*meilleur([Cm,Vm], [Cm,Vm]).
394
+	meilleur([[C,V]|R], [Cm,Vm]) :-
395
+    	meilleur(R, [Cm,Vm]),
396
+    	(   Vm > V ->   ).*/
397
+	meilleur([X], X). %le meilleur dans une liste a un seul element est cet element
398
+    meilleur(Liste_de_Couples, MeilleurCouple ) :- 
399
+    	Liste_de_Couples = [ [C,V] | Fin ] ,
400
+    	meilleur(Fin,[Cout,Val]),
401
+    	
402
+    	(   Val > V ->    MeilleurCouple = [C , V] 
403
+        	;   
404
+        	MeilleurCouple = [Cout , Val] 
405
+        ).
406
+
407
+
408
+
409
+	    test_meilleur :-
410
+    	( ( meilleur([[a,2],[b,3],[c,4] ],[a,2]),
411
+            meilleur([[a,2],[b,3],[c,1] ],[c,1]),
412
+            meilleur([[a,2],[b,2]],[b,2]),
413
+            meilleur([[a,2]],[a,2])
414
+           )  -> 
415
+        		write('test meilleur successful')
416
+         	;   
417
+                write('error meilleur test')
418
+        ).
419
+
420
+	/******************
421
+  	PROGRAMME PRINCIPAL
422
+  	*******************/
423
+
424
+main(B,V, Pmax) :-
425
+	   situation_initiale(Ini),
426
+       joueur_initial(J),
427
+       negamax(J,Ini,0,Pmax,[B,V]).	
428
+

Loading…
Cancel
Save