예제 #1
0
class FenetrePartie(Tk):
    """Interface graphique de la partie de dames.

    Attributes:
        partie (Partie): Le gestionnaire de la partie de dame
        canvas_damier (CanvasDamier): Le «widget» gérant l'affichage du damier à l'écran
        messages (Label): Un «widget» affichant des messages textes à l'utilisateur du programme
    """
    def __init__(self):
        """Constructeur de la classe FenetrePartie. On initialise une partie en utilisant la classe Partie du TP3 et
        on dispose les «widgets» dans la fenêtre.
        """

        # Appel du constructeur de la classe de base (Tk)
        super().__init__()

        # La partie
        self.partie = Partie()

        # Création du canvas damier.
        self.canvas_damier = CanvasDamier(self, self.partie.damier, 60)
        self.canvas_damier.grid(sticky=NSEW)
        self.canvas_damier.bind('<Button-1>', self.selectionner)
        # self.canvas_damier.bind('<B1-Button_release>', self.enregistrer_position_cible)
        self.canvas_damier.couleur_foncee = '#426D8F'  # Bleu
        self.canvas_damier.couleur_pale = 'light cyan'

        # Ajout d'une étiquette d'information.
        self.messages1 = Label(self)  # Affichage des messages
        self.messages1.grid()
        self.messages1['foreground'] = 'blue'
        self.messages1['background'] = 'mint cream'
        self.messages1['text'] = 'Quelle pièce désirez-vous déplacer?'
        self.colonne_damier_reel = "abcdefgh"

        # Ajout des boutons : A permettant d'obtenir l'aide et B de quitter et d'enregistrer.
        self.bouton1_A = Button(self,
                                text='Aide',
                                bg='SlateGray1',
                                command=self.aide)
        self.bouton1_B = Button(self,
                                text='Quitter',
                                bg='SlateGray1',
                                command=self.quitter_damier)
        self.bouton1_C = Button(self,
                                text='Partie sauvegardée',
                                bg='SlateGray1',
                                command=self.partie_sauvegardee)
        self.bouton1_D = Button(self,
                                text="Couleurs du damier",
                                bg='SlateGray1',
                                command=self.couleur_damier)
        self.bouton1_A['foreground'] = 'midnight blue'
        self.bouton1_B['foreground'] = 'midnight blue'
        self.bouton1_C['foreground'] = 'midnight blue'
        self.bouton1_D['foreground'] = 'midnight blue'

        self.bouton1_A.grid(row=2, column=0, pady=5, sticky=W)
        self.bouton1_B.grid(row=1, column=1, padx=25, pady=5)
        self.bouton1_C.grid(row=2, column=1, pady=5, sticky=E)
        self.bouton1_D.grid(row=2, column=0, pady=5, sticky=S)

        # Liste des mouvements
        self.messages1_B = Label(self)
        self.messages1_B.grid(row=0, column=1, sticky=N)
        self.messages1_B['foreground'] = 'black'
        self.messages1_B['background'] = 'white'
        self.messages1_B['text'] = "Blanc       Noir\n───────\n"
        self.numero_deplacement = 1

        # Initialisation des attributs
        self.doit_prendre = False
        self.position_source_selectionnee = None
        self.position_source_forcee = None

        # Nom de la fenêtre
        self.titre_joueur = self.partie.couleur_joueur_courant + " joue!"
        self.title("Jeu de dames. Le joueur " + self.titre_joueur)
        self['background'] = 'mint cream'

        # Truc pour le redimensionnement automatique des éléments de la fenêtre.
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)

    def selectionner(self, event):
        """Méthode qui gère le clic de souris sur le damier. La méthode appelle les méthodes vérifiant la validité
        des sélections source et cible.
        Tant qu'une cible valide n'est pas sélectionnée, la position source peut être modifiée.

        Args:
            event (tkinter.Event): Objet décrivant l'évènement qui a causé l'appel de la méthode.

        """
        try:  # Permet d'affecter le premier clic à la position source et le second à la cible.
            if self.flg == 0:  # Génère l'erreur qui affecte le premier clic.
                ligne = event.y // self.canvas_damier.n_pixels_par_case
                colonne = event.x // self.canvas_damier.n_pixels_par_case  # On trouve le numéro de ligne/colonne en
                # divisant les positions en y/x par le nombre de pixels par case.
                self.position_cible = Position(ligne, colonne)

                self.messages1['foreground'] = 'black'
                position_source_damier_reel =self.colonne_damier_reel[self.position.colonne]\
                                                 + str(8 - self.position.ligne)
                position_cible_damier_reel = self.colonne_damier_reel[self.position_cible.colonne]\
                                                 + str(8 - self.position_cible.ligne)
                self.messages1['text'] = 'Pièce à la position {} déplacée à {}.'\
                        .format(position_source_damier_reel, position_cible_damier_reel)

                if not self.valider_et_enregistrer_position_cible()[0]:
                    self.messages1['foreground'] = 'red'
                    self.messages1[
                        'text'] = self.valider_et_enregistrer_position_cible(
                        )[1]
                    raise ValueError

                retour_apres_deplacement = self.partie.damier.deplacer(
                    self.position, self.position_cible)
                # ok, prise ou erreur
                if self.partie.couleur_joueur_courant == "blanc":
                    self.messages1_B['text'] = self.messages1_B['text'] + str(self.numero_deplacement) + "- "\
                                + str(position_source_damier_reel) + "  " + str(position_cible_damier_reel) + "     "
                    self.numero_deplacement += 1
                    #if retour_apres_deplacement == "prise":
                    #   self.messages1_B['text'] = self.messages1_B['text'] + "\n"
                else:
                    #if retour_apres_deplacement == "prise":
                    #   self.messages1_B['text'] = self.messages1_B['text'] + "          "
                    self.messages1_B['text'] = self.messages1_B['text'] + str(position_source_damier_reel) + "  " \
                                               + str(position_cible_damier_reel) + "\n"

                if retour_apres_deplacement == "ok":
                    pass
                elif retour_apres_deplacement == "prise":
                    if self.partie.damier.piece_peut_faire_une_prise(
                            self.position_cible):

                        self.position_source_forcee = self.position_cible
                        self.doit_prendre = True

                        if self.partie.couleur_joueur_courant == 'noir':
                            self.messages1_B['text'] = self.messages1_B[
                                'text'] + "                    "
                        else:
                            self.messages1_B['text'] = self.messages1_B[
                                'text'] + "          \n"

                    else:
                        self.doit_prendre = False
                        self.position_source_selectionnee = None
                        self.position_source_forcee = None
                else:
                    self.messages1['foreground'] = 'red'
                    self.messages1['text'] = "Il y a erreur dans le code!"

                if self.doit_prendre == False:
                    if self.partie.couleur_joueur_courant == "blanc":
                        self.partie.couleur_joueur_courant = "noir"
                    else:
                        self.partie.couleur_joueur_courant = "blanc"

                    self.titre_joueur = self.partie.couleur_joueur_courant + " joue!"
                    self.title("Jeu de dames. Le joueur " + self.titre_joueur)
                else:
                    self.titre_joueur = self.partie.couleur_joueur_courant + " joue et doit faire une prise!"
                    self.title("Jeu de dames. Le joueur " + self.titre_joueur)

                del self.flg  # Libère le drapeau pour le tour suivant

        except:  # Permet de valider la position source et déterminer si une prise doit être faite.
            ligne = event.y // self.canvas_damier.n_pixels_par_case
            colonne = event.x // self.canvas_damier.n_pixels_par_case
            self.position = Position(ligne, colonne)
            position_source_damier_reel = self.colonne_damier_reel[
                self.position.colonne] + str(8 - self.position.ligne)
            if self.valider_prise_obligee()[0]:
                self.title("Jeu de dames. Le joueur " +
                           self.valider_prise_obligee()[1])
                if self.partie.damier.piece_peut_faire_une_prise(
                        self.position):
                    self.messages1['foreground'] = 'black'
                    self.messages1['text'] = 'La pièce sélectionnée en position ' \
                                                 + position_source_damier_reel + ' peut faire une prise.'

                else:
                    self.messages1['foreground'] = 'red'
                    self.messages1[
                        'text'] = 'Sélectionnez une pièce qui peut faire une prise.'

            else:
                self.titre_joueur = self.partie.couleur_joueur_courant + " joue!"
                self.title("Jeu de dames. Le joueur " + self.titre_joueur)

            if self.partie.position_source_valide(self.position)[0]:
                if self.valider_et_enregistrer_position_source()[0]:
                    self.messages1['foreground'] = 'black'
                    self.messages1[
                        'text'] = self.valider_et_enregistrer_position_source(
                        )[1]
                    self.flg = 0  # Valide la position source et autorise la sélection de la cible
                else:
                    self.messages1['foreground'] = 'red'
                    self.messages1[
                        'text'] = self.valider_et_enregistrer_position_source(
                        )[1]

            else:
                self.messages1['foreground'] = 'red'
                self.messages1['text'] = self.partie.position_source_valide(
                    self.position)[1]

        self.canvas_damier.actualiser()

        # Fin de partie
        if self.partie.damier.piece_de_couleur_peut_se_deplacer(self.partie.couleur_joueur_courant) or \
                self.partie.damier.piece_de_couleur_peut_faire_une_prise(self.partie.couleur_joueur_courant):
            pass
        else:
            self.title("Jeu de dames. La partie est terminée!")
            self.messages1['foreground'] = 'orange'
            if self.partie.couleur_joueur_courant == "blanc":
                self.partie.couleur_joueur_courant = "noir"
            else:
                self.partie.couleur_joueur_courant = "blanc"
            self.messages1[
                'text'] = "Le joueur " + self.partie.couleur_joueur_courant + " a gagné!"

    def valider_prise_obligee(self):
        """
        Détermine si le joueur actif doit ou non faire une prise. Rend la prise obligatoire dans le choix
        de la position source.
        return:
                [0] : True ou False
                [1] : Message à afficher si le joueur doit faire une prise.
        """
        if self.partie.damier.piece_de_couleur_peut_faire_une_prise(
                self.partie.couleur_joueur_courant):
            self.doit_prendre = True
            if self.position_source_forcee is None:  # C'est une première prise
                self.titre_joueur = self.partie.couleur_joueur_courant + " joue et doit faire une prise!"

            else:  # Indique une prise successive
                position_source_damier_reel = self.colonne_damier_reel[
                    self.position_source_forcee.colonne] + str(
                        8 - self.position_source_forcee.ligne)
                self.titre_joueur = self.partie.couleur_joueur_courant + " joue. La pièce en position "\
                                    + position_source_damier_reel + " doit faire une prise!"
            return [True, self.titre_joueur]

        else:
            return [False, ""]

    def valider_et_enregistrer_position_source(self):
        """
        S'assure que la position source doit faire une prise, si c'est le cas, sinon que la pièce choisie est de la
        couleur du joueur et qu'elle peut se déplacer.

        return:
                [0] : True ou False
                [1] : Message à afficher si la source n'est pas valide.
        """
        position_source_damier_reel = self.colonne_damier_reel[
            self.position.colonne] + str(8 - self.position.ligne)
        if self.doit_prendre == True:
            if self.position_source_forcee is None:
                texte_messages1 = "Vous devez prendre. Assurez-vous que la pièce sélectionnée, en position " \
                                  + position_source_damier_reel + " peut prendre."
                return [True, texte_messages1]
            else:
                if self.position_source_forcee == self.position:
                    self.messages1['foreground'] = 'red'
                    texte_messages1 = "Vous devez prendre à nouveau. La pièce en position "\
                                      + position_source_damier_reel + " a été sélectionnée."
                    return [True, texte_messages1]
                else:
                    texte_messages1 = "Vous devez prendre à nouveau. La pièce choisie ne peut pas être sélectionnée."
                    return [False, texte_messages1]
        elif self.partie.damier.piece_peut_se_deplacer(self.position):
            self.messages1['foreground'] = 'black'
            texte_messages1 = 'La pièce en position ' + position_source_damier_reel \
                                       + ' a été sélectionnée. Cliquez sur la cible désirée. '
            return [True, texte_messages1]
        else:
            texte_messages1 = "La pièce que vous avez sélectionnée ne peut pas se déplacer. Veuillez " \
                                         "faire un autre choix. "
            return [False, texte_messages1]

    def valider_et_enregistrer_position_cible(self):
        """
        S'assure que la pièce choisie comme position source peut se déplacer à la position cible, soit en faisant
        une prise ou en se déplaçant.

        return:
                [0] : True, si la position cible est valide, sinon False
                [1] : Message à afficher si la cible n'est pas valide.
        """
        if self.doit_prendre == True:
            if self.partie.damier.piece_peut_sauter_vers(
                    self.position, self.position_cible,
                    self.partie.couleur_joueur_courant):
                return [True, ""]

            else:
                texte_messages1 = "La pièce choisie doit prendre une pièce adverse. La cible choisie doit être modifiée."
                return [False, texte_messages1]

        elif self.partie.damier.piece_peut_se_deplacer_vers(
                self.position, self.position_cible):
            return [True, ""]
        else:
            texte_messages1 = "La pièce choisie ne peut pas être déplacée vers cette case."
        return [False, texte_messages1]

    def aide(self):
        """
        Fait apparaître une fenêtre contextuelle présentant, sous la forme d'un texte, l'aide à l'utilisation
        du programme damier ainsi que les règlements applicables.
        Le bouton "Quitter" permet de fermer la fenêtre et de retourner au damier.
        """
        self.fenetre_2 = Tk()
        self.fenetre_2.title("Aide et règlements")

        texte_aide0 = Message(self.fenetre_2)
        texte_aide0['foreground'] = 'brown'

        texte_aide1 = Message(self.fenetre_2, width=492)
        texte_aide1['foreground'] = 'blue'

        texte_aide2 = Message(self.fenetre_2)
        texte_aide2['foreground'] = 'brown'

        texte_aide3 = Message(self.fenetre_2)
        texte_aide3['foreground'] = 'blue'

        Extrait_aide = open("Aide_reglements.txt", 'r', encoding="utf-8")
        texte_extrait = Extrait_aide.readlines()
        texte_aide0['text'] = texte_extrait[0]
        texte_aide1['text'] = texte_extrait[1]
        for i in range(2, 5):
            texte_aide1['text'] = texte_aide1['text'] + texte_extrait[i]

        texte_aide2['text'] = texte_extrait[5]
        texte_aide3['text'] = texte_extrait[6]
        for i in range(7, len(texte_extrait)):
            texte_aide3['text'] = texte_aide3['text'] + texte_extrait[i]
        Extrait_aide.close()
        texte_aide0.grid()
        texte_aide1.grid(sticky=W)
        texte_aide2.grid()
        texte_aide3.grid(sticky=W)
        bouton2_A = Button(self.fenetre_2,
                           text='Quitter',
                           command=self.fenetre_aide_quit)
        bouton2_A.grid()
        self.fenetre_2.tkraise()  # mainloop()

    def fenetre_aide_quit(self):
        """
        Méthode appelée par le bouton "Quitter" de la fenêtre "Aide et règlements".
        Permet de fermer la fenêtre en permettant aux joueurs de retourner au jeu déjà commencé.
        """
        self.fenetre_2.withdraw()

    def quitter_damier(self):
        """
        Méthode appelée par le bouton "Quitter" de la fenêtre principale du damier.
        Permet
            1. de fermer la fenêtre avec ou sans sauvegarde de la partie en cours;
            2.aux joueurs de retourner au jeu déjà commencé;
            3. d'ouvrir une nouvelle partie sans fermer la partie en cours.
        Boutons activés :
            A. Quitter et sauvegarder
            B. Quitter sans sauvegarder
            C. Nouvelle partie
            D. Annuler et revenir à la partie
        """
        self.fenetre_3 = Tk()
        self.fenetre_3.geometry("460x230")
        self.fenetre_3.title("Pourquoi quitter?")

        # texte_3_A = Message(self.fenetre_3)
        texte_3_A = Label(self.fenetre_3)
        texte_3_B = Label(self.fenetre_3)
        texte_3_C = Label(self.fenetre_3)
        texte_3_D = Label(self.fenetre_3)
        texte_3_A[
            'text'] = "1- Si vous ouvrez une nouvelle partie, la partie non terminée que vous venez de quitter"
        texte_3_B[
            'text'] = "sera encore accessible et il sera possible de jouer deux parties à la fois!\n "
        texte_3_C[
            'text'] = "2- En annulant, vous retournez à la partie déjà ouverte.\n "
        texte_3_D[
            'text'] = "3- Si vous quittez sans sauvegarder, toutes les parties seront fermées."
        texte_3_A.grid(sticky=W)
        texte_3_B.grid(sticky=W)
        texte_3_C.grid(sticky=W)
        texte_3_D.grid(sticky=W)
        bouton3_A = Button(self.fenetre_3,
                           text='Quitter et sauvegarder',
                           command=self.sauvegarde_partie)
        bouton3_B = Button(self.fenetre_3,
                           text='Quitter sans sauvegarder',
                           command=self.quit)
        bouton3_C = Button(self.fenetre_3,
                           text='Nouvelle partie',
                           command=self.nouvelle_partie)
        bouton3_D = Button(self.fenetre_3,
                           text='Annuler',
                           command=self.fenetre_quit_annulee)
        bouton3_A.grid(row=4, column=0, pady=10, sticky=W)
        bouton3_B.grid(row=4, column=0, pady=10, sticky=E)
        bouton3_C.grid(row=5, column=0, sticky=W)
        bouton3_D.grid(row=5, column=0, sticky=E)
        self.fenetre_3.tkraise()

    def sauvegarde_partie(self):
        """
        Méthode appelée par le bouton "Quitter et sauvegarder" de la fenêtre "Quitter".
        Permet de sauvegarder la partie au point où elle était rendue.
        """
        self.fenetre_4 = Tk()
        self.fenetre_4.geometry("500x130")
        self.fenetre_4.title("Fichier de sauvegarde")
        # Sauvegarde de plusieurs parties à la même date
        # Le nom des fichiers contient la date de la sauvegarde
        n_fich = 1
        while os.path.isfile("Sauvegarde-" + str(date.today()) + "(" +
                             str(n_fich) + ").txt"):
            n_fich += 1
        nom_fichier_sauvegarde = "Sauvegarde-" + str(
            date.today()) + "(" + str(n_fich) + ").txt"

        fichier_partie = open(nom_fichier_sauvegarde, "wb")
        dump([self.partie.couleur_joueur_courant, self.partie.damier.cases],
             fichier_partie)

        fichier_partie.close()

        texte_4_A = Label(self.fenetre_4)
        texte_4_B = Label(self.fenetre_4)
        texte_4_C = Label(self.fenetre_4)
        texte_4_D = Label(self.fenetre_4)
        texte_4_A['foreground'] = 'blue'
        texte_4_B['foreground'] = 'green'
        texte_4_C['foreground'] = 'blue'
        texte_4_A[
            'text'] = "La partie que vous quittez a été sauvegardée dans le fichier : "
        texte_4_B['text'] = nom_fichier_sauvegarde  # + "!"
        texte_4_C['text'] = "!"
        texte_4_A.grid(row=0, column=0)
        texte_4_B.grid(row=0, column=1)
        texte_4_C.grid(row=0, column=2)
        texte_4_D.grid(row=1, column=0)
        bouton4_A = Button(self.fenetre_4,
                           text='Quitter le jeu',
                           command=self.quit)
        bouton4_B = Button(self.fenetre_4,
                           text='Retour au jeu',
                           command=self.retour_jeu)
        bouton4_A.grid(row=2, column=0, sticky=SW)
        bouton4_B.grid(row=2, column=0, sticky=SE)
        self.fenetre_4.tkraise()

    def nouvelle_partie(self):
        """
        Méthode appelée par le bouton "Nouvelle partie" de la fenêtre "Quitter".
        Ouvre une autre fenêtre de jeu sans fermer le damier déjà commencé,
        permettant aux joueurs de jouer plusieurs parties simultanées.
        """
        self.fenetre_3.withdraw()
        fenetre = FenetrePartie()
        fenetre.mainloop()

    def partie_sauvegardee(self):
        """
        Méthode appelée par le bouton "Partie sauvegardée" de la fenêtre principale.
        Permet d'ouvrir une partie non complétée au point où elle avait été arrêtée.
        """
        self.fenetre_5 = Tk()
        self.fenetre_5.geometry("400x230")  # Ajuster
        self.fenetre_5.title("Fichiers sauvegardés")

        texte_5_A = Label(self.fenetre_5)
        bouton5_A = Button(self.fenetre_5,
                           text='Annuler',
                           command=self.ouverture_fich_annulee)

        self.liste_fich = Listbox(self.fenetre_5,
                                  width=27,
                                  height=10,
                                  selectmode=SINGLE)
        fich_insere = 0
        for nom_fich in os.listdir():
            if nom_fich[0:10] == "Sauvegarde":
                self.liste_fich.insert(END, nom_fich)
                fich_insere += 1
        if fich_insere != 0:
            texte_5_A['foreground'] = 'purple'
            texte_5_A[
                'text'] = "Liste des fichiers de sauvegarde dans le répertoire du projet :"
            self.liste_fich.grid(row=2, column=0)
            self.liste_fich.bind('<Double-Button-1>',
                                 lambda event: self.ouvrir_sauvegarde())

        else:
            texte_5_A['foreground'] = 'red'
            texte_5_A[
                'text'] = "Il n'y a pas de fichiers de sauvegarde dans le répertoire du projet."
        texte_5_A.grid(row=0, column=0)  # Ajuster
        bouton5_A.grid(row=2, column=1, sticky=N)
        self.fenetre_5.tkraise()

    def ouvrir_sauvegarde(self):
        self.index_fich_select = self.liste_fich.curselection()[0]

        nom_fichier = open(self.liste_fich.get(self.index_fich_select), "rb")
        list_dic = load(nom_fichier)
        self.partie.couleur_joueur_courant = list_dic[0]
        self.partie.damier.cases = list_dic[1]
        # self.damier_ouvert = literal_eval(damier_cases)
        nom_fichier.close()
        self.fenetre_5.withdraw()
        self.titre_joueur = self.partie.couleur_joueur_courant + " joue!"
        self.title("Jeu de dames. Le joueur " + self.titre_joueur)
        self.canvas_damier.actualiser()
        self.mainloop()

    def fenetre_quit_annulee(self):
        """
        Méthode appelée par le bouton "Annuler" de la fenêtre "Quitter".
        Permet de fermer la fenêtre en permettant aux joueurs de retourner au jeu déjà commencé.
        """
        self.fenetre_3.withdraw()

    def retour_jeu(self):
        """
        Méthode appelée par le bouton "Retour au jeu" de la fenêtre "Quitter et sauvegarder".
        Ferme la fenêtre confirmant le nom du fichier de sauvegarde sans fermer ni le damier déjà commencé
        ni la fenêtre contextuelle, permettant aux joueurs de retourner à la partie en cours.
        """
        self.fenetre_3.withdraw()
        self.fenetre_4.withdraw()

    def couleur_damier(self):
        #TODO
        """
        Méthode appelée par le bouton "Couleurs du damier" de la fenêtre principale du damier.
        Permet de choisir les couleurs du damier;
        Boutons activés :
            A. Bleu
            B. Mauve
            C. Marron
            D. Rouge
            E. Vert
        """
        self.fenetre_6 = Tk()
        self.fenetre_6.geometry("460x230")
        self.fenetre_6.title("Couleurs du damier!")
        self.var_b = IntVar()
        r_bouton6_A = Radiobutton(self.fenetre_6,
                                  text='Bleu',
                                  variable=self.var_b,
                                  value=1,
                                  command=self.selection_couleur_bleue)
        r_bouton6_B = Radiobutton(self.fenetre_6,
                                  text='Mauve',
                                  variable=self.var_b,
                                  value=2,
                                  command=self.selection_couleur_mauve)
        r_bouton6_C = Radiobutton(self.fenetre_6,
                                  text='Marron',
                                  variable=self.var_b,
                                  value=3,
                                  command=self.selection_couleur_marron)
        r_bouton6_D = Radiobutton(self.fenetre_6,
                                  text='Rouge',
                                  variable=self.var_b,
                                  value=4,
                                  command=self.selection_couleur_rouge)
        r_bouton6_E = Radiobutton(self.fenetre_6,
                                  text='Vert',
                                  variable=self.var_b,
                                  value=5,
                                  command=self.selection_couleur_verte)

        texte_6_A = Label(self.fenetre_6)
        texte_6_A[
            'text'] = "Choisir la couleur du damier que vous désirez afficher."

        texte_6_A.grid(row=0, sticky=N)

        r_bouton6_A.grid(sticky=W)  # row=2)
        r_bouton6_B.grid(sticky=W)
        r_bouton6_C.grid(sticky=W)
        r_bouton6_D.grid(sticky=W)
        r_bouton6_E.grid(sticky=W)

        bouton6_A = Button(self.fenetre_6,
                           text='Annuler',
                           command=self.fenetre_6.withdraw)
        bouton6_A.grid()

        self.fenetre_6.tkraise()

    def selection_couleur_bleue(self):
        """
        Modification de la couleur du damier.
        Ferme la fenêtre contextuelle dès que la sélection est faite et retourne au damier.
        """
        self.canvas_damier.couleur_foncee = '#426D8F'  # Bleu
        self.canvas_damier.couleur_pale = 'light cyan'
        self.fenetre_6.withdraw()
        self.canvas_damier.actualiser()

    def selection_couleur_mauve(self):
        """
        Modification de la couleur du damier.
        Ferme la fenêtre contextuelle dès que la sélection est faite et retourne au damier.
        """
        self.canvas_damier.couleur_foncee = 'maroon4'  # Mauve
        self.canvas_damier.couleur_pale = 'thistle1'
        self.fenetre_6.withdraw()
        self.canvas_damier.actualiser()

    def selection_couleur_marron(self):
        """
        Modification de la couleur du damier.
        Ferme la fenêtre contextuelle dès que la sélection est faite et retourne au damier.
        """
        self.canvas_damier.couleur_foncee = 'Firebrick4'  # Marron
        self.canvas_damier.couleur_pale = 'Wheat1'
        self.fenetre_6.withdraw()
        self.canvas_damier.actualiser()

    def selection_couleur_rouge(self):
        """
        Modification de la couleur du damier.
        Ferme la fenêtre contextuelle dès que la sélection est faite et retourne au damier.
        """
        self.canvas_damier.couleur_foncee = 'red4'  # Rouge
        self.canvas_damier.couleur_pale = 'LightPink1'
        self.fenetre_6.withdraw()
        self.canvas_damier.actualiser()

    def selection_couleur_verte(self):
        """
        Modification de la couleur du damier.
        Ferme la fenêtre contextuelle dès que la sélection est faite et retourne au damier.
        """
        self.canvas_damier.couleur_foncee = 'chartreuse4'  # Vert
        self.canvas_damier.couleur_pale = 'light cyan'
        self.fenetre_6.withdraw()
        self.canvas_damier.actualiser()

    def ouverture_fich_annulee(self):
        """
        Méthode appelée par le bouton "Annuler" de la fenêtre "Quitter".
        Permet de fermer la fenêtre en permettant aux joueurs de retourner au jeu déjà commencé.
        """
        self.fenetre_5.withdraw()
예제 #2
0
class FenetrePartie(Tk):
    """
    Interface graphique de la partie de dames.

    Attributes:
        partie (Partie): Le gestionnaire de la partie de dame.
        canvas_damier (CanvasDamier): Le «widget» gérant l'affichage du damier à l'écran.
        messages (Label): Un «widget» affichant des messages textes à l'utilisateur du programme.
        couleur_joueur (Label): Un «widget» affichant la couleur du joueur courant.
        bool_piece_selectionnee (bool): Un booleen dont la valeur est True si une pièce est sélectionnée par le joueur.
        piece_selectionnee (Piece): La pièce sélectionnée par le joueur.
        position_selectionnee (Position): La position de la pièce sélectionnée par le joueur.
        position_cible_graphique (Position): La position cible valide sélectionnée par le joueur.
        texte_deplacements (str): Le texte enregistrant les déplacements effectuées par les joueurs.
        cadre_bouton (Frame): Un cadre servant à positionner les boutons.
        bouton_nouvelle_partie (Button): Un bouton pour initialiser une nouvelle partie.
        bouton_Quitter (Button): Un bouton pour quitter la fenêtre de partie.
        bouton_reglements (Button): Un bouton pour afficher les règlements dans une nouvelle fenêtre.
        bouton_deplacements (Button): Un bouton pour afficher l'historique des déplacements de la partie dans une
                                        nouvelle fenêtre.
        bouton_sauvegarder (Button): Un bouton pour sauvegarder la partie dans un fichier .txt.
        bouton_charger (Button): Un bouton pour charger la précédente sauvegarde.
        bouton_triche (Button): Un bouton pour ouvrir les options de triches dans une nouvelle fenêtre.
        fenetre_alarme (Tk): Une fenêtre affichant un message d'alarme au joueur.
    """
    def __init__(self):
        """
        Constructeur de la classe FenetrePartie. On initialise une partie en utilisant la classe Partie du TP3 et
        on dispose les «widgets» dans la fenêtre.
        """
        # Appel du constructeur de la classe de base (Tk):
        super().__init__()

        # La partie:
        self.partie = Partie()

        # Création du canvas damier:
        self.canvas_damier = CanvasDamier(self, self.partie.damier, 60)
        self.canvas_damier.grid(sticky=NSEW)
        self.canvas_damier.bind('<Button-1>', self.selectionner)

        # Ajout d'une étiquette d'information:
        self.messages = Label(self)
        self.messages.grid()

        # Ajout d'une étiquette de couleur du tour du joueur:
        self.couleur_joueur = Label(self)
        self.couleur_joueur.grid()
        self.couleur_joueur['text'] = 'Tour du joueur {}'.format('blanc')

        # Nom de la fenêtre («title» est une méthode de la classe de base «Tk»):
        self.title("Jeu de dames")

        # Truc pour le redimensionnement automatique des éléments de la fenêtre:
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)

        # Ajout de variables se rappelant si une pièce est sélectionnée:
        self.bool_piece_selectionnee = False
        self.piece_selectionnee = None
        self.position_selectionnee = None

        # Ajout d'uhe variable gérant le déplacement:
        self.position_cible_graphique = None

        # Ajout d'une chaîne de caractère enregistrant les déplacements effectués:
        self.texte_deplacements = 'Liste des déplacements, du plus récent au plus ancien: \n'

        # Ajout du cadre des boutons:
        self.cadre_bouton = Frame(self)
        self.cadre_bouton.grid(padx=10, pady=10)

        # Création du bouton 'Nouvelle partie':
        self.bouton_nouvelle_partie = Button(self.cadre_bouton,
                                             text="Nouvelle partie",
                                             command=self.nouvelle_partie,
                                             padx=10,
                                             pady=10)
        self.bouton_nouvelle_partie.grid(padx=10, pady=10, column=0, row=0)

        # Création du bouton 'quitter':
        self.bouton_Quitter = Button(self.cadre_bouton,
                                     text="Quitter",
                                     command=self.destroy,
                                     padx=10,
                                     pady=10)
        self.bouton_Quitter.grid(padx=10, pady=10, column=1, row=0)

        # Création du bourron 'Règlements':
        self.bouton_reglements = Button(self.cadre_bouton,
                                        text="Règlements",
                                        command=ouvrir_reglements,
                                        padx=10,
                                        pady=10)
        self.bouton_reglements.grid(padx=10, pady=10, column=2, row=0)

        # Création du bouton 'Déplacement':
        self.bouton_deplacements = Button(self.cadre_bouton,
                                          text='Déplacements',
                                          command=self.afficher_deplacements,
                                          padx=10,
                                          pady=10)
        self.bouton_deplacements.grid(padx=10, pady=10, column=3, row=0)

        # Création du bouton 'Sauvegarder':
        self.bouton_sauvegarder = Button(self.cadre_bouton,
                                         text='Sauvegarder',
                                         command=self.sauvegarder_partie,
                                         padx=10,
                                         pady=10)
        self.bouton_sauvegarder.grid(padx=10, pady=10, column=0, row=1)

        # Création du bouton 'Charger':
        self.bouton_charger = Button(self.cadre_bouton,
                                     text='Charger',
                                     command=self.charger_partie,
                                     padx=10,
                                     pady=10)
        self.bouton_charger.grid(padx=10, pady=10, column=1, row=1)

        # Création du bouton 'Tricher':
        self.bouton_triche = Button(self.cadre_bouton,
                                    text="Tricher",
                                    command=self.ouvrir_triches,
                                    padx=10,
                                    pady=10)
        self.bouton_triche.grid(padx=10, pady=10, column=2, row=1)

        # Initialisation de la variable 'fenetre_alarme':
        self.fenetre_alarme = None

    def nouvelle_partie(self):
        """
        Fonction supprimant la fenêtre de partie courante et relançant une nouvelle partie.
        """
        self.destroy()
        Fenetredimension()

    def afficher_deplacements(self):
        """
        Fonction créant une nouvelle fenêtre contenant un texte affichant l'historique des déplacements des joueurs.
        L'enregistrement des déplacements est géré par la fonction selectionner.
        """
        fenetre_deplacements = Tk()  # Création de la nouvelle fenêtre.

        # Création de l'étiquette ayant les déplacements comme texte:
        texte_d = Label(fenetre_deplacements,
                        text=self.texte_deplacements,
                        anchor='e',
                        padx=10,
                        pady=10)

        texte_d.grid()
        fenetre_deplacements.mainloop()

    def fonction_de_sauvegarde(self):
        """
        Fonction écrasant la sauvegarde précédente et sauvegardant la partie actuelle.
        """
        # Vérification de l'existence du fichier 'sauvegarde':
        if existe('sauvegarde.txt'):
            remove('sauvegarde.txt')

        # Création de la sauvegarde:
        fichier = open('sauvegarde.txt', 'w')
        print(self.partie.damier.cases, file=fichier)
        fichier.close()

        # Fermeture de la fenêtre d'alarme générée par la fonction self.sauvegarder_partie:
        self.fenetre_alarme.destroy()

    def sauvegarder_partie(self):
        """
        Fonction générant une fenêtre d'alarme demandant à l'utilisateur s'il veut bien sauvegarder.
        """
        # Génération de la nouvelle fenêtre d'alarme:
        self.fenetre_alarme = Tk()

        # Ajout d'une étiquette contenant le texte voulue:
        texte_alarme = Label(
            self.fenetre_alarme,
            text=
            'Si vous sauvegarder maintenant, la sauvegarde précédente sera écrasée. '
            '\n Voulez-vous continuer?',
            padx=10,
            pady=10)
        texte_alarme.grid(row=0, column=1, padx=10, pady=10)

        # Création du bouton 'oui':
        bouton_oui = Button(self.fenetre_alarme,
                            text='Oui',
                            command=self.fonction_de_sauvegarde,
                            padx=10,
                            pady=10)
        bouton_oui.grid(row=1, column=0, padx=10, pady=10)

        # Création du bouton 'non':
        bouton_non = Button(self.fenetre_alarme,
                            text='Non',
                            command=self.fenetre_alarme.destroy,
                            padx=10,
                            pady=10)
        bouton_non.grid(row=1, column=2, padx=10, pady=10)

    def charger_partie(self):
        """
        Fonction chargeant une partie précédemment sauvegardée. Génère une fenêtre d'alarme en cas d'absence
         de sauvegarde.
        """
        if existe('sauvegarde.txt'
                  ):  # Vérification de l'exitence de la sauvegarde.

            # Récupération des données enregistrées dans le fichier .txt:
            unfichier = open('sauvegarde.txt', 'r')
            dicostr = unfichier.readline()
            unfichier.close()

            # Gestion de la sauvegarde du dictionnaire:
            # initialisation des variables nécessaires:
            dico = {}  # Le dictionnaire récupéré.
            clef = ''  # La clé de dictionnaire récupérée sous format str.
            clef_p = ''  # La clé de dictionnaire récupérée sous format Position.

            # Parcours des éléments de la chaîne de caratères contenue dans le fichier .txt:
            for i in dicostr:
                if i == '{' or i == '}' or i == ' ' or i == ',':  # Les caractères à ignorer.
                    pass
                elif i == 'o' or i == 'O' or i == 'x' or i == 'X':  # Les caratères représentant les types de pièces.

                    # Gestion des valeurs du dictionnaire:
                    if i == 'o':
                        dico[clef_p] = Piece(
                            "blanc", "pion"
                        )  # Ajout de l'élément clé-valeur au dictionnaire 'dico'.
                        # Réinitialisation des variables:
                        clef_p = ''
                        clef = ''

                    # Les mêmes étapes sont répétés pour les autres types de pièces:
                    if i == 'O':
                        dico[clef_p] = Piece("blanc", "dame")
                        clef_p = ''
                        clef = ''

                    if i == 'x':
                        dico[clef_p] = Piece("noir", "pion")
                        clef_p = ''
                        clef = ''

                    if i == 'X':
                        dico[clef_p] = Piece("noir", "dame")
                        clef_p = ''
                        clef = ''

                # Gestion des clefs du dictionnaire 'dico':
                elif i == ':':  # Passage d'une clef à une valeur dans la variable dico_str:
                    tuple1 = ''  # Initialisation de la chaîne de caractère 'tuple1'
                    for j in clef:  # Parcours des caractères de la chaîne de caractère 'clef'.
                        # Si le caractère peut être un entier, il est ajouter à tuple1:
                        try:
                            int(j)
                            tuple1 += j
                        except ValueError:
                            pass

                    # La variable 'clef_p' reçoit la bonne valeur:
                    clef_p = Position(int(tuple1[0]), int(tuple1[1]))

                    # Réinitialisation des variables:
                    clef = ''

                # Dans les autres cas, 'clef' continie de stocker les caratères:
                else:
                    clef += i

            # Le dictionnaire définissant la partie est mis à jour par la sauvegarde:
            self.partie.damier.cases = dico

        # Si la sauvegarde n'existe pas, l'utilisateur en est informé:
        else:
            fenetre_alerte = Tk()  # Création d'une nouvelle fenêtre.
            texte = Label(fenetre_alerte,
                          text='Aucune sauvegarde n\'est disponible',
                          anchor='e')
            texte.grid(padx=10, pady=10)

            # Création du bouton pour quitter la fenêtre:
            bouton_quitter = Button(fenetre_alerte,
                                    text="Ok",
                                    command=fenetre_alerte.destroy,
                                    padx=10,
                                    pady=10)
            bouton_quitter.grid(padx=10, pady=10)

            fenetre_alerte.mainloop()

    def ouvrir_triches(self):
        """
        Fonction ouvrant une nouvelle fenêtre exposant les options de triches.
        """
        # Création d'une nouvelle fenêtre:
        fenetre_triche = Tk()

        # Création du bouton permettant de transformer toutes les pièces en dames:
        bouton_dame_tous = Button(
            fenetre_triche,
            text='Toutes les pièces \n deviennent des dames',
            command=self.pion_en_dames_tous,
            padx=10,
            pady=10)
        bouton_dame_tous.grid(column=0, row=0, padx=10, pady=10)

        # Création du bouton permettant de transformer les pièces blanches en dames:
        bouton_dame_blanc = Button(
            fenetre_triche,
            text='Les pièces blanches \n deviennent des dames',
            command=self.pion_en_dames_blanc,
            padx=10,
            pady=10)
        bouton_dame_blanc.grid(column=1, row=0, padx=10, pady=10)

        # Création du bouton permettant de transformer les pièces noires en dames:
        bouton_dame_noir = Button(
            fenetre_triche,
            text='Les pièces noires \n deviennent des dames.',
            command=self.pion_en_dames_noir,
            padx=10,
            pady=10)
        bouton_dame_noir.grid(column=2, row=0, padx=10, pady=10)

        # Création du bouton permettant de quitter la fenêtre:
        bouton_quitter = Button(fenetre_triche,
                                text='Terminer',
                                command=fenetre_triche.destroy,
                                padx=10,
                                pady=10)
        bouton_quitter.grid(column=1, row=1, padx=10, pady=10)

        fenetre_triche.mainloop()

    def pion_en_dames_noir(self):
        """
        Fonction transformant les pièces noires en dames.
        """
        # Boucle sur le dictionnaire définissant la partie:
        for position in self.partie.damier.cases:
            piece = self.partie.damier.recuperer_piece_a_position(position)

            # Promotion de la pièce si celle-ci est noire:
            if piece.couleur == 'noir':
                piece.promouvoir()

        # Actualisation du damier:
        self.canvas_damier.actualiser()

    def pion_en_dames_tous(self):
        """
        Fonction transformant toutes les pièces en dames.
        """
        # Boucle sur le dictionnaire définissant la partie:
        for position in self.partie.damier.cases:
            piece = self.partie.damier.recuperer_piece_a_position(position)

            # Promotion de la pièce:
            piece.promouvoir()

        # Actualisation du damier:
        self.canvas_damier.actualiser()

    def pion_en_dames_blanc(self):
        """
        Fonction transformant les pièces noires en dames.
        """
        # Boucle sur le dictionnaire définissant la partie:
        for position in self.partie.damier.cases:
            piece = self.partie.damier.recuperer_piece_a_position(position)

            # Promotion de la pièce si celle-ci est blanche:
            if piece.couleur == 'blanc':
                piece.promouvoir()

        # Actualisation du damier:
        self.canvas_damier.actualiser()

    def selectionner(self, event):
        """Méthode qui gère le clic de souris sur le damier, exécute la gestion des tours et retourne un message
        pertinent en cas de victoire.

        Args:
            event (tkinter.Event): Objet décrivant l'évènement qui a causé l'appel de la méthode.

        """
        # Gestion du clic de position cible:
        if self.bool_piece_selectionnee:

            # On trouve le numéro de ligne/colonne en divisant les positions en y/x par le nombre de pixels par case.
            ligne_deplacement = event.y // self.canvas_damier.n_pixels_par_case
            colonne_deplacement = event.x // self.canvas_damier.n_pixels_par_case

            # Vérification de la validité de la position cible désignée:
            if self.partie.position_cible_valide(
                    Position(ligne_deplacement, colonne_deplacement))[0]:
                # Si la position cible est valide, elle est attribuée à la variable 'self.position_cible_graphique:
                self.position_cible_graphique = Position(
                    ligne_deplacement, colonne_deplacement)

            else:  # Affichage d'un message d'erreur pertinent en cas de position non valide:
                self.messages['foreground'] = 'red'
                self.messages['text'] = \
                    self.partie.position_cible_valide(Position(ligne_deplacement, colonne_deplacement))[1]

            # Attribution des positions sélectionnées par clic à la variable pertinente de la classe partie:
            self.partie.couple_de_position = self.position_selectionnee, self.position_cible_graphique

            # Mise à jour du texte des déplacements:
            self.texte_deplacements += 'Déplacement du joueur {}, de {} vers {}. \n'.format(
                str(self.partie.couleur_joueur_courant),
                str(self.position_selectionnee),
                str(self.position_cible_graphique))

            # Exécution d'un tour:
            self.partie.tour()

            # On affiche le damier mis a jour.
            self.canvas_damier.actualiser()

            #  Mise à jour de l'affichage du joueur courant:
            couleur = str(self.partie.couleur_joueur_courant)
            self.couleur_joueur['text'] = 'Tour du joueur {}'.format(couleur)

            # Réinitialisation des attributs
            self.bool_piece_selectionnee = False
            self.piece_selectionnee = None
            self.position_selectionnee = None
            self.partie.position_source_selectionnee = None

            # Gestion des messages de victoire:
            if not self.partie.damier.piece_de_couleur_peut_se_deplacer(self.partie.couleur_joueur_courant) and \
                    not self.partie.damier.piece_de_couleur_peut_faire_une_prise(self.partie.couleur_joueur_courant):

                if self.partie.couleur_joueur_courant == 'noir':
                    self.messages['foreground'] = 'blue'
                    self.messages[
                        'text'] = 'Victoire du joueur blanc! Félicitations!'
                    return  # Arrêt de la méthode

                if self.partie.couleur_joueur_courant == 'blanc':
                    self.messages['foreground'] = 'blue'
                    self.messages[
                        'text'] = 'Victoire du joueur noir! Félicitations!'
                    return  # Arrêt de la méthode

            # Fin de l'exécution de la méthode:
            return

        # Gestion du clic de position source:
        if not self.bool_piece_selectionnee:

            # On trouve le numéro de ligne/colonne en divisant les positions en y/x par le nombre de pixels par case:
            ligne = event.y // self.canvas_damier.n_pixels_par_case
            colonne = event.x // self.canvas_damier.n_pixels_par_case

            # Validation de la position source sélectionnée
            if self.partie.position_source_valide(Position(ligne, colonne))[0]:
                # Si le joueur peut faire une prise, il y est obligé.
                # Vérification si le joueur peut faire une prise:
                if self.partie.damier.piece_de_couleur_peut_faire_une_prise(
                        self.partie.couleur_joueur_courant):
                    # Vérification si la pièce sélectionnée peut faire une prise:
                    if self.partie.damier.piece_peut_faire_une_prise(
                            Position(ligne, colonne)):
                        # Vérification si le joueur à déjà pris une pièce et doit continuer à prendre avec celle-ci:
                        if self.partie.position_source_forcee is not None \
                                and self.partie.position_source_forcee == Position(ligne, colonne):
                            # Si toutes les conditions sont réunies, les variables spnt mises à jour:
                            self.position_selectionnee = Position(
                                ligne, colonne)
                            self.partie.position_source_selectionnee = self.position_selectionnee

                        # Si le joueur n'est pas contraint par une position source forcée:
                        elif self.partie.position_source_forcee is None:
                            self.position_selectionnee = Position(
                                ligne, colonne)
                            self.partie.position_source_selectionnee = self.position_selectionnee

                        # Affichage d'un message d'erreur pertinent en cas d'erreur et arrêt de la méthode:
                        else:
                            self.messages['foreground'] = 'red'
                            self.messages['text'] = 'Erreur: Vous devez sélectionner la piece {}' \
                                .format(str(self.partie.position_source_forcee))
                            return  # Arrêt de la méthode

                    #  Affichage d'un message d'erreur pertinent en cas d'erreur:
                    else:
                        self.messages['foreground'] = 'red'
                        self.messages[
                            'text'] = 'Erreur: Vous devez sélectionner une pièce pouvant faire une prise.'

                # Si le joueur n'est pas contraint dans son choix de position source:
                else:
                    self.position_selectionnee = Position(ligne, colonne)
                    self.partie.position_source_selectionnee = self.position_selectionnee

                # On récupère l'information sur la pièce à l'endroit choisi.
                self.piece_selectionnee = self.partie.damier.recuperer_piece_a_position(
                    self.position_selectionnee)

                # Affichage d'un message pertinent si le joueur à sélectionner une pièce source valide:
                if self.piece_selectionnee is not None:
                    self.messages['foreground'] = 'black'
                    self.messages[
                        'text'] = 'Pièce sélectionnée à la position {}.'.format(
                            self.position_selectionnee)
                    self.bool_piece_selectionnee = True

            # Affichage d'un message d'erreur si la position source n'est pas valide et arrêt de la méthode:
            else:
                self.messages['foreground'] = 'red'
                self.messages['text'] = self.partie.position_source_valide(
                    Position(ligne, colonne))[1]
                return  # Arrêt de la méthode

            # On affiche le damier mis a jour.
            self.canvas_damier.actualiser()