From 9ca3dca68bc717d33f9e1abcf07813dd3b63450c Mon Sep 17 00:00:00 2001 From: unknown <22013326@etu.unicaen.fr> Date: Mon, 10 Mar 2025 20:17:06 +0100 Subject: [PATCH] Tournoi OK --- TP1/Tournoi.py | 249 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 TP1/Tournoi.py diff --git a/TP1/Tournoi.py b/TP1/Tournoi.py new file mode 100644 index 0000000..551b336 --- /dev/null +++ b/TP1/Tournoi.py @@ -0,0 +1,249 @@ +import random + +class Prisonnier: + """ + Classe représentant un prisonnier avec une stratégie basée sur une probabilité d'avouer. + """ + def __init__(self, proba_avouer=0.5, nom="Prisonnier Aléatoire"): + """ + Constructeur de la classe Prisonnier. + + Args: + proba_avouer: probabilité d'avouer (par défaut 0.5 pour un joueur aléatoire uniforme) + nom: nom de la stratégie pour l'affichage + """ + self.proba_avouer = proba_avouer + self.historique_adversaire = [] + self.nom = nom + + def jouer(self): + """ + Méthode qui détermine l'action du joueur en fonction de sa probabilité d'avouer. + + Returns: + 'avoue' ou 'nie' selon la probabilité + """ + if random.random() < self.proba_avouer: + return "avoue" + else: + return "nie" + + def maj(self, coup): + """ + Méthode qui met à jour l'historique des coups joués par l'adversaire. + + Args: + coup: le coup joué par l'adversaire ('avoue' ou 'nie') + """ + self.historique_adversaire.append(coup) + + def reinitialiser(self): + """ + Méthode qui réinitialise l'historique des coups de l'adversaire. + """ + self.historique_adversaire = [] + + +class TitForTat(Prisonnier): + """ + Stratégie Tit for Tat (oeil pour oeil) : commence par coopérer (nier), + puis imite le dernier coup de l'adversaire. + """ + def __init__(self): + """ + Constructeur de la classe TitForTat. + """ + super().__init__(nom="TitForTat") # Appel du constructeur de la classe parente + + def jouer(self): + """ + Méthode qui détermine l'action du joueur selon la stratégie Tit for Tat. + + Returns: + 'avoue' ou 'nie' selon la stratégie + """ + # Si c'est le premier coup, on coopère (nie) + if not self.historique_adversaire: + return "nie" + # Sinon, on imite le dernier coup de l'adversaire + else: + return self.historique_adversaire[-1] + + +class Dummy(Prisonnier): + """ + Stratégie Dummy : toujours coopérer ou toujours trahir selon un paramètre. + """ + def __init__(self, coopere=True): + """ + Constructeur de la classe Dummy. + + Args: + coopere: si True, toujours coopérer (nier), sinon toujours trahir (avouer) + """ + nom = "Dummy (coopère)" if coopere else "Dummy (trahit)" + super().__init__(nom=nom) # Appel du constructeur de la classe parente + self.coopere = coopere + + def jouer(self): + """ + Méthode qui détermine l'action du joueur selon la stratégie Dummy. + + Returns: + 'avoue' ou 'nie' selon la stratégie + """ + if self.coopere: + return "nie" # Coopérer = nier + else: + return "avoue" # Trahir = avouer + + +def dilemme_prisonnier_itere(matrice_pertes, joueur1, joueur2, nb_tours=10, afficher_details=False): + """ + Fonction qui simule un dilemme du prisonnier itéré entre deux joueurs. + + Args: + matrice_pertes: dictionnaire des pertes + joueur1: premier joueur + joueur2: deuxième joueur + nb_tours: nombre de tours à jouer + afficher_details: si True, affiche les détails de chaque tour + + Returns: + tuple des scores totaux (score_joueur1, score_joueur2) + """ + # Réinitialisation des historiques + joueur1.reinitialiser() + joueur2.reinitialiser() + + score_joueur1 = 0 + score_joueur2 = 0 + + if afficher_details: + print(f"Dilemme du prisonnier itéré: {joueur1.nom} vs {joueur2.nom}") + print(f"Nombre de tours: {nb_tours}") + print("-" * 50) + + for tour in range(nb_tours): + # Les joueurs choisissent leur action + action_joueur1 = joueur1.jouer() + action_joueur2 = joueur2.jouer() + + # On calcule les pertes pour ce tour + perte_joueur1, perte_joueur2 = matrice_pertes[(action_joueur1, action_joueur2)] + + # On met à jour les scores (négatif des pertes) + score_joueur1 -= perte_joueur1 + score_joueur2 -= perte_joueur2 + + # On met à jour l'historique des joueurs + joueur1.maj(action_joueur2) + joueur2.maj(action_joueur1) + + # Affichage du tour si demandé + if afficher_details: + print(f"Tour {tour+1}: {joueur1.nom} joue {action_joueur1}, {joueur2.nom} joue {action_joueur2}") + print(f"Pertes: {joueur1.nom} = {perte_joueur1}, {joueur2.nom} = {perte_joueur2}") + print(f"Scores cumulés: {joueur1.nom} = {score_joueur1}, {joueur2.nom} = {score_joueur2}\n") + + if afficher_details: + print("Résultat final:") + print(f"Score {joueur1.nom}: {score_joueur1}") + print(f"Score {joueur2.nom}: {score_joueur2}") + + if score_joueur1 > score_joueur2: + print(f"{joueur1.nom} gagne!") + elif score_joueur2 > score_joueur1: + print(f"{joueur2.nom} gagne!") + else: + print("Match nul!") + print("-" * 50) + + return score_joueur1, score_joueur2 + + +def organiser_tournoi_simple(matrice_pertes, joueurs, nb_tours=50): + """ + Fonction qui organise un tournoi simple entre différentes stratégies. + + Args: + matrice_pertes: dictionnaire des pertes + joueurs: liste des joueurs + nb_tours: nombre de tours par partie + + Returns: + dictionnaire des scores totaux par joueur + """ + scores = {joueur.nom: 0 for joueur in joueurs} + nb_matchs = {joueur.nom: 0 for joueur in joueurs} + + print("\nTournoi sur la matrice de pertes") + print("=" * 40) + + # Chaque joueur affronte tous les autres joueurs + for i, joueur1 in enumerate(joueurs): + for j, joueur2 in enumerate(joueurs): + if i != j: # Un joueur ne s'affronte pas lui-même + score1, score2 = dilemme_prisonnier_itere(matrice_pertes, joueur1, joueur2, nb_tours=nb_tours) + + # On met à jour les scores totaux + scores[joueur1.nom] += score1 + scores[joueur2.nom] += score2 + + # On met à jour le nombre de matchs + nb_matchs[joueur1.nom] += 1 + nb_matchs[joueur2.nom] += 1 + + # On affiche les résultats de cette confrontation + print(f"{joueur1.nom} vs {joueur2.nom}: {score1} - {score2}") + + # On calcule les scores moyens + scores_moyens = {nom: scores[nom] / nb_matchs[nom] for nom in scores} + + # On trie les joueurs par score moyen décroissant + classement = sorted(scores_moyens.items(), key=lambda x: x[1], reverse=True) + + print("\nClassement final:") + for rang, (nom, score) in enumerate(classement, 1): + print(f"{rang}. {nom}: {score:.1f}") + + return scores_moyens + + +# Main pour tester le code +if __name__ == "__main__": + # Définition des actions possibles + actions = ["avoue", "nie"] + + # Représentation des pertes (peines) par un dictionnaire + pertes = { + ("avoue", "avoue"): (3, 3), + ("avoue", "nie"): (0, 10), + ("nie", "avoue"): (10, 0), + ("nie", "nie"): (1, 1) + } + + print("Dilemme du Prisonnier Itéré") + print("===========================") + + # Création des joueurs avec différentes stratégies + joueurs = [ + TitForTat(), + Dummy(coopere=True), + Dummy(coopere=False), + Prisonnier(proba_avouer=0.5), + Prisonnier(proba_avouer=0.3, nom="Prisonnier (p=0.3)") + ] + + # Test de différentes combinaisons de stratégies + print("\nTest de différentes combinaisons de stratégies") + print("-------------------------------------------") + + # TitForTat vs Dummy (trahit) + dilemme_prisonnier_itere(pertes, joueurs[0], joueurs[2], nb_tours=10, afficher_details=True) + + # TitForTat vs Dummy (coopère) + dilemme_prisonnier_itere(pertes, joueurs[0], joueurs[1], nb_tours=10, afficher_details=True) + + # Organisation d'un tournoi simple + organiser_tournoi_simple(pertes, joueurs, nb_tours=50) \ No newline at end of file -- GitLab