# dilemme du prisonnier itéré
# version tournoi

from random import *

choix = ['T','C']  # T : trahit, C : coopère

def gain(lui,moi):
    if lui=='C' and moi=='C':
        return 3
    elif lui=='C' and moi=='T':
        return 5
    elif lui=='T' and moi=='C':
        return 0
    elif lui=='T' and moi=='T':
        return 1


# Toujours seul
# ne coopère jamais

def toujours_seul(liste_lui,liste_moi):
    return 'T'

    
# Bonne poire
# coopère toujours

def bonne_poire(liste_lui,liste_moi):
    return 'C'

    
# Aléatoire
# joue avec une probabilité égale 'T' ou 'C'

def aleatoire(liste_lui,liste_moi):
    global choix
    return choice(choix)

    
# Donnant donnant
# coopère seulement si l'autre joueur a coopéré au coup précédent.

def donnant_donnant(liste_lui,liste_moi):
    if len(liste_lui)>0:
        return liste_lui[-1]
    else:  # premier coup
        return 'C'


# Majorité
# coopère seulement si l'autre joueur a coopéré en majorité.

def majorite(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if liste_lui.count('C') > len(liste_lui)//2:
            return 'C'
        else:
            return 'T'
    else:  # premier coup
        return 'C'


# Ajouter les stratégies des joueurs ici

#Raphael
#Coopère jusqu'a lautre joueur trahi deux foix, fini les deux derniers coups de la partie en trahissant.
def raphael(liste_lui,liste_moi):    
    # if nb_coups >= nb_total_coups-2:     inconnu !!!!!!!!!
    #    return 'T'
    if liste_lui.count('T')>=2:
        return 'T'
    else:
        return 'C'

# Oscar
# coopere les 5 premiers coups, puis coopere si l'autre joueur a coopere les 5 derniers coups
def oscar(liste_lui,liste_moi):
    if len(liste_lui)<=5:
        return 'C'
    elif liste_lui[-5:].count('C')==5:
        return 'C'
    else :
        return 'T'

# Matteo
# coopère toujours jusqu'à ce qu'il trahisse puis trahir jusqu'à la fin
nb_trahison = 0
def matteo(liste_lui,liste_moi):
    global nb_trahison
    if len(liste_lui) > 0:
        if liste_lui[-1]=='T':
            nb_trahison+=1
    if nb_trahison > 0:
        return 'T'
    elif nb_trahison == 0:
        return 'C'

# Axel
# Coopère jusqu'à ce que l'autre trahi deux fois de suite, après quoi trahi jusqu'à la fin.
def axel(liste_lui,liste_moi):
    coup = 'C'
    if len(liste_lui)>1:
        if liste_lui[-1]=='T' and liste_lui[-2]=='T':
            coup = 'T'
    return coup

# Louis
# Coopère jusqu'à trahision
def louis(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if liste_lui.count('T') >= 1: #point faible : trop fort
            return 'T'
        else:
            return 'C'
    else: #premier coup
        return 'C'

# Loris R
def loris(liste_lui,liste_moi):
    dico = {}
    dico['T'] = 1
    dico['C'] = 2
    if len(liste_lui) > 0:
        if dico[liste_lui[-1]] == dico[liste_moi[-1]]:
            if dico[liste_lui[-1]] == 1:
                return 'T'
            elif dico[liste_lui[-1]] == 2:
                return 'C'
        elif dico[liste_lui[-1]] < dico[liste_moi[-1]]:
            return 'T'
        else:
            if liste_lui[-2] != liste_moi[-2]:
                return 'T'
            else:
                return 'C'
    else: 
        return 'C'

# Coyote
# Compte le nombre de trahisons et punit de plus en plus fort
etat_coyote = 0
# coyote peut etre dans trois etats: 0 (zen), 1 (punition), 2 (acceptation).
nb_trahisons = 0
duree_punition = 1
duree_paix = 2
def coyote(liste_lui,liste_moi):
    global etat_coyote, nb_trahisons, duree_punition, duree_paix
    if len(liste_lui)>2:
        if liste_lui[-1]=='T' and etat_coyote==0:
            etat_coyote = 1
            nb_trahisons += 1
            duree_punition = 3*nb_trahisons
        if etat_coyote == 1:
            duree_punition -= 1
            if duree_punition==0:
                etat_coyote = 2
            return 'T'
        if etat_coyote == 2:
            if duree_paix>0:
                duree_paix -= 1
            else:
                etat_coyote = 0
                duree_paix = 2
            return 'C'
        return 'C'
    else:  # 3 premiers coups
        return 'C'
    
#Corentin
#Donnant-Donnant dans 70%, Trahir dans 20% des cas et Coopérer dans 10% des cas
def corentin(liste_lui, liste_moi):
    if liste_lui.count('T')==0:
        return 'C'
    else:
        if randint(1,100)<=70:
            return liste_lui[-1]
        elif randint(1,100)<=90:
            return 'T'
        else:
            return 'C'

#Névé
#trahit si l'adversaire a trahi deux fois ou plus (© Kiril) et durant les 3 derniers coups
#coopère le reste du temps
def névé(liste_lui, liste_moi):
    #if len(liste_moi) >= (nb_total_coups-3):   inconnu !!!!!!!!!
    #    return 'T'
    if liste_lui.count('T') >= 2:
        return 'T'
    else:
        return 'C'

# Gael
# coopère les 2 premiers coups, puis coopère jusqu'à ce que l'adversaire trahit
# puis trahit jusqu'à la fin.
def Gael(liste_lui,liste_moi):
    if len(liste_lui)>1:
         if liste_lui[-1]== 'T' or liste_moi[-1]== 'T':
             return 'T'
         else:
             return 'C'
    else:  
        return 'C'

#Antoine
#Coopère d'abord, trahit deux fois si l'adversaire trahit une fois et trahit tout le temps si l'adversaire trahit plus de 5 fois
def antoine(liste_lui, liste_moi):
    if len(liste_lui)>0 and liste_lui[-1]=='T':
        return 'T'
    elif liste_lui.count('T')>=5:
        return 'T'
    elif len(liste_moi)>1 and liste_moi[-1]=='T' and liste_moi[-2]=='C':
        return 'T'
    else :  
        return 'C'

# Steven
# Le dromadaire retourné 
def steven(liste_lui,liste_moi):
    if len(liste_lui)>1:
        return liste_lui[-2]
    else:  # premier coup
        return 'C'

# tristan aka la technique du renard flamboyant
# coopère toujours
def tristan(liste_lui,liste_moi):
    return 'C'

#Christophe
# s'il trahi 3 fois --> trahir tout le temps
def Christophe(lui,moi):
    if lui.count("T")>2:
        return "T"
    else:
        return "C"

#Arthur
#coopère au premier coup, joue le premier coup de l'adversaire au second tour,
#coopère au troisième à moins que l'adversaire ait toujours trahit. Si le coup
#joué est un nombre premier, que l'adversaire a coopéré plus de trois fois et
# qu'il a coopéré au dernier tour, coopérer. Si il a trahit au dernier tour,
#jouer aléatoirement. Sinon, jouer le coup précédent de l'adversaire.

from math import sqrt
# Crééer une liste de n+1 éléments 1 (leurs indices vont de 0 à  n) :
n = 5000
n += 1
liste = [1]*n
premiers = []
# Parcourir la liste à partir de l'élément d'indice 2:
for i in range(2,int(sqrt(n))+1):
    if liste[i]==1:
        # Mettre à  zéro les éléments dont l'indice est multiple de i :
        for j in range(i*2, n, i):
            liste[j] = 0
# Afficher les indices des éléments restés à 1 (on ignore les éléments 0 et 1) :
for i in range(2,n):
    if liste[i]==1:
##        print(i, end=' ')
        premiers.append(i)

def arthur(liste_lui,liste_moi):
    global a
    if len(liste_lui)==0:
        return 'C'
    else:
        if len(liste_lui)==1:
            return liste_lui[-1]
        elif len(liste_lui)==2:
            if liste_lui[-2]=='T' and liste_lui[-1]=='T':
                return 'T'
            else:
                return 'C'
        elif len(liste_lui)==3:
            if liste_lui.count('T') == 3:
                    return 'T'
            else:
                return 'C'
        elif len(liste_lui) in premiers and liste_lui.count('C') >= 3:
            if liste_lui[-1]=='C':
                return 'C'
            elif liste_lui[-1]=='T':
                w=randint(1,2)
                if w==1:
                    return 'C'
                else:
                    return 'T'
        else:
            return liste_lui[-1]

# Le tournoi

liste = {}
strategie = {}
score = {}
duel = {}

# ajouter des joueurs ci-dessous, selon les modèles des joueurs existants
# commencer ici
liste['Toujours seul'] = []
liste['Bonne poire'] = []
liste['Majorité'] = []
liste['Aléatoire'] = []
liste['Donnant donnant'] = []
liste['Raphael'] = []
liste['Oscar'] = []
liste['Matteo'] = []
liste['Axel'] = []
liste['Louis'] = []
liste['Loris'] = []
liste['Coyote'] = []
liste['Corentin'] = []
liste['Névé'] = []
liste['Gael'] = []
liste['Antoine'] = []
liste['Steven'] = []
liste['Tristan'] = []
liste['Christophe'] = []
liste['Arthur'] = []

strategie['Toujours seul'] = lambda lui, moi : toujours_seul(lui,moi)
strategie['Bonne poire'] = lambda lui, moi : bonne_poire(lui,moi)
strategie['Majorité'] = lambda lui, moi : majorite(lui,moi)
strategie['Aléatoire'] = lambda lui, moi : aleatoire(lui,moi)
strategie['Donnant donnant'] = lambda lui, moi : donnant_donnant(lui,moi)
strategie['Raphael'] = lambda lui, moi : raphael(lui,moi)
strategie['Oscar'] = lambda lui, moi : oscar(lui,moi)
strategie['Matteo'] = lambda lui, moi : matteo(lui,moi)
strategie['Axel'] = lambda lui, moi : axel(lui,moi)
strategie['Louis'] = lambda lui, moi : louis(lui,moi)
strategie['Loris'] = lambda lui, moi : loris(lui,moi)
strategie['Coyote'] = lambda lui, moi : coyote(lui,moi)
strategie['Corentin'] = lambda lui, moi : corentin(lui,moi)
strategie['Névé'] = lambda lui, moi : névé(lui,moi)
strategie['Gael'] = lambda lui, moi : Gael(lui,moi)
strategie['Antoine'] = lambda lui, moi : antoine(lui,moi)
strategie['Steven'] = lambda lui, moi : steven(lui,moi)
strategie['Tristan'] = lambda lui, moi : tristan(lui,moi)
strategie['Christophe'] = lambda lui, moi : Christophe(lui,moi)
strategie['Arthur'] = lambda lui, moi : arthur(lui,moi)

nb_total_coups = int(input("Nombre total de coups par duel (200-5000) ? "))
germe = int(input("Germe du générateur de nombres pseudo-aléatoires ? "))
print()

for joueur in liste.keys():
    score[joueur] = 0

for i in liste.keys():  # i et j sont les joueurs
    for j in liste.keys() :
        liste[i] = []   # on recommence une partie
        liste[j] = []
        if i>=j:
            nb_coups = 0
            score_joueur1 = 0
            score_joueur2 = 0
            seed(germe) # germe du générateur aléatoire
            etat_coyote = 0
            # coyote peut être dans trois états: 0 (zen), 1 (punition), 2 (acceptation).
            nb_trahisons = 0
            duree_punition = 1
            duree_paix = 2
            nb_trahison = 0
            while nb_coups < nb_total_coups :
                coup_joueur1 = strategie[i](liste[j],liste[i])
                coup_joueur2 = strategie[j](liste[i],liste[j])
                liste[i].append(coup_joueur1)
                if i!=j:
                    liste[j].append(coup_joueur2)
                score_joueur1 += gain(coup_joueur2,coup_joueur1)
                score_joueur2 += gain(coup_joueur1,coup_joueur2)
                nb_coups += 1
            duel[(i,j)] = score_joueur1
            if i!=j:
                duel[(j,i)] = score_joueur2
            score[i] += score_joueur1
            if i!=j:
                score[j] += score_joueur2

# affichage des résultats

def trie_par_valeur(d):
    #retourne une liste de tuples triée selon les valeurs
    return sorted(d.items(), key=lambda x: x[1])

def trie_par_cle(d):
    #retourne une liste de tuples triée selon les clés
    return sorted(d.items(), key=lambda x: x[0])

score_trie = trie_par_valeur(score)
score_trie.reverse()
for i in range(0,len(score_trie)):
    print(score_trie[i][0],":",score_trie[i][1])
print()
duel_trie = trie_par_cle(duel)
for i in range(0,len(duel_trie)):
    print(duel_trie[i][0][0],"contre",duel_trie[i][0][1],"gagne",duel_trie[i][1],"pts")

