# dilemme du prisonnier itéré
# version tournoi
# janvier 2017

from random import choice, randint, seed

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'


# Coyote
# Compte le nombre de trahisons et punit de plus en plus fort

etat_coyote = 0
# coyote peut être dans trois états: 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'


#LePLusBeauGosse

def bastien(liste_lui,liste_moi):
    if len(liste_lui)>2:
        if liste_lui.count('T'):
            return 'T'
        else:
            return 'C'
    else:
        return 'C'

#Justine
#Coopère seulement si le score est supérieure à celui de l'adversaire
    
def justine(liste_lui,liste_moi):
    score_lui = 0
    score_moi = 0
    
    if len(liste_lui)>0:
        for x in range(len(liste_lui)):
            if liste_lui[x] == 'T' and liste_moi[x] == 'T':
                score_lui += 1
                score_moi += 1
            elif liste_lui[x] == 'C' and liste_moi[x] == 'T':
                score_moi += 5
            elif liste_lui[x] == 'T' and liste_moi[x] == 'C':
                score_lui += 5
            elif liste_lui[x] == 'C' and liste_moi[x] == 'C':
                score_lui += 3
                score_moi += 3

    if score_lui>= score_moi:
        return 'T'
    else:
        return 'C'


traitor = 0 #0 = ne sait pas; 1 = ne doit pas trahir; 2 = peut trahir
#cherche a s'adapter à la strategie adversaire,en definissant son pattern
def noe(a, p):
    global traitor
    if len(a) == 0:#si premier tour
        traitor=0#reinitialise traitor
    try :
        if len(a) < 10:#pour le dix premierscoups
            if a.count('T') /len(a) > 0.9: #ennemi traitre
                return 'T'
            elif a.count('C')/len(a) > 0.9:#ennemi cooperatif
                return 'C'
            elif 0.65 > a.count('T')/len(a) > 0.35 :#considere l'ennemi aleatoire
                return choice(['T', 'C'])

        #coups suivants...
        
        elif 0.65 > a.count('T')/len(a) > 0.35:#ennemi aleatoire
            return choice(['T', 'C'])
        else:
            strat = []
            for x in range(10, 0, -1):#recupere les derniers coups de l'adversaire
                strat.append(a[-x])
            if strat.count('C') == 9 and strat[-1] == 'T' and traitor == 0:#ennemi passe en tjr coop
                traitor = 1
            elif traitor == 1 and strat[-1]=='T' and strat[-2]=='T' and strat[-3]=='T' and strat[-4]=='T':#finalement pas
                traitor = 2 
            if (strat.count('T') /len(strat) > 0.8 or strat.count('C')/len(strat) > 0.9) and traitor != 1: #considere l'ennemi con
                return 'T'
            elif 0.65 > strat.count('T')/len(strat) > 0.35 :#ennemi aleatoire sur ses derniers coups
                return choice(['T', 'C'])
        
        return 'C'
    except ZeroDivisionError:#Oui, je sais qu'on peut le faire sans try-except,
                             #mais c'est mon algo donc je fais ce que je veux
        return 'C'#premier coup


#Lisa
def lisa(liste_moi, liste_lui):

    # premier tour, coopérer
    if len(liste_moi) == 0:
        return 'C'

    # si son nombre total de trahison est plus grand que 3 
    # tjs trahir
    if liste_lui.count('T') >= 3:
        return 'T'

    # si aucune de ces conditions
    # coopérer
    return 'C'


#Léane
#Renvoie le coups qu'à fait en majorité l'adversaire dans les trois derniers coups
#Pour le deuxième coup, renvoie ce que l'adversaire a renvoyé au premier
#Coopère au premier coup
    
def leane(liste_lui, liste_moi):
    if len(liste_lui) > 2:
        liste_lui_3_derniers = []
        liste_lui_3_derniers.append(liste_lui[-1])
        liste_lui_3_derniers.append(liste_lui[-2])
        liste_lui_3_derniers.append(liste_lui[-3])
        if liste_lui_3_derniers.count('T') >= len(liste_lui_3_derniers)//2:
            return 'T'
        else :
            return 'C'
    elif len(liste_lui) == 1 :
        return liste_lui[-1]
    else :
        return 'C'

#Loris
def loris(liste_lui,liste_moi) :
    y=0
    z=0
    x=len(liste_lui)
    if x>=3 :
        if liste_lui[x-3] == 'C' :
            y+=1
        else :
            z+=1
        if liste_lui[x-2] == 'C' :
            y+=1
        else :
            z+=1
        if liste_lui[x-1] == 'C' :
            y+=1
        else :
            z+=1
    elif x==2:
        return 'T'
    elif x==1:
        return 'T'
    else :
        return 'C'

    if y<z :
        return 'T'
    else :
        return 'C'

# Arnaud
def arnaud(liste_lui,liste_moi):
    global progamer
    if len(liste_lui)==0:
        return 'C'
    if liste_lui[-1]== 'T':
        progamer+=1
    if progamer==0:
        return 'C'
    else:
        return'T'

# Le tournoi 2017

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['Coyote'] = []
liste['Bastien'] = []
liste['Justine'] = []
liste['Noe'] = []
liste['Lisa'] = []
liste['Leane'] = []
liste['Loris'] = []
liste['Arnaud'] = []


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['Coyote'] = lambda lui, moi : coyote(lui,moi)
strategie['Bastien'] = lambda lui, moi : bastien(lui,moi)
strategie['Justine'] = lambda lui, moi : justine(lui,moi)
strategie['Noe'] = lambda lui, moi : noe(lui,moi)
strategie['Lisa'] = lambda lui, moi : lisa(lui,moi)
strategie['Leane'] = lambda lui, moi : leane(lui,moi)
strategie['Loris'] = lambda lui, moi : loris(lui,moi)
strategie['Arnaud'] = lambda lui, moi : arnaud(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)
            etat_coyote = 0
            progamer=0
            # coyote peut être dans trois états: 0 (zen), 1 (punition), 2 (acceptation).
            nb_trahisons = 0
            duree_punition = 1
            duree_paix = 2
            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")

