/* Pour jouer contre l'IA: lancer_partie(f). permet de jouer en tant que noir lancer_partie(s). permet de jouer en tant que blanc Rentrez les lettres des colonnes en minuscules seulement. */ profondeur(6). % Profondeur actuelle = 6 /*************************************************************************************************************************************** Initialise un plateau de jeu selon les règles de l'arbitre. Structure d'une case: [Colonne, Ligne] Par exemple, la case [-3, 3] est la case en haut à gauche du plateau (A1) et [3, 3] celle en bas à droite (F6). ***************************************************************************************************************************************/ init_plateau([[[1, 1], [-1, -1]], [[-1, 1], [1, -1]]]). /*************************************************************************************************************************************** Cette fonction renvoie la valeur de l'indice supérieur à celui reçu en argument en tenant compte de l'ordre suivant: -3, -2, -1, 1, 2, 3 voisin_superieur(1, -Y): associe 2 à Y car 2 est le voisin supérieur de 1. voisin_superieur(-X, 2): associe 1 à X car 1 est le voisin inférieur de 2 puisque 2 est le voisin supérieur de 1. voisin_superieur(1, 2) = true car 2 est après 1 (attention à voisin_superieur(-1, 1) qui est true également). voisin_superieur(1, 3) = false car le voisin supérieur de 1 est 2 et non 3. ***************************************************************************************************************************************/ voisin_superieur(-3, -2). voisin_superieur(-2, -1). voisin_superieur(-1, 1). voisin_superieur(1, 2). voisin_superieur(2, 3). /*************************************************************************************************************************************** On peut se déplacer dans tous les sens. Nous avons donc décidé d'utiliser les noms des points cardinaux pour plus de simplicité. Cette fonction permet de récupérer les coordonnées de la case voisine suivant une direction donnée parmi les points cardinaux: - nord - nordEst - est - sudEst - sud - sudOuest - ouest - nordOuest case_voisine(+Case, +Direction, -CaseVoisine) -> Renvoie la case voisine de Case suivant la direction Direction. case_voisine(+Case, -Direction, -CaseVoisine) -> Renvoie toutes les cases voisines de Case en backtrackant. ***************************************************************************************************************************************/ case_voisine([Xd, Yd], est, [Xa, Yd]) :- voisin_superieur(Xd, Xa). case_voisine([Xd, Yd], sudEst, [Xa, Ya]) :- voisin_superieur(Xd, Xa), voisin_superieur(Ya, Yd). case_voisine([Xd, Yd], sud, [Xd, Ya]) :- voisin_superieur(Ya, Yd). case_voisine([Xd, Yd], sudOuest, [Xa, Ya]) :- voisin_superieur(Xa, Xd), voisin_superieur(Ya, Yd). case_voisine([Xd, Yd], ouest, [Xa, Yd]) :- voisin_superieur(Xa, Xd). case_voisine([Xd, Yd], nordOuest, [Xa, Ya]) :- voisin_superieur(Xa, Xd), voisin_superieur(Yd, Ya). case_voisine([Xd, Yd], nord, [Xd, Ya]) :- voisin_superieur(Yd, Ya). case_voisine([Xd, Yd], nordEst, [Xa, Ya]) :- voisin_superieur(Xd, Xa), voisin_superieur(Yd, Ya). /*************************************************************************************************************************************** Permet de récupérer la couleur de l'adversaire. couleur_adversaire(+C1, -C2): Affecte à C2 la couleur de l'adversaire de C1. ***************************************************************************************************************************************/ couleur_adversaire(noir, blanc). couleur_adversaire(blanc, noir). /*************************************************************************************************************************************** Permet de récupérer la liste des pions d'un joueur suivant sa couleur. pions_joueur(+Couleur, +Plateau, -Pions): Affecte à Pions la liste des pions du joueur dont la couleur est Couleur. ***************************************************************************************************************************************/ pions_joueur(noir, [X, _], X). pions_joueur(blanc, [_, X], X). /*************************************************************************************************************************************** Construction de la liste des coups légaux. Pour ce faire, nous allons observer toutes les cases contenant des pions du joueur adverse et voir si ces pions peuvent être mangés. Structure d'un coup: [Case, [Liste des cases que ce coup retournera s'il est joué]] ***************************************************************************************************************************************/ %%% Finalement, enregistrer les coups légaux permet de faire moins d'inférences mais prend beaucoup plus de temps CPU... %:- dynamic (tous_coups_legaux_mem/3). %tous_coups_legaux(Couleur, Plateau, Coups) :- tous_coups_legaux_mem(Couleur, Plateau, Coups), !. tous_coups_legaux(Couleur, Plateau, Coups) :- pions_joueur(Couleur, Plateau, Pions), couleur_adversaire(Couleur, CAdverse), pions_joueur(CAdverse, Plateau, PAdverse), tous_coups_legaux1(Couleur, Pions, PAdverse, PAdverse, [], [], Coups). % asserta(tous_coups_legaux_mem(Couleur, Plateau, Coups):- !), !. /*************************************************************************************************************************************** Appel successivement coups_legaux avec les pions de l'adversaire. Dès que l'on en a terminé avec un pion, on passe au suivant. Lorsque la liste des pions à explorer est vide, on termine. ***************************************************************************************************************************************/ tous_coups_legaux1(Couleur, P, PA, [X|Y], T, A, R) :- coups_legaux(X, P, PA, T, [], T1, R1), append(R1, A, A1), tous_coups_legaux1(Couleur, P, PA, Y, T1, A1, R). tous_coups_legaux1(_, _, _, [], _, A, A). % Transfert de l'accumulateur dans la variable de résultat. /*************************************************************************************************************************************** Recherche tous les coups légaux que l'on peut faire à partir d'un pion du joueur adverse. ***************************************************************************************************************************************/ coups_legaux(Case, P, PA, T, A, TR, R) :- case_voisine(Case, _, NCase), not(member(NCase, T)), !, jetons_retournes(NCase, P, PA, [], [], R1), (not_empty(R1)-> coups_legaux(Case, P, PA, [NCase|T], [[NCase, R1]|A], TR, R); coups_legaux(Case, P, PA, [NCase|T], A, TR, R)). coups_legaux(_, _, _, T, A, T, A). /*************************************************************************************************************************************** Construction de la liste des pions qu'il faudra retourner si le coup est joué. Si un coup n'est pas légal, sa liste de pions à retourner est vide. ***************************************************************************************************************************************/ jetons_retournes(Case, P, PA, T, A, R) :- not(member(Case, P)), not(member(Case, PA)), case_voisine(Case, Direction, NCase), member(NCase, PA), not(member(NCase, T)), sandwich(NCase, Direction, P, PA, [NCase], R1), !, append(R1, A, A1), jetons_retournes(Case, P, PA, [NCase|T], A1, R). jetons_retournes(_, _, _, _, A, A). /*************************************************************************************************************************************** Construit la liste des pions à retourner pour un coup suivant une certaine direction. Les directions sont données par jetons_retournes. ***************************************************************************************************************************************/ % Lorsque la case observée est un pion ami, on a terminé la construction de la liste des jetons à retourner. % Un appel à sandwich/6 est donc fait avec des listes vides à la place des pions. Dans ce cas, on doit mettre l'accumulateur dans la variable résultat. sandwich(_, _, [], [], A, A) :- !. % La construction de la liste n'est pas terminée, on check donc la case reçue. sandwich(Case, Direction, P, PA, A, R) :- case_voisine(Case, Direction, NCase), (member(NCase, PA)-> (!, sandwich(NCase, Direction, P, PA, [NCase|A], R)); (!, member(NCase, P), !, sandwich(Case, Direction, [], [], A, R))). /*************************************************************************************************************************************** Sélectionne aléatoirement un coup à jouer parmi tous les coups légaux suivant une couleur et un plateau de jeu. ***************************************************************************************************************************************/ select_coup(Couleur, Plateau, Coup) :- tous_coups_legaux(Couleur, Plateau, L), length(L, NbCoups), NbCoups > 0, !, Index is random(NbCoups), nth0(Index, L, Coup). select_coup(_, _, [[0, 0], []]). /*************************************************************************************************************************************** Joue un coup donné dans le plateau courant. ***************************************************************************************************************************************/ joue_coup(Couleur, Plateau, [Case, CasesR], NPlateau) :- ajouter_pions(Couleur, Plateau, [Case|CasesR], NPlateau1), couleur_adversaire(Couleur, CAdverse), retirer_pions(CAdverse, NPlateau1, CasesR, NPlateau). /*************************************************************************************************************************************** Ajoute une liste de pions dans la liste des pions d'un certain joueur suivant sa couleur. ***************************************************************************************************************************************/ ajouter_pions(noir, [PN, PB], Pions, [PNB, PB]) :- append(Pions, PN, PNB). ajouter_pions(blanc, [PN, PB], Pions, [PN, PBB]) :- append(Pions, PB, PBB). /*************************************************************************************************************************************** Retire une liste de pions dans la liste des pions d'un certain joueur suivant sa couleur. ***************************************************************************************************************************************/ retirer_pions(noir, [PN, PB], Pions, [PNB, PB]) :- delete_multiple(PN, Pions, PNB). retirer_pions(blanc, [PN, PB], Pions, [PN, PBB]) :- delete_multiple(PB, Pions, PBB). /*************************************************************************************************************************************** Permet de savoir si une liste est non vide. Il faut être sûr que l'élément donné en argument est une liste. Aucun test n'est effectué dans la fonction. ***************************************************************************************************************************************/ not_empty([]) :- !, fail. not_empty(_). /*************************************************************************************************************************************** Supprime une liste d'éléments dans une liste. Même principe que delete mais avec une liste d'éléments et non un singleton. ***************************************************************************************************************************************/ delete_multiple(L, [], L) :- !. delete_multiple(L, [X|Y], R) :- delete(L, X, L1), delete_multiple(L1, Y, R). /**************************************************************************************************************************************/ /*************************************************************************************************************************************** HEURISTIQUE ***************************************************************************************************************************************/ /**************************************************************************************************************************************/ /*************************************************************************************************************************************** Permet de savoir si une case est un coin. ***************************************************************************************************************************************/ coin([3, 3]) :- !. coin([3, -3]) :- !. coin([-3, -3]) :- !. coin([-3, 3]). /*************************************************************************************************************************************** Permet de savoir si une case est un bord. ***************************************************************************************************************************************/ bord([-1, 3]) :- !. bord([1, 3]) :- !. bord([-3, 1]) :- !. bord([3, 1]) :- !. bord([-3, -1]) :- !. bord([3, -1]) :- !. bord([-1, -3]) :- !. bord([1, -3]). /*************************************************************************************************************************************** Permet de savoir si une case est une case X. Les cases X sont les cases adjacentes des coins situées sur les diagonales. ***************************************************************************************************************************************/ % case X du nord-ouest caseX_NO([-2, 2]). % case X du sud-ouest caseX_SO([-2, -2]). % case X du nord-est caseX_NE([2, 2]). % case X du sud-est caseX_SE([2, -2]). % N'importe qu'elle case X caseX(Case) :- (caseX_NO(Case), !); (caseX_NE(Case), !); (caseX_SO(Case), !); (caseX_SE(Case), !). /*************************************************************************************************************************************** Permet de savoir si une case est une case C. Les cases C sont les cases adjacentes des coins situées sur les bords. ***************************************************************************************************************************************/ % cases C du nord-ouest caseC_NO([-2, 3]) :- !. caseC_NO([-3, 2]). % cases C du sud-ouest caseC_SO([-3, -2]) :- !. caseC_SO([-2, -3]). % cases C du nord-est caseC_NE([2, 3]) :- !. caseC_NE([3, 2]). % cases C du sud-est caseC_SE([3, -2]) :- !. caseC_SE([2, -3]). % N'importe qu'elle case C caseC(Case) :- (caseC_NO(Case), !); (caseC_NE(Case), !); (caseC_SE(Case), !); (caseC_SO(Case), !). /*************************************************************************************************************************************** Permet de savoir si une case est une case centrale. ***************************************************************************************************************************************/ case_centrale([-1, 2]) :- !. case_centrale([1, 2]) :- !. case_centrale([-2, 1]) :- !. case_centrale([-1, 1]) :- !. case_centrale([1, 1]) :- !. case_centrale([2, 1]) :- !. case_centrale([-2, -1]) :- !. case_centrale([-1, -1]) :- !. case_centrale([1, -1]) :- !. case_centrale([2, -1]) :- !. case_centrale([-1, -2]) :- !. case_centrale([1, -2]). /*************************************************************************************************************************************** Fonction de tri des coups pour Alpha-Beta. Cette fonction sera en argument de predsort/3 qui prend un prédicat afin de trier une liste d'éléments. predsort/3 élimine les doublons. Par conséquent, pour des coups à priori égaux, nous ne renvoyons pas = car l'importance d'un coup est ici jugée uniquement sur la case où va être placé le pion et non la valeur du plateau qu'il va générer. ***************************************************************************************************************************************/ % Coup2 > Coup1 coup_important(>, [Coup1, _], [Coup2, _]) :- (coin(Coup2), !); (caseX(Coup1), !); (caseC(Coup1), not(caseX(Coup2)), !); (not(coin(Coup1)), bord(Coup2), !). % Sinon, on a donc Coup2 < Coup1 coup_important(<, _, _). /*************************************************************************************************************************************** Assigne une certaine valeur à une case selon sa position et aussi pour certaines, selon la configuration du plateau courant. ***************************************************************************************************************************************/ % Les coins sont très importants. case_val(Case, _, _, 800) :- coin(Case), !. % Les bords sont importants mais attention aux cases C. case_val(Case, _, _, 300) :- bord(Case), !. % Une case C est très dangereuse car elle peut permettre à l'adversaire de prendre un coin. Cependant, si on possède déjà le coin relatif à une case C, alors cette case est au final un bord comme un autre. case_val(Case, P, _, Value) :- (caseC_NO(Case), (member([-3, 3], P) -> Value is 300; Value is -400 ), !); (caseC_NE(Case), (member([3, 3], P) -> Value is 300; Value is -400 ), !); (caseC_SO(Case), (member([-3, -3], P) -> Value is 300; Value is -400 ), !); (caseC_SE(Case), (member([3, -3], P) -> Value is 300; Value is -400 ), !). % Les cases X sont considérées comme les cases les plus dangereuses. case_val(Case, _, _, -500) :- caseX(Case), !. /* N'a pas l'air d'être efficace... case_val(Case, P, _, Value) :- (caseX_NO(Case), (( member([-3, 3], P), member([-3, 2], P), member([-3, 1], P), member([-2, 3], P), member([-1, 3], P) ) -> Value is 100; Value is -500 ), !), (caseX_NE(Case), (( member([3, 3], P), member([3, 2], P), member([3, 1], P), member([2, 3], P), member([1, 3], P) ) -> Value is 100; Value is -500 ), !), (caseX_SO(Case), (( member([-3, -3], P), member([-3, -2], P), member([-3, -1], P), member([-2, -3], P), member([-1, -3], P) ) -> Value is 100; Value is -500 ), !), (caseX_SE(Case), (( member([3, -3], P), member([3, -2], P), member([3, -1], P), member([2, -3], P), member([1, -3], P) ) -> Value is 100; Value is -500 ), !).*/ % Ni un coin, ni un bord, ni une case C et ni une case X. C'est une case centrale. case_val(_, _, _, 100). /*************************************************************************************************************************************** Calcule la valeur totale des pions d'un joueur sur le plateau courant. ***************************************************************************************************************************************/ pions_val([], _, _, 0). pions_val([X|Y], P, PA, V) :- case_val(X, P, PA, V1), pions_val(Y, P, PA, V2), V is V1 + V2. /*************************************************************************************************************************************** Réussit si un pion est dans la frontière, sachant que la frontière d'un joueur est le nombre de pions adjacents à une case vide qu'il possède. ***************************************************************************************************************************************/ pion_frontiere(Case, P, PA) :- case_voisine(Case, _, NCase), not(member(NCase, P)), not(member(NCase, PA)), !. /*************************************************************************************************************************************** Associe une valeur à la frontière totale d'un joueur. ***************************************************************************************************************************************/ frontiere([], _, _, 0). frontiere([X|Y], P, PA, V) :- (pion_frontiere(X, P, PA) -> V1 is 10; V1 is 0 ), frontiere(Y, P, PA, V2), V is V1 + V2. /*************************************************************************************************************************************** Renvoie le nombre de pions d'un joueur. ***************************************************************************************************************************************/ nb_pions([PN, PB], N) :- length(PN, LN), length(PB, LB), N is LN + LB. /*************************************************************************************************************************************** Evalue la valeur d'un plateau selon l'heuristique que nous avons définie. ***************************************************************************************************************************************/ evaluer(Couleur, Plateau, Valeur) :- pions_joueur(Couleur, Plateau, Pions), couleur_adversaire(Couleur, CAdverse), pions_joueur(CAdverse, Plateau, PionsA), pions_val(Pions, Pions, PionsA, V1), pions_val(PionsA, PionsA, Pions, V2), frontiere(Pions, Pions, PionsA, V3), frontiere(PionsA, PionsA, Pions, V4), Valeur is (V1 - V2 - V3 + V4). /*************************************************************************************************************************************** Récupère le meilleur coup à jouer à partir d'une couleur et d'un plateau suivant une certaine profondeur de recherche. Appelle Alpha-Beta avec Alpha=-9999999 et Beta=9999999 ***************************************************************************************************************************************/ meilleur_coup(_, _, [[[1, 1], [-1, -1]], [[-1, 1], [1, -1]]], [[2,-1], [[1,-1]]]) :- !. meilleur_coup(N, Couleur, Plateau, Coup) :- alpha_beta(N, N, Couleur, Plateau, -9999999, 9999999, Coup, _). /**************************************************************************************************************************************/ /*************************************************************************************************************************************** ALPHA BETA Algorithme du MinMax avec l'optimisation Alpha-Beta qui élague des parties de l'arbre de recherche. Alpha-Beta est constitué des fonctions suivantes: alpha_beta/8 evaluate_and_choose/9 cutoff/11 ***************************************************************************************************************************************/ /**************************************************************************************************************************************/ /*************************************************************************************************************************************** Alpha-Beta alpha_beta(ProfondeurInitiale, ProfondeurCourante, Joueur, Plateau, Alpha, Beta, MeilleurCoup, ValeurCoup) ***************************************************************************************************************************************/ % La profondeur est égale à 0, on évalue le plateau feuille reçu. alpha_beta(_PInit, 0, Couleur, Plateau, _Alpha, _Beta, _Coup, Value) :- !, evaluer(Couleur, Plateau, Value). % On recherche les coups légaux pour ce plateau et on fait max_value, min_value qui sont résumés dans une seule fonction: evaluate_and_choose/9 % Un tri des coups est effectué au préalable afin d'optimiser l'élagage d'Alpha-Beta. alpha_beta(PInit, Profondeur, Couleur, Plateau, Alpha, Beta, Coup, Value) :- tous_coups_legaux(Couleur, Plateau, Coups), not_empty(Coups), Alpha1 is -Beta, % max/min Beta1 is -Alpha, Profondeur1 is Profondeur-1, predsort(coup_important, Coups, CoupsTries), evaluate_and_choose(Couleur, CoupsTries, Plateau, PInit, Profondeur1, Alpha1, Beta1, nil, (Coup, Value)), !. % alpha_beta a fail sur la taille de la liste des coups légaux dès le premier appel. % Le joueur doit passer, on indique un coup invalide qui sera reconnaissable par la suite. % Nous essaierons d'éviter cette situation tant que possible. alpha_beta(PInit, Profondeur, _, _, _, _, [[0,0],[]], 0) :- PInit==Profondeur, !. % alpha_beta a fail mais ce n'était pas le premier appel. % Cela veut dire que l'on peut jouer mais que dans l'arbre d'exploration des coups jouables on tombe à un moment sur un plateau où on ne peut pas jouer. % Dans ce cas, on regarde si le joueur adverse pourra jouer. % Si oui, on évalue ce coup avec une connotation négative. % Si non, c'est un état terminal et on regarde donc qui gagnerait dans ce cas. Si c'est le joueur courant, on évalue ce coup avec une très forte note. En revanche, si c'est l'adversaire qui gagne, on évalue à l'équivalent de moins l'infini. alpha_beta(_, _, Couleur, Plateau, _, _, _, Value) :- couleur_adversaire(Couleur, CAdverse), tous_coups_legaux(CAdverse, Plateau, Coups), (not_empty(Coups) -> Value is -2000; ( pions_joueur(Couleur, Plateau, Pions), length(Pions, NbPions), pions_joueur(CAdverse, Plateau, PionsA), length(PionsA, NbPionsA), Value is ((NbPions - NbPionsA) * 5000) ) ), !. /*************************************************************************************************************************************** Joue un coup et appelle alpha_beta/8 avec le plateau obtenu et le joueur ennemi. (min et max). ***************************************************************************************************************************************/ evaluate_and_choose(Couleur, [Coup|Coups], Plateau, PInit, Profondeur, Alpha, Beta, Record, BestCoup) :- joue_coup(Couleur, Plateau, Coup, Plateau1), couleur_adversaire(Couleur, CAdversaire), alpha_beta(PInit, Profondeur, CAdversaire, Plateau1, Alpha, Beta, _OtherCoup, Value), Value1 is -Value, cutoff(Couleur, Coup, Value1, PInit, Profondeur, Alpha, Beta, Coups, Plateau, Record, BestCoup). evaluate_and_choose(_Couleur, [], _Plateau, _PInit, _Profondeur, Alpha, _Beta, Coup, (Coup, Alpha)). /*************************************************************************************************************************************** Si Valeur >= Beta : On arrête le parcours car la valeur va soit continuer d'augmenter alors que l'on cherche un min, soit continuer de diminuer alors que l'on cherche un max. Si Alpha < Valeur < Beta : Le coup actuel est meilleur que le précédent, on le garde et on continue l'exploration. Si Valeur <= Alpha : Le coup actuel est moins bon que le précédent, on ne le garde pas et on continue l'exploration. ***************************************************************************************************************************************/ cutoff(_Couleur, Coup, Value, _PInit, _Profondeur, _Alpha, Beta, _Coups, _Plateau, _Record, (Coup, Value)) :- Value >= Beta, !. cutoff(Couleur, Coup, Value, PInit, Profondeur, Alpha, Beta, Coups, Plateau, _Record, BestCoup) :- Alpha < Value, Value < Beta, !, evaluate_and_choose(Couleur, Coups, Plateau, PInit, Profondeur, Value, Beta, Coup, BestCoup). cutoff(Couleur, _Coup, Value, PInit, Profondeur, Alpha, Beta, Coups, Plateau, Record, BestCoup) :- Value =< Alpha, !, evaluate_and_choose(Couleur, Coups, Plateau, PInit, Profondeur, Alpha, Beta, Record, BestCoup). /**************************************************************************************************************************************/ /*************************************************************************************************************************************** RECUPERATION DES COUPS DE L'UTILISATEUR ***************************************************************************************************************************************/ /**************************************************************************************************************************************/ /*************************************************************************************************************************************** Transformation d'une case (mouvement) du type [colonne, ligne] en un mouvement de la forme << LettreChiffre >> (ou passe). coup_arbitre(+Case, -CaseFormatArbitre) -> Transforme la case Case dans le format des cases de l'arbitre. coup_arbitre(-Case, +CaseFormatArbitre) -> Transforme la case au format arbitre en une case au format de notre programme. [-3, 3] = a1 [3, -3] = f6 ***************************************************************************************************************************************/ coup_arbitre([0, 0], passe) :- !. coup_arbitre([X, Y], Coup) :- colonne_arbitre(X, Colonne), ligne_arbitre(Y, Ligne), string_concat(Colonne, Ligne, String), string_to_atom(String, Coup). colonne_arbitre(-3, 'a'). colonne_arbitre(-2, 'b'). colonne_arbitre(-1, 'c'). colonne_arbitre(1, 'd'). colonne_arbitre(2, 'e'). colonne_arbitre(3, 'f'). ligne_arbitre(3, 1). ligne_arbitre(2, 2). ligne_arbitre(1, 3). ligne_arbitre(-1, 4). ligne_arbitre(-2, 5). ligne_arbitre(-3, 6). coup_user_to_arbitre(Colonne, Ligne, C) :- string_concat(Colonne, Ligne, String), string_to_atom(String, Coup), coup_arbitre(C, Coup). /**************************************************************************************************************************************/ /*************************************************************************************************************************************** JOUER + AFFICHAGE ***************************************************************************************************************************************/ /**************************************************************************************************************************************/ /*************************************************************************************************************************************** Lance une partie. ***************************************************************************************************************************************/ % L'humain joue en premier (noir). lancer_partie(f) :- init_plateau(P), joue_partie(0, humain, noir, P, _). % L'humain joue en second (blanc). lancer_partie(s) :- init_plateau(P), joue_partie(0, pc, noir, P, _). /*************************************************************************************************************************************** Joue une partie entre 2 joueurs. ***************************************************************************************************************************************/ % Double passe joue_partie(2, _, _, [PN, PB], [PN, PB]) :- !, write('FIN DE LA PARTIE d='')\n'), length(PN, LN), length(PB, LB), format('Noir a ~a pions et Blanc en a ~a.\n', [LN, LB]), (LN == LB -> write('Egalité!'); ((LN > LB -> write('Noir'); write('Blanc')), write(' est le grand gagnant! Il est trop fort à Othello :P\n'))). joue_partie(_, pc, Couleur, Plateau, NPlateau) :- write('Au tour de l''IA de jouer ('), write(Couleur), write('):\n'), afficher_plateau(Plateau), profondeur(Prof), time(meilleur_coup(Prof, Couleur, Plateau, Coup)), not(Coup == [[0,0],[]]), joue_coup(Couleur, Plateau, Coup, NPlateau1), !, couleur_adversaire(Couleur, CAdverse), joue_partie(0, humain, CAdverse, NPlateau1, NPlateau). joue_partie(_, humain, Couleur, Plateau, NPlateau) :- write('C''est a votre tour de jouer ('), write(Couleur), write('):\n'), afficher_plateau(Plateau), get_coup_humain(Couleur, Plateau, Coup), joue_coup(Couleur, Plateau, Coup, NPlateau1), !, couleur_adversaire(Couleur, CAdverse), joue_partie(0, pc, CAdverse, NPlateau1, NPlateau). % Passe joue_partie(NbPasses, pc, Couleur, Plateau, NPlateau) :- !, N is NbPasses + 1, couleur_adversaire(Couleur, CAdverse), joue_partie(N, humain, CAdverse, Plateau, NPlateau). joue_partie(NbPasses, humain, Couleur, Plateau, NPlateau) :- !, N is NbPasses + 1, couleur_adversaire(Couleur, CAdverse), joue_partie(N, pc, CAdverse, Plateau, NPlateau). /*************************************************************************************************************************************** Récupère au clavier la case dans laquelle veut jouer le joueur humain. ***************************************************************************************************************************************/ get_coup_humain(Couleur, Plateau, Coup) :- write('\nColonne: '), read(Colonne), write('\nLigne: '), read(Ligne), tous_coups_legaux(Couleur, Plateau, Coups), length(Coups, NbCoups), (NbCoups > 0 -> (coup_user_to_arbitre(Colonne, Ligne, CaseCoup), get_coup_from_list(Coups, CaseCoup, Coup)); (!, fail)) , !. get_coup_humain(C, P, Coup) :- write('Mauvaise case\n'), get_coup_humain(C, P, Coup). /*************************************************************************************************************************************** Récupère le coup correspondant à la case reçue. ***************************************************************************************************************************************/ get_coup_from_list([[CC, Y]|_], CC, [CC, Y]). get_coup_from_list([[_, _]|Z], CC, Coup) :- get_coup_from_list(Z, CC, Coup). /*************************************************************************************************************************************** Affichage d'un plateau de jeu 6x6. ***************************************************************************************************************************************/ afficher_plateau(P) :- write(' A B C D E F\n1 '), afficher_plateau1(-3, 3, P). /*************************************************************************************************************************************** Même chose que affiche_plateau mais avec des arguments en plus pour savoir quelles sont la ligne et la colonne courante. Cette fonction est appelée par afficher_plateau avec au départ C=-3 et L=3. ***************************************************************************************************************************************/ % Fin de l'affichage. afficher_plateau1(3, -3, P) :- !, afficher_case([3, -3], P), write('\n\n'). % Affichage d'une case en fin de colonne => saut de ligne. afficher_plateau1(3, L, P) :- afficher_case([3, L], P), write('\n'), voisin_superieur(L1, L), !, ligne_arbitre(L1, LA), write(LA), write(' '), afficher_plateau1(-3, L1, P). % Affichage d'une case normale. afficher_plateau1(C, L, P) :- afficher_case([C, L], P), voisin_superieur(C, C1), afficher_plateau1(C1, L, P). /*************************************************************************************************************************************** Affiche une case du plateau de jeu. Pion noir = x Pion blanc = o Case vide = _ ***************************************************************************************************************************************/ afficher_case(C, [PN, PB]) :- write(' '), (member(C, PN) -> write('x'); (member(C, PB) -> write('o'); write('_'))).