Le tournoi d’Axelrod

La mission est d’implémenter le tournoi d’Axelrod première version présenté dans la vidéo ci-dessous.

Vous ferez concourir les stratégies suivantes en écrivant une fonction par stratégie :

  • AllC : coopère toujours.
  • AllD : trahit toujours.
  • Rando : joue au hasard.
  • Alt : alterne un coup sur deux.
  • TitforTat : coopère au premier coup puis joue le coup précédent de l’adversaire.
  • Grudger : commence par coopérer mais dès que l’autre trahit, trahit toujours.
  • Joss : commence par coopérer puis joue le coup précédent de l’adversaire sauf 10% du temps où il trahit.
  • TitforTwoTats : trahit si l’autre trahit deux fois de suite, coopère sinon.
  • TwoTitsforTat : coopère tant que l’autre coopère, mais à chaque trahison de l’adversaire, trahit deux fois consécutives.
  • Tester : commence par trahir. Si l’autre a coopéré au premier coup, Tester se repent et joue TitforTat pour la suite, sinon il joue Alt.
  • Eatherly : commence par trahir puis trahit en proportion du taux de trahison de l’autre jusqu’à ce coup.
  • Gofman : commence par coopérer. Si les deux joueurs ont joué différemment au coup précédent, trahit avec une probabilité de 5/7, et coopère s’ils ont joué la même chose.
  • Tullock : coopère les 10 premiers coups puis coopère à chaque coup 10% de moins que l’autre a coopéré sur les 10 coups qui précèdent.
  • Pavlov : commence par coopérer. Si l’autre a coopéré au dernier tour, on garde le même coup, sinon on change.

Donnez le palmares du tournoi après 200 parties.

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/mScpHTIi-kM?autoplay=0&controls=1&end=0&loop=0&mute=0&start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"
  ></iframe>
</div>


Une solution :

from random import random
import seaborn as sns
import matplotlib.pyplot as plt

Les différentes stratégies :

def TitforTat(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 1
    elif liste_coups_adverses[-1]:
        return 1
    else:
        return 0

def Grudger(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 1
    else:
        if liste_coups_joueur[-1] == 0:
            return 0
        elif liste_coups_adverses[-1] == 0:
            return 0
        else:
            return 1

def Joss(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 1
    else:
        if random()<0.1:
            return 0
        else:
            return liste_coups_adverses[-1]

def Rando(liste_coups_joueur,liste_coups_adverses):
    return int(random()*2)

def AllD(liste_coups_joueur,liste_coups_adverses):
    return 0

def AllC(liste_coups_joueur,liste_coups_adverses):
    return 1

def TitforTwoTats(liste_coups_joueur,liste_coups_adverses): # tit for 2 tats
    if len(liste_coups_adverses) < 2:
        return 1
    elif liste_coups_adverses[-1]+liste_coups_adverses[-2] == 0:
        return 0
    else:
        return 1
        
def TwoTitsforTat(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) < 1:
        return 1
    elif liste_coups_adverses[-1] == 0 or (len(liste_coups_adverses) > 1 and liste_coups_adverses[-2] == 0):
        return 0
    else:
        return 1

def Tester(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 0
    elif len(liste_coups_adverses) == 1:
        return liste_coups_adverses[-1]
    else:
        if liste_coups_adverses[1]: # tit for tat
            return liste_coups_adverses[-1]
        else: # alternance
            return (len(liste_coups_adverses)+1)%2

def Eatherly(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 0
    else:
        if random() < (len(liste_coups_adverses)-sum(liste_coups_adverses))/len(liste_coups_adverses):
            return 0
        else:
            return 1

def Alt(liste_coups_joueur,liste_coups_adverses):
    return (len(liste_coups_adverses)+1)%2

def Gofman(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 1
    else:
        if liste_coups_adverses[-1] != liste_coups_joueur[-1]:
            if random() < 2/7:
                return 1
            else:
                return 0
        else:
            return 1

def Tullock(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) < 11:
        return 1
    else:
        if random() < sum(liste_coups_adverses)/len(liste_coups_adverses)-0.1:
            return 1
        else:
            return 0
            
def Pavlov(liste_coups_joueur,liste_coups_adverses):
    if len(liste_coups_adverses) == 0:
        return 1
    else:
        if not liste_coups_adverses[-1]:
            return 1-liste_coups_joueur[-1]
        else:
            return liste_coups_joueur[-1]

Match entre deux joueurs :

def partie(joueurA,joueurB):
    nbre_parties = 200
    score_joueurA = 0
    score_joueurB = 0
    liste_coups_joueurA = []
    liste_coups_joueurB = []
    for i in range(nbre_parties):
        JA = dico_strategies[joueurA](liste_coups_joueurA,liste_coups_joueurB)
        JB = dico_strategies[joueurB](liste_coups_joueurB,liste_coups_joueurA)
        liste_coups_joueurA.append(JA)
        liste_coups_joueurB.append(JB)
        if JA:
            if JB:
                score_joueurA += 3
                score_joueurB += 3
            else:
                score_joueurA += 0
                score_joueurB += 5
        else:
            if JB:
                score_joueurA += 5
                score_joueurB += 0
            else:
                score_joueurA += 1
                score_joueurB += 1
    if joueurA == joueurB:
        dico_scores[joueurA] += score_joueurA
        matrice_tournoi[liste_joueurs.index(joueurA)][liste_joueurs.index(joueurA)] += score_joueurA
    else:
        dico_scores[joueurA] += score_joueurA
        dico_scores[joueurB] += score_joueurB
        matrice_tournoi[liste_joueurs.index(joueurB)][liste_joueurs.index(joueurA)] += score_joueurB
        matrice_tournoi[liste_joueurs.index(joueurA)][liste_joueurs.index(joueurB)] += score_joueurA

Le tournoi (on fait s’affronter tous les joueurs y compris eux-mêmes et on recommence 50 fois) :

def tournoi():
    global partie
    nb_tournois = 100
    for i in range(nb_tournois):
        for j,JA in enumerate(liste_joueurs):
            for JB in liste_joueurs[j:]:
                partie(JA,JB)
    for i in range(len(matrice_tournoi)):
        for j in range(len(matrice_tournoi[0])):
            matrice_tournoi[i][j] = int(matrice_tournoi[i][j]/nb_tournois)
    for key in dico_scores:
        dico_scores[key] = int(dico_scores[key]/nb_tournois) 

Pour réinitialiser les scores entre deux tournois :

def raz():
    global liste_joueurs,liste_strategies,dico_strategies,dico_scores,matrice_tournoi
    liste_joueurs = ["TitforTat","Grudger","Joss","AllD","AllC","TitforTwoTats","Tester","Eatherly","Alt","Gofman","Tullock","Rando","TwoTitsforTat"]
    liste_strategies = [TitforTat,Grudger,Joss,AllD,AllC,TitforTwoTats,Tester,Eatherly,Alt,Gofman,Tullock,Rando,TwoTitsforTat]
    dico_strategies = {J:S for (J,S) in zip(liste_joueurs,liste_strategies)}
    dico_scores = {J:0 for J in liste_joueurs}
    matrice_tournoi = [[0 for _ in range(len(liste_joueurs))] for _ in range(len(liste_joueurs))]
raz()
tournoi()
liste_scores = [dico_scores[k] for k in dico_scores]
combo = list(zip(liste_scores,liste_joueurs))
combo.sort(reverse=True)
for s,j in combo:
    print(f"{j:17} : {s}")
TitforTwoTats     : 7333
Gofman            : 7132
TitforTat         : 7113
AllC              : 7076
TwoTitsforTat     : 7048
Grudger           : 6990
Pavlov            : 6957
Tullock           : 6467
Rando             : 5991
Alt               : 5962
Eatherly          : 5792
Joss              : 5775
AllD              : 5732
Tester            : 5664
plt.figure(figsize=(15,15),dpi=150)
s = sns.heatmap(matrice_tournoi, annot=True, fmt="d", cmap='YlGnBu', xticklabels=liste_joueurs, yticklabels=liste_joueurs)
plt.tick_params(axis='both', which='both', length=5)
s.xaxis.tick_top()
s.xaxis.set_label_position('top')
plt.xticks(rotation=90)

Scores:

Player A : 0

Player B : 0


Mission bonus :

Imaginer une nouvelle stratégie qui se hisse sur le podium du tournoi contre les autres stratégies déjà implémentées.