%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Vous pouvez tester une partie entre deux joueurs aléatoire de la façon suivante: % lancer_partie. % Le nombre de lignes/colonnes du damier va vous être demandé. Entrez un nombre pair positif. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* Permet de lancer une partie. Demande le nombre de lignes/colonnes à l'utilisateur. */ %%%%% %%%%% %%%%% %%%%% %%%%% lancer_partie :- write('Veuillez indiquer le nombre de lignes svp: '), read(N), integer(N), N > 0, NM is N mod 2, NM == 0, N1 is N/2, !, init_plateau(N1, P), joue_partie(0, noir, P, _). lancer_partie :- write('Nombre de lignes incorrect.'), !, fail. %%%%% %%%%% %%%%% %%%%% %%%%% /* Initialise un plateau de jeu. N=nombre de (lignes ou colonnes)/2 Le plateau de jeu doit être carré avec un nombre pair de lignes/colonnes. Structure d'une case: [Colonne, Ligne] Par exemple, la case [-3, 3] est la case en haut à gauche du plateau et [3, 3] celle en bas à droite. */ %%%%% %%%%% %%%%% %%%%% %%%%% init_plateau(N, [N, [[-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(_, -1, 1) :- !. voisin_superieur(N, X, Y) :- (integer(X), X < N, N1 is N*(-1), (X > N1; X == N1), !, Y is X + 1); (integer(Y), (Y < N; Y == N), N1 is N*(-1), Y > N1, !, X is Y - 1). %%%%% %%%%% %%%%% %%%%% %%%%% /* 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(N, [Xd, Yd], est, [Xa, Yd]) :- voisin_superieur(N, Xd, Xa). case_voisine(N, [Xd, Yd], sudEst, [Xa, Ya]) :- voisin_superieur(N, Xd, Xa), voisin_superieur(N, Ya, Yd). case_voisine(N, [Xd, Yd], sud, [Xd, Ya]) :- voisin_superieur(N, Ya, Yd). case_voisine(N, [Xd, Yd], sudOuest, [Xa, Ya]) :- voisin_superieur(N, Xa, Xd), voisin_superieur(N, Ya, Yd). case_voisine(N, [Xd, Yd], ouest, [Xa, Yd]) :- voisin_superieur(N, Xa, Xd). case_voisine(N, [Xd, Yd], nordOuest, [Xa, Ya]) :- voisin_superieur(N, Xa, Xd), voisin_superieur(N, Yd, Ya). case_voisine(N, [Xd, Yd], nord, [Xd, Ya]) :- voisin_superieur(N, Yd, Ya). case_voisine(N, [Xd, Yd], nordEst, [Xa, Ya]) :- voisin_superieur(N, Xd, Xa), voisin_superieur(N, 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é]] */ %%%%% %%%%% %%%%% %%%%% %%%%% tous_coups_legaux(Couleur, [N, PN, PB], R) :- pions_joueur(Couleur, [N, PN, PB], Pions), couleur_adversaire(Couleur, CAdverse), pions_joueur(CAdverse, [N, PN, PB], PAdverse), tous_coups_legaux1(Couleur, N, Pions, PAdverse, PAdverse, [], [], R). %%%%% %%%%% %%%%% %%%%% %%%%% /* 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, N, P, PA, [X|Y], T, A, R) :- coups_legaux(N, X, P, PA, T, [], T1, R1), append(R1, A, A1), tous_coups_legaux1(Couleur, N, 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(N, Case, P, PA, T, A, TR, R) :- case_voisine(N, Case, _, NCase), not(member(NCase, T)), !, jetons_retournes(N, NCase, P, PA, [], [], R1), (not_empty(R1)-> coups_legaux(N, Case, P, PA, [NCase|T], [[NCase,R1]|A], TR, R); coups_legaux(N, 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(N, Case, P, PA, T, A, R) :- not(member(Case, P)), not(member(Case, PA)), case_voisine(N, Case, Direction, NCase), member(NCase, PA), not(member(NCase, T)), sandwich(N, NCase, Direction, P, PA, [NCase], R1), !, append(R1, A, A1), jetons_retournes(N, 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. */ %%%%% %%%%% %%%%% %%%%% %%%%% sandwich(_, _, _, [], [], A, A) :- !. sandwich(N, Case, Direction, P, PA, A, R) :- case_voisine(N, Case, Direction, NCase), (member(NCase, PA)-> (!, sandwich(N, NCase, Direction, P, PA, [NCase|A], R)); (!, member(NCase, P), !, sandwich(N, Case, Direction, [], [], A, R))). %%%%% %%%%% %%%%% %%%%% %%%%% /* Sélectionne aléatoirement un coup à jouer parmi tous les coup 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). %%%%% %%%%% %%%%% %%%%% %%%%% /* 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, [N, PN, PB], Pions, [N, PNB, PB]) :- append(Pions, PN, PNB). ajouter_pions(blanc, [N, PN, PB], Pions, [N, 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, [N, PN, PB], Pions, [N, PNB, PB]) :- delete_multiple(PN, Pions, PNB). retirer_pions(blanc, [N, PN, PB], Pions, [N, 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). %%%%% %%%%% %%%%% %%%%% %%%%% /* Joue une partie avec 2 joueurs aléatoires tant qu'il existe un coup légal. */ %%%%% %%%%% %%%%% %%%%% %%%%% joue_partie(2, _, [N, PN, PB], [N, 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(_, Couleur, Plateau, NPlateau) :- write('Au tour de '), write(Couleur), write(' de jouer:\n'), afficher_plateau(Plateau), select_coup(Couleur, Plateau, Coup), joue_coup(Couleur, Plateau, Coup, NPlateau1), couleur_adversaire(Couleur, CAdverse), !, joue_partie(0, CAdverse, NPlateau1, NPlateau). joue_partie(NbPasses, Couleur, Plateau, NPlateau) :- !, NbPasses2 is NbPasses + 1, couleur_adversaire(Couleur, CAdverse), joue_partie(NbPasses2, CAdverse, Plateau, NPlateau). %%%%% %%%%% %%%%% %%%%% %%%%% /* Affiche le plateau de jeu reçu. */ %%%%% %%%%% %%%%% %%%%% %%%%% afficher_plateau([N, PN, PB]) :- N1 is N*(-1), afficher_plateau1(N1, N, [N, PN, PB]). %%%%% %%%%% %%%%% %%%%% %%%%% /* 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=-N et L=N. */ %%%%% %%%%% %%%%% %%%%% %%%%% % Fin de l'affichage. afficher_plateau1(N, N1, [N, PN, PB]) :- N1 is N*(-1), !, afficher_case([N, N1], PN, PB), write('\n\n'). % Affichage d'une case en fin de colonne => saut de ligne. afficher_plateau1(N, L, [N, PN, PB]) :- afficher_case([N, L], PN, PB), write('\n'), voisin_superieur(N, L1, L), !, C1 is N*(-1), afficher_plateau1(C1, L1, [N, PN, PB]). % Affichage d'une case normale. afficher_plateau1(C, L, [N, PN, PB]) :- afficher_case([C, L], PN, PB), voisin_superieur(N, C, C1), afficher_plateau1(C1, L, [N, PN, PB]). %%%%% %%%%% %%%%% %%%%% %%%%% /* 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('_'))). %%%%% %%%%% %%%%% %%%%% %%%%% % trace, init_plateau(3, P), afficher_plateau(P). % trace, init_plateau(3, P), tous_coups_legaux(noir, P, R). % init_plateau(3, P), joue_partie(noir, P, NP).