371 lines
9.4 KiB
Prolog
371 lines
9.4 KiB
Prolog
/*********************************
|
||
DESCRIPTION DU JEU DU TIC-TAC-TOE
|
||
*********************************/
|
||
|
||
/*
|
||
Une situation est decrite par une matrice 3x3.
|
||
Chaque case est soit un emplacement libre (Variable LIBRE), soit contient le symbole d'un des 2 joueurs (o ou x)
|
||
|
||
Contrairement a la convention du tp precedent, pour modeliser une case libre
|
||
dans une matrice on n'utilise pas une constante speciale (ex : nil, 'vide', 'libre','inoccupee' ...);
|
||
On utilise plut<75>t un identificateur de variable, qui n'est pas unifiee (ex : X, A, ... ou _) .
|
||
La situation initiale est une "matrice" 3x3 (liste de 3 listes de 3 termes chacune)
|
||
o<> chaque terme est une variable libre.
|
||
Chaque coup d'un des 2 joureurs consiste a donner une valeur (symbole x ou o) a une case libre de la grille
|
||
et non a deplacer des symboles deja presents sur la grille.
|
||
|
||
Pour placer un symbole dans une grille S1, il suffit d'unifier une des variables encore libres de la matrice S1,
|
||
soit en ecrivant directement Case=o ou Case=x, ou bien en accedant a cette case avec les predicats member, nth1, ...
|
||
La grille S1 a change d'etat, mais on n'a pas besoin de 2 arguments representant la grille avant et apres le coup,
|
||
un seul suffit.
|
||
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
|
||
par un nouvel identificateur).
|
||
*/
|
||
|
||
situation_initiale([ [_,_,_],
|
||
[_,_,_],
|
||
[_,_,_] ]).
|
||
|
||
% Convention (arbitraire) : c''est x qui commence
|
||
|
||
joueur_initial(x).
|
||
|
||
|
||
% Definition de la relation adversaire/2
|
||
|
||
adversaire(x,o).
|
||
adversaire(o,x).
|
||
|
||
|
||
/****************************************************
|
||
DEFINIR ICI a l'aide du predicat ground/1 comment
|
||
reconnaitre une situation terminale dans laquelle il
|
||
n'y a aucun emplacement libre : aucun joueur ne peut
|
||
continuer a jouer (quel qu'il soit).
|
||
****************************************************/
|
||
|
||
|
||
situation_terminale(_Joueur, Situation) :-
|
||
ground(Situation), !.
|
||
|
||
situation_terminale(J, Situation) :-
|
||
alignement(Alig,Situation),
|
||
(alignement_gagnant(Alig,J) ; alignement_perdant(Alig,J)),
|
||
!.
|
||
|
||
|
||
|
||
/***************************
|
||
DEFINITIONS D'UN ALIGNEMENT
|
||
***************************/
|
||
|
||
alignement(L, Matrix) :- ligne( L,Matrix).
|
||
alignement(C, Matrix) :- colonne( C,Matrix).
|
||
alignement(D, Matrix) :- diagonale(D,Matrix).
|
||
|
||
/********************************************
|
||
DEFINIR ICI chaque type d'alignement maximal
|
||
existant dans une matrice carree NxN.
|
||
********************************************/
|
||
|
||
ligne(L, M) :-
|
||
nth1(_,M,L).
|
||
|
||
colonne(C,M) :-
|
||
colonne(_,C,M).
|
||
|
||
colonne(_,[],[]).
|
||
colonne(K, [Ele | Rest],[Ligne | M]) :-
|
||
nth1(K, Ligne, Ele),
|
||
colonne(K, Rest, M).
|
||
|
||
/* Definition de la relation liant une diagonale D a la matrice M dans laquelle elle se trouve.
|
||
il y en a 2 sortes de diagonales dans une matrice carree(https://fr.wikipedia.org/wiki/Diagonale) :
|
||
- la premiere diagonale (principale) : (A I)
|
||
- la seconde diagonale : (Z R)
|
||
A . . . . . . . Z
|
||
. \ . . . . . / .
|
||
. . \ . . . / . .
|
||
. . . \ . / . . .
|
||
. . . . X . . .
|
||
. . . / . \ . . .
|
||
. . / . . . \ . .
|
||
. / . . . . . \ .
|
||
R . . . . . . . I
|
||
*/
|
||
|
||
diagonale(D, M) :-
|
||
premiere_diag(1,D,M).
|
||
|
||
% deuxieme definition A COMPLETER
|
||
|
||
diagonale(D, M) :-
|
||
seconde_diag(_,D,M).
|
||
|
||
premiere_diag(_,[],[]).
|
||
premiere_diag(K,[E|D],[Ligne|M]) :-
|
||
nth1(K,Ligne,E),
|
||
K1 is K+1,
|
||
premiere_diag(K1,D,M).
|
||
|
||
seconde_diag(0, [], []).
|
||
seconde_diag(K, [E|D], [Ligne|M]) :-
|
||
seconde_diag(K1,D,M),
|
||
K is K1+1,
|
||
nth1(K,Ligne,E).
|
||
|
||
|
||
|
||
/*****************************
|
||
DEFINITION D'UN ALIGNEMENT
|
||
POSSIBLE POUR UN JOUEUR DONNE
|
||
*****************************/
|
||
|
||
possible([X|L], J) :- unifiable(X,J), possible(L,J).
|
||
possible( [], _).
|
||
|
||
/* Attention
|
||
il faut juste verifier le caractere unifiable
|
||
de chaque emplacement de la liste, mais il ne
|
||
faut pas realiser l'unification.
|
||
*/
|
||
|
||
% A FAIRE
|
||
unifiable(X,J) :-
|
||
ground(X),
|
||
X = J.
|
||
unifiable(X,_J) :-
|
||
var(X).
|
||
|
||
|
||
/**********************************
|
||
DEFINITION D'UN ALIGNEMENT GAGNANT
|
||
OU PERDANT POUR UN JOUEUR DONNE J
|
||
**********************************/
|
||
/*
|
||
Un alignement gagnant pour J est un alignement
|
||
possible pour J qui n'a aucun element encore libre.
|
||
*/
|
||
|
||
/*
|
||
Remarque : le predicat ground(X) permet de verifier qu'un terme
|
||
prolog quelconque ne contient aucune partie variable (libre).
|
||
exemples :
|
||
?- ground(Var).
|
||
no
|
||
?- ground([1,2]).
|
||
yes
|
||
?- ground(toto(nil)).
|
||
yes
|
||
?- ground( [1, toto(nil), foo(a,B,c)] ).
|
||
no
|
||
*/
|
||
|
||
/* Un alignement perdant pour J est un alignement gagnant pour son adversaire. */
|
||
|
||
% A FAIRE
|
||
|
||
alignement_gagnant(Ali, J) :-
|
||
ground(Ali),
|
||
possible(Ali,J).
|
||
|
||
alignement_perdant(Ali, J) :-
|
||
adversaire(J, Adv),
|
||
alignement_gagnant(Ali, Adv).
|
||
|
||
/* ****************************
|
||
DEFINITION D'UN ETAT SUCCESSEUR
|
||
****************************** */
|
||
|
||
/*
|
||
Il faut definir quelle operation subit la matrice
|
||
M representant l'Etat courant
|
||
lorsqu'un joueur J joue en coordonnees [L,C]
|
||
*/
|
||
|
||
% A FAIRE
|
||
successeur(J, Etat, [L,C]) :-
|
||
nth1(L, Etat, Ligne),
|
||
nth1(C, Ligne, X),
|
||
var(X),
|
||
X = J.
|
||
|
||
|
||
/**************************************
|
||
EVALUATION HEURISTIQUE D'UNE SITUATION
|
||
**************************************/
|
||
|
||
/*
|
||
1/ l'heuristique est +infini si la situation J est gagnante pour J
|
||
2/ l'heuristique est -infini si la situation J est perdante pour J
|
||
3/ sinon, on fait la difference entre :
|
||
le nombre d'alignements possibles pour J
|
||
moins
|
||
le nombre d'alignements possibles pour l'adversaire de J
|
||
*/
|
||
%'
|
||
|
||
heuristique(J,Situation,H) :- % cas 1
|
||
H = 10000, % grand nombre approximant +infini
|
||
alignement(Alig,Situation),
|
||
alignement_gagnant(Alig,J), !.
|
||
|
||
heuristique(J,Situation,H) :- % cas 2
|
||
H = -10000, % grand nombre approximant -infini
|
||
alignement(Alig,Situation),
|
||
alignement_perdant(Alig,J), !.
|
||
|
||
|
||
% on ne vient ici que si les cut precedents n''ont pas fonctionne,
|
||
% c-a-d si Situation n''est ni perdante ni gagnante.
|
||
|
||
%' A FAIRE cas 3'
|
||
heuristique(J,Situation,H) :-
|
||
adversaire(J, Adv),
|
||
findall(1, ( alignement(Ali, Situation), possible(Ali, J) ), Lj),
|
||
findall(1, ( alignement(Ali, Situation), possible(Ali, Adv) ), Ladv),
|
||
length(Lj, LenJ),
|
||
length(Ladv, LenAdv),
|
||
H is LenJ - LenAdv.
|
||
|
||
|
||
pretty_print([]).
|
||
|
||
pretty_print([A,B,C]):-
|
||
writeln(A),
|
||
writeln(B),
|
||
writeln(C),
|
||
write('\n\n').
|
||
|
||
/**************************
|
||
TEST ET SITUATIONS DE TEST
|
||
**************************/
|
||
situation_gagnanteJ([ [x,o,_],
|
||
[x,x,x],
|
||
[o,_,o] ]).
|
||
|
||
situation_gagnante2J([ [x,o,_],
|
||
[x,x,x],
|
||
[o,_,x] ]).
|
||
|
||
|
||
situation_perdanteJ([ [ o,_,x],
|
||
[o,x,x],
|
||
[o,x,o] ]).
|
||
|
||
situation_perdante2J([ [ o,_,x],
|
||
[o,x,x],
|
||
[o,o,o] ]).
|
||
|
||
situation_nulle([ [x,x,o],
|
||
[o,o,x],
|
||
[x,o,x] ]).
|
||
|
||
situation_test1([[x,o,_],
|
||
[x,x,_],
|
||
[_,_,o]]).
|
||
|
||
situation_test2([[x,_,_],
|
||
[_,o,_],
|
||
[_,_,x]]).
|
||
|
||
situation_chiffre([[1,2,3],
|
||
[4,5,6],
|
||
[7,8,9]]).
|
||
|
||
lignes_chiffre(L) :-
|
||
L= [[1,2,3],[4,5,6],[7,8,9]].
|
||
|
||
colonnes_chiffre(L) :-
|
||
L= [[1,4,7],[2,5,8],[3,6,9]].
|
||
|
||
diagonales_chiffre(L) :-
|
||
L= [[1,5,9],[3,5,7]].
|
||
|
||
alignements_chiffre(L) :-
|
||
L = [[1,2,3],[4,5,6],[7,8,9],[1,4,7],[2,5,8],[3,6,9],[1,5,9],[3,5,7]].
|
||
|
||
/*RUN ALL TESTS*/
|
||
test_all_pred :-
|
||
test_pred_situation_terminale,
|
||
test_pred_ligne,
|
||
test_pred_colonne,
|
||
test_pred_diagonale,
|
||
test_pred_alignement,
|
||
test_pred_alignement_gagnant,
|
||
test_pred_alignement_perdant,
|
||
test_heuristique_situation_gagnante,
|
||
test_heuristique_situation_perdante,
|
||
test_heuristique_situation_nulle,
|
||
test_heuristique_avantage.
|
||
|
||
|
||
test_pred_situation_terminale :-
|
||
situation_nulle(Final),
|
||
joueur_initial(J),
|
||
situation_terminale(J,Final).
|
||
|
||
test_pred_ligne:-
|
||
situation_chiffre(Situation),
|
||
findall(Ligne,(ligne(Ligne,Situation)), L),
|
||
length(L,3),
|
||
lignes_chiffre(L).
|
||
|
||
test_pred_colonne:-
|
||
situation_chiffre(Situation),
|
||
findall(Colonne,(colonne(Colonne,Situation)), L),
|
||
length(L,3),
|
||
colonnes_chiffre(L).
|
||
|
||
test_pred_diagonale:-
|
||
situation_chiffre(Situation),
|
||
findall(Diagonale,(diagonale(Diagonale,Situation)), L),
|
||
length(L,2),
|
||
diagonales_chiffre(L).
|
||
|
||
test_pred_alignement :-
|
||
situation_chiffre(Situation),
|
||
findall(Ali, (alignement(Ali, Situation)), L),
|
||
length(L,8),
|
||
alignements_chiffre(L).
|
||
|
||
test_pred_alignement_gagnant:-
|
||
situation_gagnanteJ(Situation),
|
||
situation_gagnante2J(Situation2),
|
||
joueur_initial(J),
|
||
findall(1, ( alignement(Ali, Situation), alignement_gagnant(Ali,J) ), L),
|
||
length(L,1),
|
||
|
||
findall(1, (alignement(Ali,Situation2), alignement_gagnant(Ali,J)), L2),
|
||
length(L2,2).
|
||
|
||
test_pred_alignement_perdant:-
|
||
situation_perdanteJ(Situation),
|
||
situation_perdante2J(Situation2),
|
||
joueur_initial(J),
|
||
findall(1, ( alignement(Ali, Situation), alignement_perdant(Ali,J) ), L),
|
||
length(L,1),
|
||
|
||
findall(1, (alignement(Ali,Situation2), alignement_perdant(Ali,J)), L2),
|
||
length(L2,2).
|
||
|
||
test_heuristique_situation_gagnante :-
|
||
situation_gagnanteJ(Situation),
|
||
joueur_initial(J),
|
||
heuristique(J, Situation, 10000).
|
||
|
||
test_heuristique_situation_perdante :-
|
||
situation_perdanteJ(Situation),
|
||
joueur_initial(J),
|
||
heuristique(J, Situation, -10000).
|
||
|
||
test_heuristique_situation_nulle :-
|
||
situation_nulle(Situation),
|
||
joueur_initial(J),
|
||
heuristique(J, Situation, 0).
|
||
|
||
test_heuristique_avantage :-
|
||
situation_test1(Situation),
|
||
joueur_initial(J),
|
||
heuristique(J, Situation, 1).
|
||
|