Motus en python 2.6 avec tksnack

Description

J'ai tenté de reproduire le célèbre Motus télévisé en Python2.6
Faites moi part de vos suggestions d'amélioration et critiques quant au code.
J'ai scindé le travail en 2 parties : le programme principal et 1 classe pour les grilles qui contiennent
les fameuses boules jaunes et noires.
En espérant qu'il ne reste pas trop de bugs.
Cordialement.
Mints.

Source / Exemple :


# -*- coding:UTF-8 -*-
################################################################################
#                                                                              #
#                                 Motux                                        #
#                                                                              #
#                            Motus'like game                                   #
#                              version 1.2                                     #
#                           language : Python 2.6                              #
#                         auteur : Guillaume Michon                            #
#                            date : 14/10/2012                                 #
#                                                                              #
################################################################################
from Tkinter import *
import tkSnack
from grilleLoto2 import *

#-------------------------------------------------------------------------------
def NouvellePartie(fichierMots):
    """Appelle la fonction de chargement de mots.
       Crée l'espace de jeu.
       Appel l'initialisation des grilles de loto.
       Donne la main au joueur 1 par défaut."""
    global tableauDeCanevas,equipeCourante,airDessin
    
    Charger(fichierMots)

    airDessin.destroy() # A chaque nouvelle partie on supprime cette Frame contenant les canvas
    airDessin = Frame(fen) # pour en recréer une autre en fonction du nouveau nombre de lettres

    # Remplissage du tableau de canvas
    tableauDeCanevas = []
    for ligne in range(6):
        tabCanvasLigne = []
        for colonne in range(nbrLettres):
            canvas = Canvas(airDessin,bg='blue',height=50,width=50,borderwidth=5,relief='ridge')
            canvas.grid(row=ligne,column=colonne)
            tabCanvasLigne.append(canvas)
        tableauDeCanevas.append(tabCanvasLigne)
    airDessin.grid(row=1,column=1)    

    # Reinitialisation des grilles des joueurs
    for equipe in equipes:
        equipe.score = 00
        equipe.labelScore.configure(text="Score : "+str(equipe.score))
        equipe.labelEquipe.configure(text=" ")
        equipe.configure(bg='white')
        equipe.Reinitialiser()
        equipe.boutonTirage.config(state='disable')  
        
    equipeCourante = 0 # equipe qui a la main
    equipes[equipeCourante].labelEquipe.configure(text=" A VOUS !")
    equipes[equipeCourante].configure(bg='maroon')

    NouveauMot() 

def Charger(fichier_mots):
    """Charge le fichier de mots correspondant au nombre de lettres désiré
       dans une liste primaire qui permettra de vérifier si le mot tapé existe,
       et dans une liste secondaire de laquelle il sera retiré pour éviter les répétitions."""
    global listeMots, listeSecondaire,nbrLettres

    fichier = open(fichier_mots,'r')
    listeMots = []
    while 1:
        mot = fichier.readline()
        if mot == "":
            break
        listeMots.append(mot[:-1])
    nbrLettres = len(listeMots[0])
    listeSecondaire = listeMots[:]

    fichier.close()

def SelectionMot():
    """Sélectionne un mot au hazard dans la liste de mots chargé et
    le retire de la liste secondaire pour éviter les répétitions."""
    global listeSecondaire

    hazard = randrange(0,len(listeSecondaire))
    motA_Trouver = listeSecondaire[hazard]
    del listeSecondaire[hazard]
    if not listeSecondaire:
        listeSecondaire = listeMots[:]

    return motA_Trouver

#-------------------------------------------------------------------------------
def NouveauMot():
    """Initialisation de toutes les variables de la grille de jeu lors de la recherche d'un nouveau mot."""

    global enigme,proposition,ligneCourante,colonneCourante,lettresBienPlacees,tableauDePropositions
    
    enigme = SelectionMot() # le mot à trouver
    print enigme

    proposition = '' # chaque lettre tappée viendra s'ajouter a cette chaîne de caractères 
    ligneCourante, colonneCourante = 0, 0 # coordonnées permettant de se déplacer dans le tableau de canvas

    # tableau qui contiendra toutes les lettres bien placées depuis le debut de la recherche
    lettresBienPlacees = ['']*nbrLettres
    # tableau qui contiendra toutes les propositions faites depuis le debut de la recherche
    tableauDePropositions = []

    EffaceCanevas() # On efface tous les canvas
    sonNouveauMot.play()
    lettresBienPlacees[0] = enigme[0] # la 1ère lettre du mot à trouver est toujours considérée comme bonne
    AttenteProposition() # on l'affiche et on donne la main au joueur

def EffaceCanevas():
    """Efface et remet en bleu tous les canvas présents dans l'air de jeu."""
    for ligne in range(6):
        for colonne in range(nbrLettres):
            tableauDeCanevas[ligne][colonne].delete(ALL)
            tableauDeCanevas[ligne][colonne].configure(bg="blue")    

#-------------------------------------------------------------------------------
def AttenteProposition():
    """Mise à jour de la ligne courante de la grille de jeu.
       Toutes les lettres bien placées au fur et à mesure des essaies
       ont été mémorisées dans lettresBienPlacees[], c'est ici qu'on les affiche.
       Liaison du clavier sur fonction LettreTapee."""
    
    compt = 0
    while compt < len(lettresBienPlacees) :
        couleur = 'blue'
        if lettresBienPlacees[compt] != '':
            couleur = 'red'
        tableauDeCanevas[ligneCourante][compt].delete(ALL)
        tableauDeCanevas[ligneCourante][compt].create_text(30,30,font='Century 28 bold ',text=lettresBienPlacees[compt])
        tableauDeCanevas[ligneCourante][compt].configure(bg=couleur)
        compt += 1

    fen.bind('<Key>',LettreTapee)

def LettreTapee(event):
    """Récupération de la lettre tapée(conversion en majuscule) et constitution de la proposition du joueur.
       Vérification de la position dans la grille de jeu."""
    global ligneCourante,colonneCourante,proposition,tableauDePropositions,correspondance

    tableauDeCanevas[ligneCourante][colonneCourante].delete(ALL)

    c = str.upper(event.char)
    if str.isalpha(c):
        # si la touche clavier est une lettre elle est ajoutée à la chaîne de caractères 'proposition' et l'indice colonneCourante est incrémenté
        proposition += c
        # et ce tant que celui-ci est inférieur aux nombres de lettres du mot recherché
        if colonneCourante < nbrLettres:
            tableauDeCanevas[ligneCourante][colonneCourante].create_text(30,30,font='Century 28 bold ',text=c)
            tableauDeCanevas[ligneCourante][colonneCourante].configure(bg='purple')
            colonneCourante += 1
        # le nombre de lettres est atteint
        if colonneCourante == nbrLettres:
            correspondance = []         # contient les couples [lettre de l'essaie,chiffre correspondant] où chiffre correspondant pourra prendre
            for lettre in proposition:  # 3 valeurs : 0 si lettre absente ; 1 si bien placée ; 2 si mal placée
                correspondance.append([lettre,0]) # par défaut on considère toutes les lettre mal placées
            tableauDePropositions.append(correspondance) # on ajoute cette correspondance au tableau total

            if proposition not in listeMots: # on vérifie si le mot entré est correct
                sonMotErrone.play()
                proposition = '' # dans le cas contraire on reinit la proposition 
                MajPropositions() # on réaffiche toutes les propositions
                LaMainPasse()    # et la main passe
                if ligneCourante != 5: # si on n'est pas sur la dernière ligne 
                    ligneCourante += 1 # on descend
                    AttenteProposition() # et on attend la futur proposition
                else:
                    AttenteProposition() # sinon on ne descend pas mais 
                    Aide()   # on affiche une lettre non bien placée jusqu'à présent
                colonneCourante = 0 
                return
            # le mot existe mais est-ce le bon ? la Verification nous renvera 'False' dans la négative
            elif not Verification(correspondance) : 
                proposition = '' # auquel cas on reinit la proposition 
                MajPropositions() # on réaffiche toutes les propositions           
                if ligneCourante != 5: # si on n'est pas sur la dernière ligne 
                    ligneCourante += 1 # on descend
                    AttenteProposition() # et on attend la futur proposition
                else: # si on n'est pas sur la dernière ligne 
                    LaMainPasse() # la main passe
                    AttenteProposition() #on attend la futur proposition
                    Aide() # on affiche une lettre non bien placée jusqu'à présent
                colonneCourante = 0
                return

#-------------------------------------------------------------------------------
def Verification(correspondance):
    """Algorithme principal
      Pour des raisons d'affichage et sonore le mot tapé est entré dans un tableau
      de correspondance du type :
      [['lettre1',chiffre correspondant],['lettre2',chiffre correspondant],[]...]
      où chiffre correspondra à une couleur et un effet sonore lors de l'animation de la proposition.

      Le mot recherché(enigme) est transformé en liste : resteEnigme

      Dans un premier temps on compare les lettres du mot tapé et du mot à découvrir 2 à 2.
      Si il y a correspondance, le chiffre de ces lettres passe à 1 dans 'correspondance' et on les remplace dans la liste resteEnigme
      par des espaces pour signifier au reste de l'algorithme que la lettre n'est plus à prendre en compte et pour conserver le même
      nombre de lettres et éviter ainsi les décalage.
      On en profite pour remplir le tableau des lettres bien placées 'lettresBienPlacees' et on incrémente
      une variable locale 'lettresBienTapees'.Si celle-ci équivaut au nombre de lettres de l'enigme la solution est trouvée.
      Dans le cas contraire on compare succesivement chaque lettre de 'correspondance' dont le chiffre est different de 1 avec toutes celles restant
      dans resteEnigme.Lorsqu'il y a correspondance le chiffre de cette lettre passe à 2 et elle est remplacée par un espace dans resteEnigme.
      On a ainsi trouvé toutes les lettres présentes mais mal placées."""

    fen.unbind('<Key>')

    resteEnigme = list(enigme)

    compt = 0
    lettresBienTapees = 0
    while(compt < nbrLettres):
        #Lettres bien placées
        if correspondance[compt][0] == resteEnigme[compt]:
            correspondance[compt][1] = 1
            resteEnigme[compt] = " "
            lettresBienPlacees[compt] = enigme[compt]
            lettresBienTapees += 1
        compt += 1

    # Le mot est trouvé
    if lettresBienTapees == nbrLettres:
        MotTrouve()
        return True # On présice à la méthode appelante 'LettreTapee' que le mot est trouvé

    # Lettres présentes mais mal placées
    for couple in correspondance:
        if couple[1] == 1:
            continue
        for lettre in resteEnigme:
            if couple[0] == lettre:
                couple[1] = 2
                resteEnigme[resteEnigme.index(lettre)] = " "
                break

    # On anime chaque lettre de la proposition 
    for indexColonne in range(nbrLettres):
        fen.after(350,AnimationProposition(indexColonne,correspondance[indexColonne][0],correspondance[indexColonne][1]))

    return False # On présice à la méthode appelante 'LettreTapee' que le mot n'est pas trouvé 

#-------------------------------------------------------------------------------
def AnimationProposition(indexColonne=None,lettre=None,couleurEtSon=None):
    """Fonction qui écrie une lettre dans le canvas de coordonnée [ligneCourante][colonneCourante] dans le tableau de canvas."""

    # On retrouve ici les 3 actions distinctes à effectuer pour chaque lettre de la proposition du joueur
    if couleurEtSon == 1:
        couleur = 'red'
        son = beepRouge
    elif couleurEtSon == 2:
        couleur = 'yellow'
        son = beepJaune
    elif couleurEtSon == 0:
        couleur = 'blue'
        son = beepBleu

    son.play()
    tableauDeCanevas[ligneCourante][indexColonne].configure(bg=couleur)
    tableauDeCanevas[ligneCourante][indexColonne].create_text(30,30,font='Century 28 bold ',text=lettre)
    fen.update()

#-------------------------------------------------------------------------------
def MajPropositions():
    """Toutes les propositions ont été morisées dans le tableau tableauDePropositions.
    C'est ici qu'elles sont affichées.Si leur nombre dépasse 5, on copie le tableau sur lui même en enlevant la 1ère."""
    global tableauDePropositions

    EffaceCanevas()

    if len(tableauDePropositions) == 6:
        tableauDePropositions = tableauDePropositions[1:]

    ligne = 0
    for proposition in tableauDePropositions:
        for colonne in range(nbrLettres):
            if proposition[colonne][1] == 1:
                couleur = 'red'
            elif proposition[colonne][1] == 2:
                couleur = 'yellow'
            elif proposition[colonne][1] == 0:
                couleur = 'blue'
            tableauDeCanevas[ligne][colonne].create_text(30,30,font='Century 28 bold ',text=proposition[colonne][0])
            tableauDeCanevas[ligne][colonne].configure(bg=couleur)
        ligne += 1

#-------------------------------------------------------------------------------
def Aide():
    """Appelée en guise d'aide sur la dernière ligne de jeu pour afficher
    la première lettre non trouvée après que la main soit passée."""

    fen.unbind('<Key>')    
    sonAide.play()
    compt = 0
    while compt < nbrLettres:
        if lettresBienPlacees[compt] == '':
            lettresBienPlacees[compt] = enigme[compt]
            for j in range(5):
                fen.after(100,AnimationAide(compt,lettresBienPlacees[compt],'red'))
                fen.after(100,AnimationAide(compt,'','blue'))

            tableauDeCanevas[ligneCourante][compt].configure(bg='red')
            tableauDeCanevas[ligneCourante][compt].create_text(30,30,font='Century 28 bold ',text=lettresBienPlacees[compt])
            break
        compt += 1

    fen.bind('<Key>',LettreTapee)

def AnimationAide(indexColonne,lettre,couleur):
    tableauDeCanevas[ligneCourante][indexColonne].configure(bg=couleur)
    tableauDeCanevas[ligneCourante][indexColonne].create_text(30,30,font='Century 28 bold ',text=lettre)
    fen.update()

#-------------------------------------------------------------------------------
def MotTrouve():
    """Mise à jour du score et animation du mot trouvé."""
    fen.unbind('<Key>')

    for indexColonne in range(nbrLettres):
        fen.after(350,AnimationProposition(indexColonne,correspondance[indexColonne][0],correspondance[indexColonne][1]))

    equipes[equipeCourante].score += 50
    equipes[equipeCourante].labelScore.configure(text="Score : "+str(equipes[equipeCourante].score))

    sonMotTrouve.play()
    for j in range(3):
        for i in range(nbrLettres):
            fen.after(50,AnimationMotTrouve(i,'green'))
        for i in range(nbrLettres):
            fen.after(50,AnimationMotTrouve(i,'red'))

    equipes[equipeCourante].boutonTirage.config(state='active')   
    TirageBoules()

def AnimationMotTrouve(indexColonne,couleur):
    tableauDeCanevas[ligneCourante][indexColonne].configure(bg=couleur)
    fen.update()

#-------------------------------------------------------------------------------
def TirageBoules():
    """Un appel récurssif de cette fonction nous donne l'etat des membres des
    instances de GrilleLoto à chaque instant.3 cas possibles :
                                     1/ il y a motux
                                     2/ boule noire tirée
                                     3/ 2 boules jaunes tirées"""

    if equipes[equipeCourante].yaMotux and equipes[equipeCourante].finAnimationMotux:
        equipes[equipeCourante].score += 100
        equipes[equipeCourante].labelScore.configure(text="Score : "+str(equipes[equipeCourante].score))
        for equipe in equipes:
            equipe.yaMotux = False
            equipe.finAnimationMotux = False
            equipe.boutonTirage.config(state='disable')
        for equipe in equipes:    
            equipe.Reinitialiser()
        fen.after(1000,NouveauMot)
        return

    if equipes[equipeCourante].bouleNoireTiree :
        equipes[equipeCourante].boutonTirage.config(state='disable')
        equipes[equipeCourante].bouleNoireTiree = False
        equipes[equipeCourante].nbrBoulesJaunesTirees = 0        
        fen.after(1000,LaMainPasse)
        fen.after(1000,NouveauMot)
        return

    if equipes[equipeCourante].nbrBoulesJaunesTirees == 2 and not equipes[equipeCourante].yaMotux:
        equipes[equipeCourante].nbrBoulesJaunesTirees = 0
        fen.after(1000,NouveauMot)
        return

    fen.after(50,TirageBoules)

#-------------------------------------------------------------------------------    
def LaMainPasse():
    """Fonction qui met a jour les grilles de loto et qui passe la main à l'autre joueur."""
    global equipes, equipeCourante

    equipes[equipeCourante].labelEquipe.configure(text=" ")
    equipes[equipeCourante].configure(bg='white')

    equipeCourante += 1
    if equipeCourante == 2:
        equipeCourante = 0
    
    equipes[equipeCourante].labelEquipe.configure(text=" A VOUS !")
    equipes[equipeCourante].configure(bg='maroon')

#-------------------------------------------------------------------------------
def Presentation():
    """Ecrit le mot "MOTUX" sur toutes les lignes de la grille de jeu instanciation de 2 GrilleLoto."""
    global airDessin,equipes

    motux = "MOTUX"
    tableauDeCanevas = []
    nbrLettres = 5
    airDessin = Frame(fen)

    tableauDeCanevas = []
    for x in range(6):
        tabCanvasLigne = []
        for y in range(nbrLettres):
            canvas = Canvas(airDessin,bg='blue',height=50,width=50,borderwidth=5,relief='ridge')
            canvas.create_text(30,30,font='Century 28 bold ',text=motux[y])            
            canvas.grid(row=x,column=y)
            tabCanvasLigne.append(canvas)
        tableauDeCanevas.append(tabCanvasLigne)

    airDessin.grid(row=1,column=1)

    numerosGrille1 = ['7','17','35','49','57','11','25','29','51','59','3','19','41','43','69','5','21','33','53','63','9','23','37','45','61']
    numerosGrille2 = ['12','26','40','52','60','10','22','30','46','58','8','18','32','44','70','6','16','38','54','62','2','20','36','56','66']

    equipes = []
    
    equipes.append(GrilleLoto(numerosGrille1))
    equipes[0].grid(row=1,column=0)
    equipes.append(GrilleLoto(numerosGrille2))
    equipes[1].grid(row=1,column=2)

#-------------------------------------------------------------------------------
def Quitter():
    fen.quit()    
    fen.destroy()

#-------------------------------------------------------------------------------
fen = Tk()
fen.title("Motux")
fen.resizable(0,0)

barreMenu = Menu(fen)
options=Menu(barreMenu)
barreMenu.add_cascade(label="Nouvelle Partie",menu=options)
options.add_command(label = "Mots de 6 lettres",command=lambda : NouvellePartie("mots6.txt"))
options.add_command(label = "Mots de 7 lettres",command=lambda : NouvellePartie("mots7.txt"))
options.add_command(label = "Mots de 8 lettres",command=lambda : NouvellePartie("mots8b.txt"))
options.add_command(label = "Quiter",command=Quitter)
fen.config(menu=barreMenu)

tkSnack.initializeSnack(fen)
sonNouveauMot = tkSnack.Sound(file='nouveau_mot.wav')
sonAide = tkSnack.Sound(file='aide.wav')
sonMotTrouve = tkSnack.Sound(file='kultur0407.wav')
sonMotErrone = tkSnack.Sound(file='incorrect.wav')
beepRouge = tkSnack.Sound(file='beep-3.wav')
beepJaune = tkSnack.Sound(file='beep-6.wav')
beepBleu = tkSnack.Sound(file='beep-4.wav')

Presentation()
fen.mainloop()

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.