# -*- coding: utf-8 -*-
# Auteur: Cathelin gaetan

from random import shuffle
from time import sleep

class Domino:
    def __init__(self, A, B):
        """constructeur"""
        self.A = A
        self.B = B
        
    def __str__(self):
        """
        Permet de renvoyer le domino

        Returns
        -------
        str
            Le domino sous la forme suivante : [A | B]

        """
        return f"[{str(self.A)} | {str(self.B)}]"
    
    def valeur(self):
        """
        Permet de calculer la somme des points du domino

        Returns
        -------
        INT
            La somme des points des 2 côtés du domino.

        """
        return self.A + self.B
    
    def peut_placer_apres(self, domino):
        """
        Parameters
        ----------
        domino : Domino
            Domino après lequel on aimerait placer self.

        Returns
        -------
        BOOL
            Est-ce que domino a un côté qui est le même que le côté vacant de self

        """
        return domino.B == self.A or domino.B == self.B
    
    def peut_placer_avant(self, domino):
        """
        Parameters
        ----------
        domino : Domino
            Domino avant lequel on aimerait placer self.

        Returns
        -------
        BOOL
            Est-ce que domino a un côté qui est le même que le côté vacant de self

        """
        return domino.A == self.A or domino.A == self.B
    
    def permute(self):
        """Permet d'échanger A et B sur le domino"""
        self.A, self.B = self.B, self.A
        
    def plus_grand(self, domino):
        """
        Permet de savoir si self rapporte plus de points que le domino entré en argument

        Parameters
        ----------
        domino : Domino
            Domino comparé avec self.

        Returns
        -------
        BOOL
            Est-ce que self rapporte plus de points que domino

        """
        return self.valeur() >= domino.valeur()
    
    def __eq__(self, other):
        """
        Permet de savoir si self et other sont des dominors identiques

        Parameters
        ----------
        other : Domino
            Autre domino que l'on souhaite comparer avec self.

        Returns
        -------
        BOOL
            Est-ce que les 2 dominos sont identiques

        """
        return isinstance(other, Domino) and ((self.A == other.A and self.B == other.B) or (self.A == other.B and self.B == other.A))
    
    def est_double(self):
        """
        Permet de savoir si le domino est un double

        Returns
        -------
        BOOL
            Est-ce que A = B ?
        """
        return self.A == self.B

class Pioche:
    
    def __init__(self):
        """Constructeur"""
        self.jeu = []
        for A in range(7):
            for B in range(A, 7):
                self.jeu.append(Domino(A, B))
        shuffle(self.jeu)
        
    def nombre_dominos(self):
        """Returns :
            Un entier représentant le nombre de dominos dans le jeu"""
        return len(self.jeu)
    
    def piocher(self):
        """Renvoie le domino en haut de la pioche et le supprime de la pioche si celle-ci n'est pas vide"""
        if self.nombre_dominos != 0:
            return self.jeu.pop()
        
class Partie:
    
    def __init__(self):
        """Constructeur"""
        self.nombre_joueurs = int(input("====== BIENVENUE DANS CETTE PARTIE DE TRAIN MEXICAIN ======\nCombien de joueurs êtes-vous ? (Entrez un chiffre entre 2 et 6.)\n"))
        self.joueurs = []
        self.pioche = Pioche()
        for _ in range(self.nombre_joueurs):
            sleep(1)
            self.joueurs.append(Joueur(input("Entrez le nom du joueur : "), self))
        self.vainqueur = None
        
    def nombre_dominos_par_joueur(self):
        """Renvoie le nombre de dominos à distribuer à chaque joueur en fonction du nombre de joueurs"""
        nombre_doms = {
            2:7,
            3:6,
            4:6,
            5:4,
            6:4
            }
        return nombre_doms[self.nombre_joueurs]
    
    def chercher_plus_grand_double(self):
        """Cette méthode renvoie l'index du joueur ayant le plus grand double. Si personne n'a de double dans son jeu, elle renvoie le premier joueur de la liste."""
        for double in range(6, -1, -1) :
            for index, joueur in enumerate(self.joueurs) :
                for piece in joueur.jeu :
                    if piece.est_double() and piece.A == double :
                        return index
        return 0
    
    def lancer_partie(self):
        """Cette fonction permet de lancer la partie self et d'enchaîner les tours jusqu'à ce qu'il y ait un gagnant ou que tout le monde soit éliminé."""
        tour = self.chercher_plus_grand_double()
        while self.vainqueur == None:
            if self.joueurs == []:
                print("Tous les joueurs ont été éliminés, il n'y a aucun gagnant.")
                break
            self.tour = Tour(self.joueurs[tour % self.nombre_joueurs])
            if self.tour.joueur.bloque :
                del self.joueurs[tour % self.nombre_joueurs]
                self.nombre_joueurs -= 1
            tour += 1
        if self.vainqueur != None :
            print(f"\n====== Le vainqueur est {self.vainqueur.nom} ! ======")
            print("====== SCORES ======")
            for position in range(1, len(self.joueurs)):
                nouvel_element = self.joueurs[position]
                position_nouvel_element = position
                while position_nouvel_element > 0 and self.joueurs[position_nouvel_element-1].compter_points() > nouvel_element.compter_points():
                    self.joueurs[position_nouvel_element] = self.joueurs[position_nouvel_element-1]
                    position_nouvel_element -= 1
                self.joueurs[position_nouvel_element] = nouvel_element
            for index, joueur in enumerate(self.joueurs) :
                print(f"{index+ 1}. {joueur.nom.capitalize()}")
                
class Joueur:
    
    def __init__(self, nom, partie):
        """Constructeur"""
        self.nom = nom
        self.partie = partie
        self.jeu = []
        self.train = []
        self.bloque = False
        for _ in range(self.partie.nombre_dominos_par_joueur()):
            self.jeu.append(self.partie.pioche.piocher())
    
    def afficher_train(self):
        """Permet d'afficher le train du joueur"""
        for domino in self.train:
            print(domino, end = " ")
            
    def afficher_jeu(self):
        """Permet d'afficher chaque domino du jeu"""
        for domino in self.jeu:
            print(domino)
            sleep(0.5)
            
    def chercher_options(self):
        """Renvoie un tableau de valeurs correspondants aux indices des pièces que le joueur peut placer"""
        if self.train == []:
            maxi = None
            for index, domino in enumerate(self.jeu):
                if domino.est_double():
                    if maxi == None or domino.plus_grand(self.jeu[maxi]):
                        maxi = index
            if maxi != None :
                return [maxi]
            else:
                maxi = 0
                liste_indices = []
                for index, domino in enumerate(self.jeu):
                    if domino.valeur() > maxi:
                        maxi = domino.valeur()
                        liste_indices = [index]
                    elif domino.valeur() == maxi:
                        liste_indices.append(index)
                return liste_indices
        else:
            liste_options = []
            for index, domino in enumerate(self.jeu):
                if domino.peut_placer_apres(self.train[-1]):
                    liste_options.append(index)
                    if domino.A != self.train[-1].B:
                        domino.permute()
                elif domino.peut_placer_avant(self.train[0]):
                    liste_options.append(index)
                    if domino.B != self.train[0].A:
                        domino.permute()
            return liste_options
        
    def jouer(self, options, num_option):
        """Effectue l'action demandée par l'utilisateur en fonction des options proposées et de l'option choisie"""
        if num_option < len(options):
            domino = self.jeu[options[num_option]]
            joue = False
            if self.train == [] or domino.peut_placer_apres(self.train[-1]) :
                self.train.append(domino)
                joue = True
            elif self.train == [] or domino.peut_placer_avant(self.train[0]) :
                self.train = [domino] + self.train
                joue = True
            if joue :
                del self.jeu[options[num_option]]
        else:
            self.jeu.append(self.partie.pioche.piocher())
    
    def compter_points(self):
        """Renvoie la somme des points du joueur (self)"""
        points = 0
        for domino in self.jeu :
            points += domino.valeur()
        return points
            
class Tour:
    
    def __init__(self, joueur):
        
        """Constructeur"""
        
        #Affichage de l'état de jeu du joueur
        self.joueur = joueur
        sleep(2)
        print(f"\n\n====== NOUVEAU TOUR : {self.joueur.nom.upper()} ======")
        sleep(1)
        if self.joueur.train == []:
            print("Votre train n'est pas encore commencé.")
        else:
            print("\nVoici votre train :")
            self.joueur.afficher_train()
        sleep(1)
        print("\n\nVoici les pièces dont vous disposez :")
        self.joueur.afficher_jeu()
        
        #Interface pour que le joueur choisisse ce qu'il veut jouer
        options = self.joueur.chercher_options()
        if options != [] or self.joueur.partie.pioche.jeu != [] :
            print("\nVoici les options que vous avez :")
            for index, option in enumerate(options) :
                print(f"- Option {index + 1} : jouer {self.joueur.jeu[option]}")
                sleep(0.5)
            if self.joueur.partie.pioche.jeu != []:
                print(f"- Option {len(options) + 1} : piocher [ ? | ? ]")
            choix_utilisateur = int(input("Que choisissez-vous de jouer ? "))
            self.joueur.jouer(options, choix_utilisateur - 1)
        else:
            print("Vous êtes bloqué et la pioche est vide, vous êtes donc éliminé.")
            self.joueur.bloque = True
        #Si le joueur n'a plus de pièces dans son jeu, il a gagné la partie
        if self.joueur.jeu == []:
            self.joueur.partie.vainqueur = self.joueur
        
partie = Partie()
partie.lancer_partie()