# dilemme du prisonnier itéré
# version tournoi
# janvier 2015

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'


# quentin

def quentin(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if gain(liste_moi[-1],liste_lui[-1]) < gain(liste_lui[-1],liste_moi[-1]):
            return liste_moi[-1]
        else:
            return liste_lui[-1]
    else:  # premier coup
        return 'C'

# Raphaël
# Sans queue ni tête
# Je ne sais pas trop comment ça marche...

def raphael(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if liste_lui.count('T') > len(liste_lui)//2:
            return 'T'
        else:
            return 'C'
    else: # 1er coup
        return 'C'

# Thierry

def thierry(liste_lui,liste_moi):
    if len(liste_lui)>0:
        return liste_lui[0]
    else: 
        return 'C'


# Sylvain
# Reproduit l'avant-dernier coup du joueur adverse. 

def sylvain(liste_lui,liste_moi):
    if len(liste_lui)>1:
        return liste_lui[-2]
    else:  # premier coup
        return 'C'

# Tobias
# Ne marche pas...


# Maren

def maren(liste_lui,liste_moi):
    if len(liste_lui)>2:
        return liste_lui[-3]
    else:
        return 'C'

# Dominik

def dominik(liste_lui,liste_moi):
    if len(liste_lui)>=2:
        return liste_lui[-2]
    else:
        return 'C'

# Jonas

def jonas(liste_lui,liste_moi):
    if len(liste_moi)>0:
        return liste_lui[0]
    else:
        return 'C'

# Matteo
# regarde si l'autre joueur coopère ou trahi, mais en le trahissant beaucoup s'il trahi un petit peu.

def matteo(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if liste_lui.count('C') > len(liste_lui)//2:
            return 'C'
        elif liste_lui.count('T') > len(liste_lui)//4:
            return 'T'
        else:
            return 'T'
    else:
        return 'C'

# Mathilde
# coopère dans 60 % des cas

from random import random

def mathilde(listeLui, listeMoi):
    if random() <= 0.6:
        return "C"
    else:
        return "T"

# Sophie
# Coopère sauf si l'autre m'a trahi deux fois de suite

def sophie(liste_lui,liste_moi):
    if liste_lui.count('T'):
        if liste_lui.count('T'):
            return 'T'
        else:
            return 'C'
    else:
        return 'C'

# Mélissa

def melissa(liste_lui, liste_moi):
    if liste_lui.count('T'):
        return 'T'
        if random() < 0.25:
            return 'T'
        else:
            return 'C'
    else:
        return 'C'

# Marcus
# D'abord 2 fois trahir
# Après, il joue la minorité de l'opposant. Si la quantité (de l'opposant) de T = la quantité de C, on continue par C

def marcus(liste_lui,liste_moi):
    if len(liste_lui)>1:
        if liste_lui.count('C') > len(liste_lui)//2:
            return 'T'
        else:
            return 'C'
    else:  
        return 'T'


# Catherine

def catherine(liste_lui, liste_moi):
    if len(liste_lui)% 3 ==0 :
        if liste_lui.count('C') > len(liste_lui)//2:
            return 'T'
        else:
            return 'C'
    elif 0 < len (liste_lui) < 3:
        return liste_lui[-1]
    elif len (liste_lui)== 0:
        return 'T'
    else:
        global choix
        return choice(choix)


# Natalie

def natalie(liste_lui,liste_moi):
    if len(liste_lui)>1:
        if random()>0.66:
            return 'T'
        else:
            return 'C'
    else:
        return 'C'


# Adrian
# Ne marche que si nb_total_coups < 2

def adrian(liste_lui,liste_moi):
    if len(liste_lui) == 0:
        return 'C'
    elif len(liste_lui) >0 and len(liste_lui)<(nb_total_coups/2): #jusqu'Ã la moitiÃ© des coups
        # print(liste_lui[-1])
        return liste_lui[-1]
    elif len(liste_lui) >=(nb_total_coups/2): #l'autre moitiÃ© des coups
        # print(liste_lui[-3])
        return liste_lui[-2]
    else:
        return 'C' #au cas où un cas ne serait pas défini -->pas utilisé = tous les cas sont définis


# Célien
# essaie de contrer tous les types de joueurs adverses

def celien(liste_lui,liste_moi):
    if len(liste_lui)>0:
        if liste_lui.count('C') == len(liste_lui):
            return 'T'
        elif liste_lui.count('C') >= len(liste_lui)//2:
            return 'C'
        elif liste_lui.count('T') >= len(liste_lui)//2:
            return 'T'
    else :
        return 'C'


# Yann

def yann(liste_lui, liste_moi):
    global nb_coups, nb_total_coups

    if nb_coups <= nb_total_coups//2:        
        if len(liste_lui) > 0:
            if liste_lui [len(liste_lui)-1] == liste_moi[len(liste_moi)-1]:
                return 'C'
            else:
                return 'T'
        else:
            return 'C'

    elif nb_coups <= nb_total_coups - 100 and nb_coups > nb_total_coups //2 :
        return 'T'

    else:
        return liste_lui[-1]

    
# Le tournoi 2015

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['Quentin'] = []
liste['Raphaël'] = []
liste['Thierry'] = []
liste['Sylvain'] = []
liste['Maren'] = []
liste['Dominik'] = []
liste['Jonas'] = []
liste['Matteo'] = []
liste['Mathilde'] = []
liste['Sophie'] = []
liste['Mélissa'] = []
liste['Marcus'] = []
liste['Catherine'] = []
liste['Natalie'] = []
liste['Adrian'] = []
liste['Célien'] = []
liste['Yann'] = []

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['Quentin'] = lambda lui, moi : quentin(lui,moi)
strategie['Raphaël'] = lambda lui, moi : raphael(lui,moi)
strategie['Thierry'] = lambda lui, moi : thierry(lui,moi)
strategie['Sylvain'] = lambda lui, moi : sylvain(lui,moi)
strategie['Maren'] = lambda lui, moi : maren(lui,moi)
strategie['Dominik'] = lambda lui, moi : dominik(lui,moi)
strategie['Jonas'] = lambda lui, moi : jonas(lui,moi)
strategie['Matteo'] = lambda lui, moi : matteo(lui,moi)
strategie['Mathilde'] = lambda lui, moi : mathilde(lui,moi)
strategie['Sophie'] = lambda lui, moi : sophie(lui,moi)
strategie['Mélissa'] = lambda lui, moi : melissa(lui,moi)
strategie['Marcus'] = lambda lui, moi : marcus(lui,moi)
strategie['Catherine'] = lambda lui, moi : catherine(lui,moi)
strategie['Natalie'] = lambda lui, moi : natalie(lui,moi)
strategie['Adrian'] = lambda lui, moi : adrian(lui,moi)
strategie['Célien'] = lambda lui, moi : celien(lui,moi)
strategie['Yann'] = lambda lui, moi : yann(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
            # 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")

