IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Programmation multimédia/Jeux Python Discussion :

Jeux Simon en Python


Sujet :

Programmation multimédia/Jeux Python

  1. #1
    Nouveau membre du Club
    Femme Profil pro
    Bordeaux
    Inscrit en
    Juillet 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Bordeaux
    Secteur : Bâtiment

    Informations forums :
    Inscription : Juillet 2012
    Messages : 24
    Points : 36
    Points
    36
    Par défaut Jeux Simon en Python
    Un Simon en python en une petite centaine de lignes..
    Testé sous Ubuntu et Windows 10.
    Fonctionne aussi sur smartphone, en installant l'appli PyDroid (il faut enlever ligne 109 l'option font=('sans', 20, 'bold').
    Le code n'est peut-être pas exemplaire pour les puristes en Python, mais fonctionnel si cela peut servir à certains.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    from tkinter import *
    from tkinter.constants import *
    import random
    from tkinter import messagebox
    """
            Reproduire la séquence de couleurs proposée par le PC
            La vitesse de clignotement est paramétrable par
            l'intermédiaire de deux RadioButtons.
            Les couleurs sont numérotées de 0 à 3 dans le sens
            haut gauche à bas droite.
    """
    def Ombre(ButClic):
        listeB[ButClic].configure(background=listeCoul[ButClic])  
     
    def JeuxHM(BoutClic):
        global compte
        global compteJH
        global flag
        global listeClic
        if compteJH <compte:               
                    compteJH+=1
                    fen.bell()
        if BoutClic!=listeClic[compteJH-1]: #dernier clic à comparer ds la liste faite par le PC
                    messagebox.showinfo("PERDU",listeClic)
                    b1.config(state='normal')
                    compteJH=0 #remise à zéro et sortir de la fonction jeuHM
                    compte=0
                    boucle=0
                    flag="jeuPC"
                    listeClic[:]=[]
                    message['text']=" En attente"
                    return
        if compteJH==compte:      #dernier clic joueur ss erreur  donc passe la main à JeuxPC()  
                    flag="jeuPC"      # je change le flag
                    fen.title("SIMON  Score={}".format(compteJH))
                    JeuxPC()
     
    def JeuxPC():
        global compte
        global compteJH
        global flag
        global boucle
        global varChoix
        global TimerC
        if flag=="jeuPC": #premier tirage avant guirlande
            for nbBut in range(0,4):    # Boutons inactifs
                            listeB[nbBut].configure(state=DISABLED)
            if varChoix.get()=="Norm":
                TimerC=800
            else:
                TimerC=200
            #print(TimerC,flag)
            message['text']=" PC joue..."
            compte+=1   #compteur des jeux PC (0,1,2,3,4...)
            compteJH=0  #compteur des jeux Humain
            boucle=0 #guirlande à comparer au nombre de jeux PC
            valCoul=random.randint(0,3) # nouveau tirage
            listeClic.append(valCoul)  # je rajoute à listeClic
            #print(listeClic)
            flag="jeuHM" # prochain passage avec fen.after(1000, JeuxPC)=suite guirlande
        if boucle<compte:
            #print(boucle,compte,listeClic[boucle])
            listeB[listeClic[boucle]].configure(background=listeCoulOmb[listeClic[boucle]])
            fen.after(TimerC, lambda x=listeClic[boucle]: Ombre(x)) # clignotement du pavé
            boucle+=1
        else: # donc boucle=compte je passe la main après la guirlande aux évènements sur Button
            message['text']=" Humain joue..."
            for nbBut in range(0,4):    # Boutons actifs
                            listeB[nbBut].configure(state=NORMAL)
            return # sortie de JeuxPC et en attente évènement sur un Button (JeuxHM())
        fen.after(1000, JeuxPC)
     
    fen = Tk()
    fen.title("SIMON")
    listeCoul=['red','blue','green','yellow'] #couleurs de base
    listeCoulOmb=['#d93199','#0090d9','#00d9c4','#f4d9a6'] # couleur de clignotement
    listeClic=[]
    listeB=[] # liste des 4 boutons cliquables (référence objet tkinter)
    htB=15
    lgB=25
    taille_grille=2
    for y in range(taille_grille) :    # créatiob de 4 boutons cliquables
            for x in range(taille_grille) :
                b = Button(fen,
                           width=lgB,
                           height=htB,
                           background=listeCoul[(y*2)+(x+1)-1], 
                           activebackground=listeCoul[(y*2)+(x+1)-1],
                           command=lambda ButClic=(y*2)+(x): JeuxHM(ButClic))#de 0 à 3
                listeB.append(b)
                b.grid(row=y, column=x)
    #print(listeB)
    varChoix=StringVar()
    boutNormal = Radiobutton(fen,
    		 text = "Normal",
    		 variable =varChoix,
    		 value = "Norm"
                        )
    boutNormal.select()
    boutHauteur = Radiobutton(fen,
    		 text = "Rapide",
    		 variable =varChoix,
    		 value = "Rap"
                     )
    boutNormal.grid(row=4,column =0,sticky="NSWE")
    boutHauteur.grid(row=4,column =1,sticky="NSWE")
    b1=Button(fen,      text="Jouer",command=JeuxPC)
    b1.grid(row=5,column=0,columnspan=2,sticky="NSWE")
    message = Label(fen,font=('sans', 20, 'bold'),text="En attente")
    message.grid(row=3,column=0,columnspan=2,sticky="NSWE")
    compte=0
    flag="jeuPC"
    fen.mainloop()

  2. #2
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut Utiliser un canevas c'est mieux
    Salut.

    C'est bien, mais toujours très confus, trop de global dans tes fonctions, mieux vaut utiliser des itérables en essayant de grouper ce qui peut l'être.
    Il y a toujours cette erreur lorsqu'on clique sur une couleur sans avoir débuté une partie.
    Il est plus que préférable de ne pas faire d'import * de tout un module.

    Il n'y a pas besoin d'utiliser de fonction anonymes dans les méthodes after, celle-ci passant directement en paramètres de la fonction les arguments stipulés à la suite. Ce qui fait que :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fen.after(TimerC, lambda x=listeClic[boucle]: Ombre(x)) # clignotement du pavé
    Peut être remplacé avantageusement par :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    fen.after(TimerC, Ombre, listeClic[boucle]) # clignotement du pavé
    D'ailleurs ta fonction Ombre devient même inutile en sachant cela.

    Les boutons, c'est très limité en ce qui concerne apparences et formes, l'utilisation d'un canevas permet de faire des choses plus jolies et de formes plus variées pour les représentations des boutons de couleurs, notamment des quarts de cercles.

    Tkinter, c'est cool, mais au bout d'un moment, même pour faire un jeu aussi simple que cela, il serait avantageux d'utiliser une bibliothèque destinée à faire des jeux, cela permettrait de jouer des sons variés (le bell ne fonctionne pas partout) en même temps que les couleurs et avoir des effets graphiques plus élaborés

    Bonne continuation.
    Le temps ronge l'amour comme l'acide.

  3. #3
    Nouveau membre du Club
    Femme Profil pro
    Bordeaux
    Inscrit en
    Juillet 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Bordeaux
    Secteur : Bâtiment

    Informations forums :
    Inscription : Juillet 2012
    Messages : 24
    Points : 36
    Points
    36
    Par défaut
    Merci pour les conseils.
    Effectivement, clic actif avant de lancer le jeu, je n'avais pas testé ;(, la solution à mettre en place est simple.
    Pour le passage de paramètres à la fonction lancée par after, je ne savais pas.
    Pour les Buttons tkinter, ds la première version que j'avais postée ds le fil GUI, j'utilisais 4 rectangles ds un Canvas. Mais comme je buttais sur un problème de gestion des évènements (le programme tournait, mais pendant la proposition du PC, le clic souris était tjs actif...malgré mes efforts...et à l'arrivée c'était un mélange de .bind de timer() et d'update()...une soupe!) j'avais tout réécris avec des Buttons.
    Pour les imports j'ai déjà fait le changement, en fait j'ai fait un .exe avec Pyinstaller (option --onfile) et comme le lancement était un peu long j'ai importé le strict nécessaire (Button, Label...)
    Ce qui m'intéresse c'est le sujet des itérables, je ne vois pas trop la solution alternative sans basculer ds la POO?

  4. #4
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Salut.

    Que tu importes quelques widgets de tkinter au lieu de tkinter en entier, cela ne change rien, le module entier sera importé tout de même sans compter ce que le module que tu importes, importe lui-même, os, sys, etc.

    Lorsque je faisais allusion à des itérables, c'est plus une façon de dire que les conteneurs comme list, dict sont globals et accessibles depuis l'intérieur d'autres focntions et on peut donc en modifier les éléments depuis l'intérieur des fonctions,

    En lisant ton code, on peut déjà voir que l'utilisation de global sur varChoix et listeClic sont inutiles, d'autres comme compte, compteJH, c'est pas sensationnel, il serait mieux de comparer les listes en elles-mêmes ou un élément précis via un indice.

    Un exemple (fait rapidos) améliorable de ce que ça peut donner avec un canvas.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    import tkinter as tk
    import random
    # nécessaire pour version de ptyhon < 3.6 sinon dict suffit
    from collections import OrderedDict
     
    def jouerSequence() :
        ''' Ajoute une couleur à la séquence et les joue ''' 
        # Choix d'un identifiant couleur au pif
        iid = random.choice(couleursIds)
        # Ajout de l'identifiant à la séquence à répéter
        sequence.append(iid)
        # Init du délai
        delai = 0
        for iid in sequence :
            delai += 300
            # Récupération de la couleur de l'identifiant choisi
            couleur = can.itemcget(iid, 'fill')
            # Changement de la couleur un bref instant
            can.after(delai, can.itemconfigure, iid, {'fill':COULEURS[couleur]})
            delai += 300
            # Remise à la couleur d'origine
            can.after(delai, can.itemconfigure, iid, {'fill':couleur})
            delai += 300
        # Appel de la fonction d'activation des clics joueurs
        can.after(delai, activerClic)    
     
     
    def validerSequence(evt) :
        ''' Validation de la répétition par le joueur de la séquence de couleurs jouée '''
        # Si le clic n'est pas autorisé, on ne fait rien
        if not statuts['clic'] : return
        # Récupération sur le canevas des ids des items contenant le point x, y du clic
        ids = can.find_overlapping(evt.x, evt.y, evt.x, evt.y)
        # Parcours de ces ids
        for iid in ids :
            # Si l'id est dans la liste des ids des couleurs, interruption de la boucle
            if iid in couleursIds :
                break
        else :
            # La totalité de l'itérable ids a été parcouru sans concordance avec les
            # ids des couleurs, rien à faire de plus
            return
     
        # Si l'id de l'item cliqué vaut l'élément de séquence ayant l'indice en cours
        if sequence[statuts['indice']] == iid :
            # Récupération de la couleur de l'item cliqué
            couleur = can.itemcget(iid, 'fill')
            try :
                # Tentative de mise à jour de la couleur
                can.itemconfigure(iid, fill=COULEURS[couleur])
            except KeyError :
                # clic trop rapide (couleur n'ayant pas eu le temps de se réinitialiser)
                return
            # Remise en état de la couleur d'origine de l'item cliqué
            can.after(200, can.itemconfigure, iid, {'fill':couleur})
            # Ajout de l'identifiant dans la séquence du joueur
            sequenceJoueur.append(iid)
            # Si les 2 séquences sont identiques
            if sequenceJoueur == sequence :
                # Remise à l'état de démarrage des valeurs
                initialiser()
                # Rappel de la fonction après un petit délai
                can.after(500, jouerSequence)
            else :
                # Donc la séquence joueur n'est pas encore identique
                # Incrémentation de l'indice en cours de vérification, afin de 
                # vérifier l'élément suivant au prochain clic
                statuts['indice'] += 1
        else :
            # La couleur cliquée est donc mauvaise => fin de la partie
            terminerJeu()
     
     
    def activerClic() :
        ''' Active la prise en compte des clics joueurs '''
        # Autorisation des clics joueur
        statuts['clic'] = True
        # Petit message indiquant que le joueur peut commencer à répéter la séquence
        labelInfo['text'] = 'À VOUS !'
     
     
    def initialiser() :
        ''' Initialisation des valeurs avant lancement d'une séquence '''
        # Interdiction de valider les clics joueurs
        statuts['clic'] = False
        # Initialisation de l'indice de la séquence à répéter
        statuts['indice'] = 0
        # Purge de la liste des séquences saisies par le joueur
        sequenceJoueur.clear()
        # Initialisation du texte label à une chaîne vide
        labelInfo['text'] = ''
     
     
    def terminerJeu() :
        ''' Fin de partie '''
        # Interdiction de prendre en compte les clics joueur
        statuts['clic'] = False
        # Affichage du message stipulant que le joueur a perdu
        labelInfo.configure(bg='red', fg='white', text='PERDU !')
        # Activation du bouton jouer, afin que le joueur puisse faire une autre partie
        boutonJouer['state'] = tk.NORMAL
     
     
    def jouer() :
        ''' Initialisation et lancement du jeu '''
        # Désactivation du bouton jouer
        boutonJouer['state'] = tk.DISABLED
        # Init des couleurs du label
        labelInfo.configure(bg='black', fg='green')
        # Purge de la liste des séquences
        sequence.clear()
        # Amorce du jeu
        initialiser()
        jouerSequence()
     
     
    # Constantes
    LARGEUR_COULEUR = 400
    HAUTEUR_COULEUR = 400
     
    COULEURS = OrderedDict((
        ('darkRed', 'red'),
        ('darkBlue', 'blue'),
        ('darkGreen', 'green'),
        ('goldenrod', 'yellow'),
    ))
     
    # Variables globales
    sequence = [] # Séquence jouée par l'ordi
    sequenceJoueur = [] # Séquence jouée par le joueur
    couleursIds = [] # identifiants des items couleurs du canevas
    statuts = dict(
        clic=False, # Stipule si les clics joueurs doivent être validés
        indice=0, # Indice de la séquence courante à répéter
    )
     
    # Création de tous les widgets tkinter
    root = tk.Tk()
    root['bg'] = 'black'
    can = tk.Canvas(
        root, width=LARGEUR_COULEUR, height=HAUTEUR_COULEUR, bg='black',
        highlightthickness=0
    )
    can.grid(padx=10, pady=10)
    can.bind('<Button-1>', validerSequence)
     
    # Création d'un itérateur simple des couleurs (clés du dict)
    icouleurs = iter(COULEURS)
    # Création des 4 quarts de couleurs
    for start in range(90, 90*4+1, 90) :
        # Obtention de la couleur via l'itérateur
        couleur = next(icouleurs)
        # Quarts de cercles principaux et >> RÉACTIFS AUX CLICS <<
        iid = can.create_arc(
            0, 0, LARGEUR_COULEUR, HAUTEUR_COULEUR,
            extent=90, start=start, fill=couleur, outline=couleur
        )
        # Ajout de l'identifiant à la liste des ids
        couleursIds.append(iid)
        # Quarts de cercles centraux et >> NON RÉACTIFS AUX CLICS <<
        can.create_arc(
            LARGEUR_COULEUR/2-30, HAUTEUR_COULEUR/2-30,
            LARGEUR_COULEUR/2+30, HAUTEUR_COULEUR/2+30,
            extent=90, start=start, fill=COULEURS[couleur],
            state=tk.DISABLED, outline=COULEURS[couleur]
        )
        # Arcs de cercles externes et >> NON RÉACTIFS AUX CLICS <<
        can.create_arc(
            4, 4, LARGEUR_COULEUR-4, HAUTEUR_COULEUR-4,
            extent=90, start=start, style=tk.ARC, fill=COULEURS[couleur],
            width=5, state=tk.DISABLED, outline=COULEURS[couleur]
        )
     
    # Texte destiné à afficher succinctement les messages du jeu. 
    labelInfo = tk.Label(root, bg='black', fg='green', font=('serif', -20, 'bold'))
    labelInfo.grid(sticky=tk.NSEW, ipady=5)
    # Bouton de démarrage du jeu
    boutonJouer = tk.Button(root, text="Jouer", command=jouer)
    boutonJouer.grid(sticky=tk.NSEW)
     
    root.mainloop()
    Le temps ronge l'amour comme l'acide.

  5. #5
    Nouveau membre du Club
    Femme Profil pro
    Bordeaux
    Inscrit en
    Juillet 2012
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Bordeaux
    Secteur : Bâtiment

    Informations forums :
    Inscription : Juillet 2012
    Messages : 24
    Points : 36
    Points
    36
    Par défaut
    Bonjour,
    Merci pour cet exemple, j'ai passé 2h ce matin à suivre la logique du programme.
    Effectivement l'IHM est plus élaborée.
    Par pur exercice de style je vais modifier mon programme en supprimant les variables globales au maximum.
    La tournure For... Else: je ne la connaissais pas (j'avais dû la lire mais pas retenue).
    J'ai balancé quelques print() pour comprendre la logique du programme: ds la fonction validerSequence, tu récupères
    l'ids de l'objet cliqué par can.find_overlapping. L'ids est renvoyé sous la forme d'une liste (ex (10,))
    A quoi bon itérer avec un for sur ids? (il n'y aura qu'un item de renvoyé?)
    En remplaçant: la boucle for... else... par:
    if not ids[0] in couleursIds:
    return
    et les iid suivants par ids[0] ça semble marcher!
    C'est juste pour comprendre, pas pour reprendre ce qui est mieux écrit que ce que j'ai pu faire...

  6. #6
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Salut.

    Citation Envoyé par brunop33 Voir le message
    l'ids de l'objet cliqué par can.find_overlapping. L'ids est renvoyé sous la forme d'une liste (ex (10,))
    A quoi bon itérer avec un for sur ids? (il n'y aura qu'un item de renvoyé?)
    En remplaçant: la boucle for... else... par:
    if not ids[0] in couleursIds:
    return
    et les iid suivants par ids[0] ça semble marcher!
    C'est juste pour comprendre, pas pour reprendre ce qui est mieux écrit que ce que j'ai pu faire...
    C'est un choix comme un autre, et plus une pure précaution, imaginons qu'on ajoute sur une partie du canevas un autre item et qu'on oublie de le rendre inactif aux clics, au moins en ne s'appuyant pas aveuglément sur le premier élément de la liste et qui engendrerait une erreur s'il se situe en haut de la file, cette façon de faire s'assure qu'un identifiant couleur se situe dans la liste retournée par Canvas.find_overlapping, le for ... else est donc une façon de faire parmi d'autres.

    Tu peux d'ailleurs tester en enlevant les state=tk.DISABLED des autres items canvas et cliquer ensuite en jeu sur le bord ou centre des couleurs pour voir qu'il y aura alors plusieurs ids retournés et le premier ne sera pas celui d'une couleur.
    Le temps ronge l'amour comme l'acide.

Discussions similaires

  1. [Python 3.X] Jeux Android avec python
    Par sadouadama dans le forum Programmation multimédia/Jeux
    Réponses: 5
    Dernier message: 24/09/2018, 11h37
  2. Jeux d’échec en python
    Par justforone dans le forum Général Python
    Réponses: 10
    Dernier message: 27/11/2013, 12h47
  3. Python pour création de jeux vidéos?
    Par punkd dans le forum Programmation multimédia/Jeux
    Réponses: 6
    Dernier message: 15/02/2009, 23h23
  4. Quelles sont les entreprises aujoud'hui qui commercialisent des jeux en Python?
    Par zuzuu dans le forum Développement 2D, 3D et Jeux
    Réponses: 14
    Dernier message: 12/02/2008, 10h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo