/* Ce programme met en oeuvre l'algorithme Minmax (avec convention negamax) et l'illustre sur le jeu du TicTacToe (morpion 3x3) */ :- [tictactoe]. /**************************************************** ALGORITHME MINMAX avec convention NEGAMAX : negamax/5 *****************************************************/ /* negamax(+J, +Etat, +P, +Pmax, [?Coup, ?Val]) SPECIFICATIONS : retourne pour un joueur J donne, devant jouer dans une situation donnee Etat, de profondeur donnee P, le meilleur couple [Coup, Valeur] apres une analyse pouvant aller jusqu'a la profondeur Pmax. Il y a 3 cas a decrire (donc 3 clauses pour negamax/5) 1/ la profondeur maximale est atteinte : on ne peut pas developper cet Etat ; il n'y a donc pas de coup possible a jouer (Coup = rien) et l'evaluation de Etat est faite par l'heuristique. 2/ la profondeur maximale n'est pas atteinte mais J ne peut pas jouer ; au TicTacToe un joueur ne peut pas jouer quand le tableau est complet (totalement instancie) ; il n'y a pas de coup a jouer (Coup = rien) et l'evaluation de Etat est faite par l'heuristique. 3/ la profondeur maxi n'est pas atteinte et J peut encore jouer. Il faut evaluer le sous-arbre complet issu de Etat ; - on determine d'abord la liste de tous les couples [Coup_possible, Situation_suivante] via le predicat successeurs/3 (deja fourni, voir plus bas). - cette liste est passee a un predicat intermediaire : loop_negamax/5, charge d'appliquer negamax sur chaque Situation_suivante ; loop_negamax/5 retourne une liste de couples [Coup_possible, Valeur] - parmi cette liste, on garde le meilleur couple, c-a-d celui qui a la plus petite valeur (cf. predicat meilleur/2); soit [C1,V1] ce couple optimal. Le predicat meilleur/2 effectue cette selection. - finalement le couple retourne par negamax est [Coup, V2] avec : V2 is -V1 (cf. convention negamax vue en cours). A FAIRE : ECRIRE ici les clauses de negamax/5 ..................................... */ negamax(J, Etat, P, P, [nill, H]) :- heuristique(J,Etat,H), !. negamax(J, Etat, _, _, [nill, H]) :- situation_terminale(J, Etat), heuristique(J,Etat,H), !. negamax(J, Etat, _, _, [nill, H]) :- heuristique(J,Etat,H), H = 10000, !. negamax(J, Etat, _, _, [nill, H]) :- heuristique(J,Etat,H), H = -10000, !. negamax(J, Etat, P, Pmax, [Coup, V2]) :- P < Pmax, successeurs(J, Etat, Succ), loop_negamax(J, P, Pmax, Succ, L), meilleur(L, [Coup, V1]), V2 is 0 - V1. /******************************************* DEVELOPPEMENT D'UNE SITUATION NON TERMINALE successeurs/3 *******************************************/ /* successeurs(+J,+Etat, ?Succ) retourne la liste des couples [Coup, Etat_Suivant] pour un joueur donne dans une situation donnee */ successeurs(J,Etat,Succ) :- copy_term(Etat, Etat_Suiv), findall([Coup,Etat_Suiv], successeur(J,Etat_Suiv,Coup), Succ). /************************************* Boucle permettant d'appliquer negamax a chaque situation suivante : *************************************/ /* loop_negamax(+J,+P,+Pmax,+Successeurs,?Liste_Couples) retourne la liste des couples [Coup, Valeur_Situation_Suivante] a partir de la liste des couples [Coup, Situation_Suivante] */ loop_negamax(_,_, _ ,[], []). loop_negamax(J,P,Pmax,[[Coup,Suiv]|Succ],[[Coup,Vsuiv]|Reste_Couples]) :- loop_negamax(J,P,Pmax,Succ,Reste_Couples), adversaire(J,A), Pnew is P+1, negamax(A,Suiv,Pnew,Pmax, [_,Vsuiv]). /* A FAIRE : commenter chaque litteral de la 2eme clause de loop_negamax/5, en particulier la forme du terme [_,Vsuiv] dans le dernier litteral ? */ /********************************* Selection du couple qui a la plus petite valeur V *********************************/ /* meilleur(+Liste_de_Couples, ?Meilleur_Couple) SPECIFICATIONS : On suppose que chaque element de la liste est du type [C,V] - le meilleur dans une liste a un seul element est cet element - le meilleur dans une liste [X|L] avec L \= [], est obtenu en comparant X et Y,le meilleur couple de L Entre X et Y on garde celui qui a la petite valeur de V. A FAIRE : ECRIRE ici les clauses de meilleur/2 */ meilleur([X], X). meilleur([[_, V] | Rest], [C1, V1]) :- Rest \= [], meilleur(Rest, [C1, V1]), V >= V1, !. meilleur([[C, V] | Rest], [C, V]) :- Rest \= []. /****************** PROGRAMME PRINCIPAL *******************/ main(B,V, Pmax) :- situation_initiale(I), /*I = [[_,o,_], [x,x,_], [_,x,o]],*/ /*joueur_initial(J),*/ J = x, negamax(J, I, 1, Pmax, [B, V]). cpu_time(Goal, Elapsed_Time) :- statistics(process_cputime,Start), call(Goal), statistics(process_cputime,Finish), Elapsed_Time is Finish-Start. /* A FAIRE : Compléter puis tester le programme principal pour plusieurs valeurs de la profondeur maximale. Pmax = 1, 2, 3, 4 ... Commentez les résultats obtenus. */ /*jouer_adv(I, N) :- jouer_*/