# -*- coding: utf-8 -*-
# La ligne précédente indique à python que les caractères non-ascii
# qu’il est susceptible de rencontrer sont encodés en UTF8

from random import randint

class Mastermind:
    """Classe permettant de gérer une partie de
    mastermind.

    Paramètre obligatoire : le niveau de difficulté
    de la partie.
    """
    valeur = str(randint(0,9999))

    def __init__(self, niv_dif):
        """Initialisation de la valeur à trouver
        et du nombre d’essais en fonction du
        niveau de difficulté.
        """
        self.dif = niv_dif
        # Convertion en chaîne de 4 caractères
        self.valeur = ('0'*(4-len(self.valeur))) + self.valeur
        # 0: difficile -> 5 tentative, 1: moyen -> 10 tentatives,
        # 2: facile -> 15 tentatives
        self.tentatives = 5 + 5*niv_dif
        self.victoire = False

    def status(self):
        """Permet de connaître l’état courant de la partie"""
        if self.victoire:
            return 'victoire'
        return self.tentatives > 0 and 'jeu' or 'défaite'

    def essai(self, nombre):
        """Compare nombre à la valeur aléatoire à deviner
        et retourne le résultat sous forme de chaîne de caractère.

        Le formatage du résultat dépend du niveau de difficulté.
        """
        # On vérifie qu’il n’y a pas de triche
        if self.status() != 'jeu':
            return 'La partie est finie, veuillez en commencer une autre'
        # Ajout de 0 manquant en début de chaîne si besoin
        tentative = '0'*(4-len(nombre)) + nombre

        # Raccourci si le nombre est bon
        if tentative == self.valeur:
            self.victoire = True
            return Mastermind.resultat([True, True, True, True])
        
        # Comparaison
        res = []
        for a,b in zip(self.valeur, tentative):
            if a == b:
                res.append(True)
            elif b in self.valeur:
                res.append(None)
            else:
                res.append(False)
        self.tentatives -= 1

        # Formatage en fonction du niveau de difficulté
        if self.dif == 2:
            return Mastermind.resultat_easy(res)
        if self.dif:
            return Mastermind.resultat(res)
        return Mastermind.resultat_trie(res)

    @staticmethod
    def moinsDe10000():
        """Demande à l’utilisateur une valeur numérique
        entre 0 et 9999.

        Recommence tant que la valeur ne respecte pas
        ces conditions.
        """
        val = '' # valeur non décimale pour rentrer dans la boucle
        while not val.isdecimal() or len(val) > 4:
            val = input('Votre tentative : ')
        return val

    @staticmethod
    def resultat_easy(liste):
        """Formate la liste passée en paramètre en une
        chaîne de caractère représentant le résultat de
        la tentative de divination en mode de difficulté
        facile.
        """
        res = ''
        for val in liste:
            if val is None:
                res += '~'
            else:
                res += val and 'O' or 'X'
        return res

    @staticmethod
    def resultat(liste):
        """Formate la liste passée en paramètre en une
        chaîne de caractère représentant le résultat de
        la tentative de divination en mode de difficulté
        normal.
        """
        return ''.join([val and 'O' or 'X' for val in liste])

    @staticmethod
    def resultat_trie(liste):
        """Formate la liste passée en paramètre en une
        chaîne de caractère représentant le résultat de
        la tentative de divination en mode de difficulté
        difficile.
        """
        # sorted ne peut pas comparer None avec des booléens
        # il faut donc transformer la liste au préalable
        return Mastermind.resultat(sorted([a and True or False for a in liste]))

    def partie(self):
        """Lance la partie associée à cette instance de
        Mastermind et s’arrète lorsque le nombre a été
        trouvé ou qu’il n’y a plus de tentatives.
        """
        while self.status() == 'jeu':
            print('Encore', self.tentatives, 'essais')
            print(self.essai(Mastermind.moinsDe10000()))

def mastermind():
    """Gère un ensemble de parties de mastermind en
    demandant à l’utilisateur, entre chaque partie,
    le niveau de difficulté souhaité.
    """
    while True:
        dif = input('Niveau de difficulté souhaité ')
        # Cas d’arrêt : ce n’est pas un niveau de difficulté
        if not dif.isdecimal():
            break
        # Cas d’arrêt : le niveau de difficulté n’existe pas
        dif = int(dif)
        if not dif in [0,1,2]:
            break

        # Lancement d’une partie
        jeu = Mastermind(dif)
        jeu.partie()
        print('Et c’est la', jeu.status())
        print('Il fallait trouver', jeu.valeur)

# Modularisation du fichier
if __name__ == '__main__':
    mastermind()
