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()
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
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 !
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 !
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 !
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.
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
ç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.
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.
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.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.