Partage
  • Partager sur Facebook
  • Partager sur Twitter

Jeu du Pendu (version graphique)

mon code + un fichier pour l'essayer

Sujet résolu
    29 décembre 2010 à 15:07:30

    Bonjour !
    Après avoir demandé de l'aide sur le forum pour mon jeu du pendu (version console), je me suis lancer dans la conception d'un pendu avec interface graphique, le jeu est completement fini, j'ai essayé de le rendre le plus complet et le plus intuitif possible en y integrant quelques classes que j'ai personnalisé.
    il charge une liste de mots a partir d'un fichier texte, et gère l'affichage et la sauvegarde des joueurs avec le nombre de points gagné le tout exporté dans un fichier externe comme la version console, mais j'ai en plus ajouté quelques classes qui permettent d'afficher des fenetres satellites, un clavier pour pouvoir essayer les lettres et le changement des images qui affichent le pendu ... Pour les images et la disposition dans l'interface, je me suis inspirer du TP de cysboy a cette page:
    http://www.siteduzero.com/tutoriel-3-6 [...] e-penduz.html
    Je poste le code pour avoir l'avis des membres du forum sur ma façon de coder, les erreurs que j'ai pu faire, et si mon utilisation de la POO est bonne. Avant de m'attaquer a des projets un peu plus poussés, je voudrais faire le point !

    voici les classes:

    #!/usr/bin/python
    #-*- coding: UTF-8 -*-
    
    # Module de classes pour le jeu du pendu
    import pickle
    from random import randrange
    from tkinter import *
    
    
    class Score(object):
        """Gère les scores des Joueurs en fonction de leur nom"""
        def __init__(self):
            "Initialisation du dictionnaire de scores"
            try:
                fichier = open("scores.bin", "rb")
                self.scores = pickle.load(fichier)
                fichier.close()
            except IOError:
                self.scores = {}
            
        def sauver(self):
            "Sauvegarde du dictionnaire dans un fichier bin"
            fichier = open("scores.bin", "wb")
            pickle.dump(self.scores, fichier)
            fichier.close()
    
        def recherche(self, nom): # Différente pour la version console !
            "Recherche le score du joueur et au besoins le crée"
            if nom in self.scores:
                self.joueur = [nom, self.scores[nom]]
            else:
                self.scores[nom] = (0, 0)
                self.joueur = [nom, self.scores[nom]]
            return self.joueur
    
        def affiche(self):
            "Affiche les scores par ordre décroissants"
            ordre = []
            noms = list(self.scores.keys())
            valeurs = tuple(self.scores.values())
            for n in range(len(noms)):
                ordre.append([valeurs[n], noms[n]])
                ordre.sort()
                ordre.reverse()
            return ordre
    
        def incremente(self, nom, score):
            "Actualise le score du joueur dès qu'il finit une partie"
            if nom in self.scores:
                self.scores[nom] = score
    
    
    class Mot(str):
        """gere les mots qui doivent être trouvés par l'utilisateur"""
        def __init__(self):
            "Chargement des mots dans une liste"
            str.__init__(self)
            self.mots, self.malus = [], 1
            try:
                fichier = open("mots.txt", "r")
            except IOError:
                print("Le fichier 'mots.txt' est introuvable !")
            while 1:
                ligne = fichier.readline()
                if ligne == "":
                    break
                else:
                    self.mots.append(ligne[:-1])
            fichier.close()
    
        def pioche(self):
            "Tire au hasard un mot dans la liste et la renvoie"
            i = randrange(len(self.mots))
            mot = self.mots[i]
            self.mots.remove(mot)
            return mot
    
        def convert(self, mot):
            "Convertie le mot visible en mot avec *"
            l = len(mot)
            mot2 = mot[0] + ((l-2)*"*") + mot[l-1]
            return mot2
       
        def afficheLettre(self, car, mot, motcrypt):
            "Affiche sur le mot crypté la lettre trouvée"
            if car not in mot:
                self.malus+=1
            else:
                for n in range(1, len(mot)):
                    if car == mot[n]:
                        motcrypt = motcrypt[:n] + car + motcrypt[n+1:]
            return motcrypt
        
    
    class BoutonPerso(Button):
        """Bouton personalisé qui change de couleur quand on clique dessus"""
        def __init__(self, boss, ** Arguments):
            Button.__init__(self, boss, bg="royal blue", fg="white",
                          activebackground="light blue", activeforeground="black",
                            font=("Helvetica", 10, "bold"), **Arguments)
    
    
    class Clavier(Frame):
        """Clavier a integrer dans l'interface graphique"""
        def __init__(self, boss, command=None, ** Arguments):
            "Initialisation du clavier"
            Frame.__init__(self, boss, bd=1, **Arguments)
            self.command = command
            alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            colones = (0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,2,3,4,5,6,7)
            lignes = (0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2)
            for b in range(26):
                bou = BoutonPerso(self, text=" "+alphabet[b]+" ", command=lambda
                             arg =alphabet[b]: self.choix(arg))
                bou.grid(row=lignes[b], column=colones[b], padx=2, pady=3)
    
        def choix(self, lettre):
            "Execute la commande avec la lettre de l'alphabet choisie"
            self.command(lettre)
    
    class NouveauSat(Toplevel):
        """Fenetre satellite qui recuperera le nom du joueur"""
        def __init__(self, boss, **Arguments):
            "Initialisation de la fenetre"
            Toplevel.__init__(self, boss, bg="light grey", **Arguments)
            self.master.nom = ""
            self.master.bouC.configure(state=DISABLED)
            self.master.configMot("Appuyez sur 'Nouveau'")
            self.master.configChances("Appuyez sur 'Nouveau' et entrez votre Nom !")
            self.geometry("200x100+450+350") # dimensions et positionnement
            self.resizable(width=0, height=0) # non redimentionable
            self.transient(self.master) # "premier plan"
            self.champ = Entry(self)
            self.champ.focus_set() # Affichage du curseur
            self.champ.pack(padx=10, pady=15)
            self.champ.bind("<Return>", self.joueur) # Evenement pour valider !
            bouOk = BoutonPerso(self, text="  OK  ", command=self.joueur)
            bouOk.pack(side=LEFT, padx=20, pady=10)
            bouAn = BoutonPerso(self, text="Annuler", command = self.destroy)
            bouAn.pack(side=LEFT, padx=20, pady=10)
    
        def joueur(self, event=None): # avec le bouton "OK" ou l'event
            "Recupère le nom du joueur dans une variable"
            recup = self.champ.get()
            if len(recup) >2:
                self.master.nom = recup.strip().title()
                self.master.bouC.configure(state=NORMAL)
                self.destroy()
                self.master.chargement(self.master.nom)
            else:
                self.nom = ""
                self.master.bouC.configure(state=DISABLED) # Garde le bouton inactif
                self.destroy()
    
    class ScoreSat(Toplevel):
        """Fenetre satellite qui affichera les meilleurs scores"""
        def __init__(self, boss, **Arguments):
            "Initialisation de la fenetre"
            Toplevel.__init__(self, boss, bg="light grey", **Arguments)
            self.geometry("750x450+100+100") # dimensions et positionnement
            self.resizable(width=100, height=0) # non redimentionable
            self.transient(self.master) # "premier plan"
            self.can = Canvas(self, width=730, height=370, bg="dark grey",bd=2,
                              relief=GROOVE)
            self.can.pack(padx=10, pady=20)
            BoutonPerso(self, text="  OK  ", command=self.destroy).pack()
            self.scores() # Commande qui affiche les scores
    
        def scores(self):
            "Affichage des scores dans la fenetre"
            self.ordre = self.master.joueur.affiche()
            for e in range(10): # Affichage des 10 meilleurs
                if e < len(self.ordre): # si inferieur au nombre de joueurs
                    self.can.create_text(10, 20+(e*35), anchor=NW, font=("Comic", 20, "bold"),
                                         fill="royal blue", text="{}.  {} avec un score de: \
    {} pour {} mot(s)".format(e+1, self.ordre[e][1], self.ordre[e][0][0], self.ordre[e][0][1]))
    



    la classe pendu contenant l'interface graphique et le "moteur" du jeu:

    #!/usr/bin/python
    #-*- coding: UTF-8 -*-
    
    # Jeu du pendu (version Graphique)
    
    from classes import *
    
    class Pendu(Frame):
        """Fenetre graphique qui gère l'interface du programme"""
        def __init__(self, boss=None):
            "Initialisation de la fenetre"
            Frame.__init__(self)
            self.master.title("Jeu du Pendu !  version 1.0 par Dario")
            self.master.configure(bg="light grey")
            self.master.resizable(width=False, height=False) # Bloque le redim.
            self.aff = Canvas(width=400, height=400, bg="white") # Pour la photo
            self.aff.grid(row=0, column=3, rowspan=3,
                          columnspan=2)
            self.interrupteur = 0 # Initialisation de l'interrupteur !
            self.nom = ""
    
            #Initialisation des classes Mot() et Score()
            self.joueur = Score()
            self.enigme = Mot()
            
            
            # Affichage de la photo initiale
            self.photo = PhotoImage(file="image0.gif")
            self.affiche = self.aff.create_image(200, 200, image=self.photo)
    
            # Affichage du mot a l'aide d'un autre canvas
            self.affmot = Canvas(width=340, height=80, relief=SOLID, bg="grey",
                                 borderwidth=2)
            self.affmot.grid(row=0, column=0, columnspan=3, padx=30, pady=20,
                             sticky=N)
            self.ecran = self.affmot.create_text(170, 40, anchor=CENTER,
                                    text="Appuyez sur 'Nouveau'", fill="blue",
                                    font=("Times", 20, "bold"))
            #Insertion du clavier
            self.touches = Clavier(self.master, command=self.sequence)
            self.touches.grid(row=1, column=0, columnspan=3, padx=10, pady=15, sticky=N)
            self.touches.configure(bg="dark grey", bd=2, relief=GROOVE)
                    
            # Création de 3 boutons: Nouvelle partie, Scores et Commencer
            self.bouN = BoutonPerso(self.master, text="Nouveau", command=self.nouveau)
            self.bouN.grid(row=2, column=0, sticky=N)
            self.bouS = BoutonPerso(self.master, text="Scores", command=self.scores)
            self.bouS.grid(row=2, column=1, sticky=N)
            self.bouC = BoutonPerso(self.master, text="Commencer", state=DISABLED,
                                    command=self.commencer)
            self.bouC.grid(row=2, column=2, sticky=N)
            
            # Création d'un label avec le nombre de chances encore disponibles
            self.chances = Label(bg="light grey",
                    text="Appuyez sur 'Nouveau' et entrez votre Nom !")
            self.chances.grid(row=3, column=0, columnspan=4, sticky=W)
    
            # Création d'un bouton pour quitter l'application
            bouQ = BoutonPerso(self.master, text="Quitter",command=self.master.destroy)
            bouQ.configure(bg="red", activebackground= "orange")
            bouQ.grid(row=3, column=4, padx=5, pady=5, sticky=E)
            
    
        ########## Méthodes modifiant l'interface graphique ##########
    
        def configMot(self, mot):
            "Configure le mot a trouver dans le canvas"
            self.affmot.itemconfig(self.ecran, text=mot)
    
        def configChances(self, texte):
            "configure le texte a afficher dans le label"
            self.chances.configure(text=texte)
    
        def configImage(self, image):
            "Configure l'immage a afficher dans le Canvas"
            self.photo = PhotoImage(file=image)
            self.aff.itemconfig(self.affiche, image=self.photo)
            
    
        ########## Méthodes de la partie fonctionelle du jeu ##########
    
        def nouveau(self):
            "Initialise la partie avec le nom du joueur et active 'Commencer'"
            self.configImage("image0.gif") # Réaffichage de la photo initiale !
            self.nom = ""
            self.interrupteur = 0
            nouv = NouveauSat(self)
                
        def chargement(self, joueur):
            "Effectue les actions de chargement du joueur(après confirmation du nom"
            self.nom = joueur
            if len(self.nom) > 2:
                self.donnees = self.joueur.recherche(self.nom)
                self.configMot("Appuyez sur 'Commencer'")
                self.configChances("Bienvenu {}, Vous avez comptabilisé {} point(s) pour {} mot(s) !".
                                   format(self.nom, self.donnees[1][0], self.donnees[1][1]))
    
        def scores(self): # A faire !
            "Ouvre une fenetre qui affiche les meilleurs scores"
            self.scoreAff = ScoreSat(self)
    
        def commencer(self):
            "Démare la partie"
            self.configImage("image1.gif") # Image de début de partie
            self.enigme.malus = 1
            self.motClair = self.enigme.pioche()
            self.motCrypt = self.enigme.convert(self.motClair)
            self.configMot(self.motCrypt)
            self.statScore, self.statMot = self.donnees[1][0], self.donnees[1][1]
            self.interrupteur = 1 # Activation de l'interrupteur !
            self.bouN.configure(state=DISABLED) # Désactivation du bouton 'Nouveau'
            self.bouC.configure(state=DISABLED) # Désactivation du bouton 'Commencer'
            
    
        def sequence(self, lettre):
            "Effectue une sequence et met a jour l'interface en fonction de la lettre"
            if self.interrupteur: # Clavier actif que si interrupteur vrai !
                if self.motClair != self.motCrypt and self.enigme.malus <8:
                    self.motCrypt = self.enigme.afficheLettre(lettre.lower(),
                                                          self.motClair, self.motCrypt)
                    self.configMot(self.motCrypt)
                    self.configChances("Il ne vous reste plus que {} chances pour trouver le mot !".format(
                        8-self.enigme.malus))
                    self.configImage("image{}.gif".format(self.enigme.malus))
                if self.motClair == self.motCrypt: # Gagné !
                    self.statMot+=1
                    self.statScore = self.statScore + (100//self.enigme.malus) # MAJ score et mots
                    self.donnees[1] = (self.statScore, self.statMot)
                    self.joueur.incremente(self.donnees[0], self.donnees[1]) # MAJ du Score
                    self.joueur.sauver() # Effectue la sauvegarde
                    self.configChances("BRAVO ! Vous avez gagné !, votre score a été mis a jour !")
                    self.interrupteur = 0 # fermeture de l'interrupteur !
                    self.enigme.malus = None # Réinitialisation de la variable
                    self.bouN.configure(state=NORMAL) # Activation du bouton 'Nouveau'
                    self.bouC.configure(state=NORMAL) # Activation du bouton 'Commencer'
                elif self.enigme.malus >= 8:
                    self.statMot+=1 # incremente 1 au nombre de mots
                    self.donnees[1] = (self.statScore, self.statMot)
                    self.joueur.incremente(self.donnees[0], self.donnees[1]) # MAJ du Score
                    self.joueur.sauver() # Effectue la sauvegarde
                    self.configMot("PERDU !")
                    self.configChances("Vous avez perdu ! le mot était: {} !".format(self.motClair))
                    self.interrupteur = 0 # fermeture de l'interrupteur !
                    self.enigme.malus = None # Réinitialisation de la variable
                    self.bouN.configure(state=NORMAL) # Activation du bouton 'Nouveau'
                    self.bouC.configure(state=NORMAL) # Activation du bouton 'Commencer'
    
    if __name__ == "__main__":
        Pendu().mainloop()
    


    et si quelqu'un veut directement tester le jeu, je met un lien de téléchargement sous rapidshare:
    http://rapidshare.com/files/439808306/pendu_par_Dario.zip

    @ +++
    • Partager sur Facebook
    • Partager sur Twitter
      29 décembre 2010 à 23:10:53

      marche pô chez moi ...
      ubuntu 2.6.32
      python 2.6.5
      • Partager sur Facebook
      • Partager sur Twitter

      Python c'est bon, mangez-en. 

        29 décembre 2010 à 23:15:26

        Moi, il marche très bien chez moi, toutefois, tu devrais mettre ta liste de mots en tant que données binaires... car là tu n'as qu'à regarder les 2 lettres déjà existantes et chercher dans ta liste de mots préfaite et écrire la bonne réponse ^^
        • Partager sur Facebook
        • Partager sur Twitter
          30 décembre 2010 à 8:53:33

          Citation

          marche pô chez moi ...
          ubuntu 2.6.32
          python 2.6.5



          Oui, le jeu est écrit sous python 3 je vais essayer de l'adapter a python 2 mais je suis pas sur de réussir, je n'ai coder que sous python 3 !

          Citation

          Moi, il marche très bien chez moi, toutefois, tu devrais mettre ta liste de mots en tant que données binaires... car là tu n'as qu'à regarder les 2 lettres déjà existantes et chercher dans ta liste de mots préfaite et écrire la bonne réponse



          J'y ai penser mais je voulais laisser la possibilité de rajouter ou de changer les mot existants, j'ajouterais surement quelques amélioration au jeu notament, une barre de menu avec la possibilité d'editer la liste de mots dirrectement dans une options, et avec ça le fichier mot passera en binaire
          J'ai pensé aussi a désactiver les boutons "nouveau" et "commencer" pour éviter de recommencer une partie avant que celle en cours ne soit finie, vu que la sauvegarde du score ne se fait qu'a la fin, ça évite de zapper les mots qu'on trouve pas !

          Pour ma part, j'ai remarqué que quand on gagne, le programme ne le met pas suffisament en évidence et que le label qui donne les instruction du jeu est pas suffisament en évidence, j'améliorerais aussi ça !

          @ ++
          • Partager sur Facebook
          • Partager sur Twitter
            30 décembre 2010 à 23:39:32

            c'est normal que pdt le jeu il y est cela :o
            Image utilisateur


            mais l'interface est pas mal :lol:


            je retire ce que j'ai dit je l'ai fait tourner avec python 3 et maintenant c'est parfait beau boulot! :p
            • Partager sur Facebook
            • Partager sur Twitter
              31 décembre 2010 à 10:57:05

              Merci pour le compliment ça fait plaisir !

              C'est chiant ces problèmes de compatibilité entre les versions 2 et 3 de python, je ferais une petite amélioration du logiciel et j'essairais de faire une version compatible avec python 2.

              Il y aurais pas une bibliothèque qui convertie le code python 3 => en python 2 ? je sais que celle contraire existe !
              • Partager sur Facebook
              • Partager sur Twitter
                17 janvier 2011 à 17:03:40

                Bonjour, je ressort ce post parce que je suis passé sous python 2 et je veut convertir mon code. J'ai une erreur sur cette ligne:

                self.scores = pickle.load(fichier)
                


                elle passe parfaitement sous python3 mais pas sous python 2.7

                mon erreur est:

                self.scores = pickle.load(fichier)
                  File "C:\Python27\lib\pickle.py", line 1378, in load
                    return Unpickler(file).load()
                  File "C:\Python27\lib\pickle.py", line 858, in load
                    dispatch[key](self)
                  File "C:\Python27\lib\pickle.py", line 886, in load_proto
                    raise ValueError, "unsupported pickle protocol: %d" % proto
                ValueError: unsupported pickle protocol: 3


                Je ne vois vraiment pas ce qui peut merder, j'ai peut être mal cherché mais je pensais que cette partie de code ne changeais pas entre les 2 versions !

                Merci de votre aide !
                • Partager sur Facebook
                • Partager sur Twitter
                Anonyme
                  17 janvier 2011 à 19:57:43

                  Je ne suis pas sûr mais ça vaut le coup d'essayer.

                  C'est peut-être le protocole par défaut quand tu appelles la méthode dump.

                  Si tu ne spécifies pas le protocole, le protocole par défaut dans la version 2.7 est le 0, alors que dans la version 3, c'est 3.

                  Donc faut voir avec (j'y ai jamais touché) et tester.

                  Edit : Euh je ne sais pas si je me suis bien fais comprendre, ce que je veux dire par "faut voir avec", je parlais de rajouter un protocole correct à ta méthode dump.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    18 janvier 2011 à 8:45:49

                    Oula je pensais que j'aurais autant de soucis entre une version et une autre, je n'avais encore jamais eu ce genre d'erreur avant, et du coups je suis bloqué dans la conversion de mon programme.
                    Possible que le problème vienne du fait que le fichier a charger ai été dumpé sous python 3 ?

                    EDIT: C'est bon, le problème venait du fait que le fichier binaire a été créer sous python 3, dès que je l'ai supprimer du répertoire, ça a marché ! J'ai perdu des heures a me prendre la tête pour rien :waw:
                    • Partager sur Facebook
                    • Partager sur Twitter
                    Anonyme
                      18 janvier 2011 à 17:59:17

                      Le fichier binaire est bien en cohérence avec le protocole choisi. ;)

                      Tu peux donc mettre un petit résolu
                      • Partager sur Facebook
                      • Partager sur Twitter
                        20 janvier 2011 à 12:06:13

                        ça y est, j'ai converti mon programme en python 2.x du coups j'ai pu le "compiler" très facilement avec py2exe.
                        j'y est apporté quelques améliorations, j'en profite pour mettre une archive si quelqu'un veut l'essayer et me donner son avis.

                        http://www.megaupload.com/?d=2VBKT77P

                        @ +++
                        • Partager sur Facebook
                        • Partager sur Twitter
                          20 janvier 2011 à 13:33:32

                          Citation : Darioo2

                          ça y est, j'ai converti mon programme en python 2.x du coups j'ai pu le "compiler" très facilement avec py2exe.



                          oué ... et le reste de la population ?
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Python c'est bon, mangez-en. 

                            20 janvier 2011 à 13:41:57

                            Citation : josmiley

                            Citation : Darioo2

                            ça y est, j'ai converti mon programme en python 2.x du coups j'ai pu le "compiler" très facilement avec py2exe.



                            oué ... et le reste de la population ?



                            C'est dans la boite tout le monde peut l'essayer, meme ceux qui n'ont pas installé python !
                            • Partager sur Facebook
                            • Partager sur Twitter
                              2 avril 2011 à 22:06:01

                              Bonjour,

                              je débute Python et j'aimerai savoir avec quel éditeur de texte du code et quel framework tu utilises pour ton interface graphique ?

                              Merci d'avance.
                              • Partager sur Facebook
                              • Partager sur Twitter

                              Jeu du Pendu (version graphique)

                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                              • Editeur
                              • Markdown