Jeu pygame fonction ex

Résolu/Fermé
yadg57 - Modifié le 10 nov. 2021 à 16:00
mamiemando Messages postés 33407 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 29 novembre 2024 - 10 nov. 2021 à 16:30
Bonjour,

J'ai trouvé un jeu pygame sur le net qui, lorsque l’on clique sur une case, inverse son fond et celui des cases adjacentes :
  • si la case est en milieu de damier, son fond change ainsi que celui des 8 cases qui l'entourent ;
  • si la case est le long d'un bord mais pas dans un coin du damier, son fond change ainsi que celui des 5 cases qui l'entourent ;
  • si la case est dans un coin du damier, son fond change ainsi que celui des 3 cases qui l'entourent.


Le but du jeu est donc d'obtenir un damier dont toutes les cases sont de la même couleur.

Le problème c'est que je n'ai pas compris deux fonctions notamment la plus longue (
execute
) si quelqu'un pourrait bien me les expliquer merci!

Voici le code:

import pygame
from pygame.locals import *
import numpy as np
import pickle  ##pour enregistrer les solutions connues


#ATTENTION POUR TESTER LE JEU IL FAUT CLIQUER SUR START
class Le_Jeu(object):

    def __init__(self, largeur=1200, hauteur=800, fps=60):
        """Initialiser pygame, fenêtre, arrière-plan, police,...
           arguments par défaut
        """
        pygame.init()
        pygame.display.set_caption("Le jeu du casse tête")
        self.largeur = largeur
        self.hauteur = hauteur
        self.screen = pygame.display.set_mode((self.largeur, self.hauteur), pygame.DOUBLEBUF)
        self.background = pygame.Surface(self.screen.get_size()).convert()
        self.background.fill((255,255,255)) # arrière plan blanc
        self.clock = pygame.time.Clock()
        self.fps = fps
        self.playtime = 0.0
        self.font = pygame.font.SysFont('mono', 24, bold=True)
        self.initialMat = np.zeros((8,8)).astype(int)
        # ------------- création des boutons initiaux ----------
        button = self.font.render('Solve', True, (0,255,0), (0,0,128))#nous n'avons malheuresement pas réussi cette fonction qui permettrait de résoudre automatiquement le jeu
        buttonRect = button.get_rect()
        buttonRect.center = (500,500)
        self.background.blit(button,buttonRect)
        for num in range(4,9):
            numButton = self.font.render(' '+ str(num) + ' ',True,(0,255,0), (0,0,128))
            numButtonRect = numButton.get_rect()
            numButtonRect.center = (-150 + 50*num, 500)
            self.background.blit(numButton,numButtonRect)
        mess = self.font.render('Choisir la taille', True, (0,0,0), (255,255,255))
        messRect = mess.get_rect()
        messRect.center = (150,470)
        self.background.blit(mess,messRect)
        randButton = self.font.render('Random', True, (0,255,0),(0,0,128))
        randRect = randButton.get_rect()
        randRect.center = (340,500)
        self.background.blit(randButton,randRect)
        setupButton = self.font.render('START', True, (0,255,255), (250,0,250))
        setupRect = setupButton.get_rect()
        setupRect.center = (425,500)
        self.background.blit(setupButton,setupRect)

    def verification(self,Mat = np.zeros((5,5))):
        ##Vérifie si le jeu peut être résolue
        size = np.sqrt(len(Mat))
        if size == 4:
            verif1 = np.array([1,0,0,0, 1,1,0,0, 1,0,1,0, 0,1,1,1])
            verif2 = np.array([0,0,0,1, 0,0,1,1, 0,1,0,1, 1,1,1,0])
            verif3 = np.array([0,1,0,0, 1,1,1,0, 0,0,0,1, 1,1,0,1])
            verif4 = np.array([0,0,1,0, 0,1,1,1, 1,0,0,0, 1,0,1,1])
            if np.dot(Mat,verif1)%2>0 or np.dot(Mat,verif2)%2>0 or np.dot(Mat,verif3)%2>0 or np.dot(Mat,verif4)%2>0:
                return False
            else:
                return True
        elif size == 5:
            verif1 = np.array([0,1,1,1,0, 1,0,1,0,1, 1,1,0,1,1, 1,0,1,0,1, 0,1,1,1,0])
            verif2 = np.array([1,0,1,0,1, 1,0,1,0,1, 0,0,0,0,0, 1,0,1,0,1, 1,0,1,0,1])
            if np.dot(Mat,verif1)%2>0 or np.dot(Mat,verif2)%2>0:
                return False
            else:
                return True
        else:
            return True

JE N'AI PAS COMPRIS CELLE CI
    def Choisir(self,sz = 5):
        ##Pas tout les puzzles ne sont solubles, alors choisir seulement un qui l'est##
        randMat = np.random.randint(2,size=sz*sz)
        while self.verification(Mat = randMat) == False:
            randMat = np.random.randint(2,size=sz*sz)
        randMat = randMat.reshape((sz,sz))
        return randMat

    def verif_victoire(self, flag, Mat = np.ones((5,5))):
        size = len(Mat)
        if flag == True:
            for i in range(size):
                if flag == True:
                    for j in range(size):
                        if Mat[i,j]==1:
                            flag = False
                            break
        return flag

    def paintInit(self, Mat = np.ones((5,5)), sM = np.zeros((5,5))):
        '''peindre les cases initiales'''
        size = len(Mat)
        shift = size + 2
        for ligne in range(size):
            for col in range(size):
                mycell = Cell(col=col, ligne=ligne, color=(0,255*Mat[ligne,col],255),
                              background=self.background)
                mycell.blit(self.background)
                solcell = Cell(col=col+shift, ligne=ligne, color=(255*sM[ligne,col],0,255),
                               background=self.background)
                solcell.blit(self.background)

    def paintPuz(self,Mat = np.ones((5,5))):
        '''peindre le puzzle'''
        size = len(Mat)
        for ligne in range(size):
            for col in range(size):
                mycell = Cell(col=col, ligne=ligne,
                              color=(255*Mat[ligne,col],255*Mat[ligne,col],255*(1-Mat[ligne,col])),
                              background=self.background)
                mycell.blit(self.background)


    def paint(self,col=0, ligne=0, color=(0,0,255)):
        """mettre à jour une seule cellule"""
        #------- Test de fonctions trouver sur internet --------
        # pygame.draw.line(Surface, color, start, end, width)
        # pygame.draw.rect(Surface, color, Rect, width=0): return Rect
        #pygame.draw.rect(self.background, (0,255,0), (50,50,100,25)) # rect: (x1, y1, width, height)
        # pygame.draw.circle(Surface, color, pos, radius, width=0): return Rect
        # pygame.draw.polygon(Surface, color, pointlist, width=0): return Rect
        # pygame.draw.arc(Surface, color, Rect, start_angle, stop_angle, width=1): return Rect
        # ------------------- blitting a cell --------------
        mycell = Cell(col=col,ligne=ligne,color=color,background=self.background)
        mycell.blit(self.background)



    def cliquable(self, ligne, col, M = np.ones((5,5)),sM = np.zeros((5,5))):
        size = len(M)
        shift = size + 2
        if ligne>=size or col >=size:
            pass
        else:
            for i in range(size):
                for j in range(size):
                    if (np.abs(ligne-i)<2 and col==j) or (ligne==i and np.abs(col-j)<2):
                        M[i,j] = (M[i,j]+1)%2
                        self.paint(ligne=i,col=j,color=(0,255*M[i,j],255))
            sM[ligne,col] = sM[ligne,col]+1
            ##peindre la cellule sur laquelle vous avez cliqué dans la solution
            self.paint(col=col+shift,ligne=ligne,color=(255*(sM[ligne,col]%2),0,255))
        return M, sM

    def alterne(self, ligne, col, M = np.ones((5,5))):
        size = len(M)
        if ligne>=size or col >=size:
            pass
        else:
            M[ligne,col] = (M[ligne,col]+1)%2 ##alterne la lumière
            self.paint(ligne=ligne,col=col,color=(0,255*M[ligne,col],255))
        return M

    def dernière_ligne(self, M=np.ones((5,5))):
        ##création d'une chaîne dans la dernière colonne
        size = len(M)
        arr = ''
        for j in range(size):
            arr = arr + str(M[-1,j])
        return arr

    def iterate(self, i, j, step2, size):
        if j<size-1:
            j += 1
        elif step2 == True:
            j = 0
            step2 = False
        else:
            i += 1
            j = 0
            if i == size-1:
                step2 = True
        return i, j, step2


## JE N'EST PAS COMPRIS CELLE CI
    def execute(self):
        """la boucle principale"""
        #matrix = self.ChooseInitBoard(sz=8)
        initMat = np.zeros((8,8))
        matrix = np.zeros((8,8))
        size = matrix.shape[0]
        solMatrix = np.zeros((size,size))  ## garder une trace des boutons enfoncés dans le travail pour résoudre
        self.paintInit(Mat=matrix, sM=solMatrix)
        running = True
        setup = True ##False lorsque l'utilisateur a terminer l'installation
        stop = False  ##True lorsque le chrono doit s'arrêter
        solving = False  ##True lorsque le processus de résolution est en cours d'execution
        step2 = False  ##True pour signifier l’étape 2 du processus de résolution
        errorMes = False  ##True lorsque le message d’erreur est visible
        finalRow = ''
        attempt = ''  ##tentative de solution si la solution est inconnue
        while running:
            ligne = -1
            col = -1
            pos = []
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        running = False
                elif event.type == pygame.MOUSEBUTTONUP:
                    errorMes = False
                    pos = pygame.mouse.get_pos()
                    #print(pos)
                    if 5<=pos[0]%55 and 5<=pos[1]%55:  ##assurez-vous que le clic n’est pas sur une limite
                        ligne = pos[1]//55
                        col = pos[0]//55

            if setup == True and len(pos)>0:
                if pos[1]<450:  ##il pourrait être sur une cellule
                    matrix = self.alterne(ligne,col,matrix)
                elif 485<=pos[1]<=515:  ##il pourrait être sur un boutton
                    if 300<=pos[0]<=385:  ##c'est le bouton "aléatoire"
                        matrix = self.Choisirlatailleinitiale(sz=size)
                        self.paintInit(Mat = matrix, sM = solMatrix)
                    elif 390<=pos[0]<=460:  ##c'est le bouton "commencer"
                        if self.verification(Mat=matrix.reshape(matrix.size,)):
                            setup = False
                            print('Out of Setup Mode')
                            initMat = matrix.astype(int)
                            matrix = matrix.astype(int)
                            self.clock = pygame.time.Clock()
                            stop = False  ##Commencer le temps
                        else: ##ne permettent pas à l’utilisateur de définir un modèle insoluble
                            errorMes = True
                    else:  #s'assurer si on clique bien sur un nombre
                        for num in range(4,9):
                            if -170+50*num<=pos[0]<=-130+50*num:
                                size = num
                                matrix = np.zeros((num,num))
                                solMatrix = np.zeros((num,num))
                                whitescreen = pygame.draw.rect(self.background, (255,255,255), (0,0,1000,450))
                                self.paintInit(Mat = matrix, sM = solMatrix)
                                break ##peut juste cliquer sur un bouton à la fois
            elif solving == False and len(pos)>0:
                ##n’autorise pas le clic pendant qu’il est en cours de résolution##
                if 460<=pos[0]<=540 and 485<=pos[1]<=515:  ##puis on clique sur le bouton résoudre
                    solving = True
                    #définir la position initiale de l’algorithme de résolution#
                    i=0
                    j=0
                elif ligne>=0 and col>=0:
                    #print(ligne,col)
                    matrix, solMatrix = self.cliquable(ligne, col, matrix, solMatrix)

            if solving == True:
                ##passer par l’étape suivante du processus de résolution##
                if i == 0 and step2 == True:
                    if attempt[j]=='1':
                        matrix, solMatrix = self.cliquable(ligne=i,col=j,M=matrix, sM=solMatrix)
                elif i<size-1:  ##ensuite, nous exécutant la première partie de l’algorithme
                    ##si une lumière est allumée, utilisez la ligne suivante vers le bas pour l’effacer
                    if matrix[i,j] == 1:
                        matrix, solMatrix = self.cliquable(ligne=i+1,col=j,M=matrix, sM=solMatrix)
                elif i == size-1:
                    ##lorsque vous atteignez la dernière ligne, obtenez une chaîne pour l’arrangement
                    temp = self.dernière_ligne(M = matrix)
                    if '1' in temp:  ##si nous n’avons pas encore fini de résoudre
                        if len(finalRow) == len(temp):
                            diff = '' ##mesurer la différence entre la dernière ligne précédente et la nouvelle
                            for ch in range(len(finalRow)):
                                diff = diff + str((int(temp[ch]) - int(finalRow[ch]))%2)
                            if diff not in KnownSols:
                                KnownSols[diff] = attempt
                                ## enregistrer les solutions connus ##
                                with open('KS.p', 'wb') as fp:
                                    pickle.dump(KnownSols, fp, protocol=pickle.HIGHEST_PROTOCOL)
                        finalRow = temp
                        print('Dernière ligne: ', finalRow)
                        if finalRow in KnownSols:
                            attempt = KnownSols[finalRow]
                        else:  ##choisissez une sélection aléatoire de boutons à appuyer dans la ligne supérieure
                            attempt = ''
                            while '1' not in attempt and attempt not in KnownSols.values():
                                attempt = ''
                                for i in range(size):
                                    attempt = attempt + str(np.random.randint(0,2))
                        step2 = True
                        i=0
                        j=-1
                        print('Attempt: ', attempt)
                    else:  ##si nous avons terminé, ajoutez les informations au dictionnaire si elles sont manquantes
                        résoudre = False
                        if finalRow not in KnownSols:
                            KnownSols[finalRow] = attempt
                            ## enregistrer les solution connu##
                            with open('KS.p', 'wb') as fp:
                                pickle.dump(KnownSols, fp, protocol=pickle.HIGHEST_PROTOCOL)
                i,j,step2 = self.iterate(i,j,step2,size)

            if stop == False:
                if self.verif_victoire(True, Mat = matrix): #checkflag == True:
                    print('Victoire!!')
                    stop = True
                    if self.playtime != 0: ##si le jeu a réellement commencer
                        setup = True
                        self.paintPuz(Mat = initMat)
                else:
                    #CREATION D'UN CHRONOMETRE (perso)
                    milliseconds = self.clock.tick(self.fps)
                    self.playtime += milliseconds / 1000.0
            self.draw_text("Cliques: {:6.4}{}TEMPS DE JEU: {:6.4} SECONDES".format(np.sum(solMatrix), " "*5, self.playtime))
            self.draw_text("Cliques Min: {:6.3}".format(np.sum(solMatrix%2)), loc=(50,570))
            self.draw_text("Cases initialement cliqué", loc=((2.7*size/2)*55,(size+0.5)*55))

            if errorMes == True:
                self.draw_text("Error: Modèle insoluble", loc = (50,520), color = (255,0,0))

            pygame.display.flip()
            self.screen.blit(self.background, (0, 0))

        pygame.quit()


    def draw_text(self, text,loc=(50,550), color=(0,0,0)):
        """Centrer le texte dans la fenêtre
        """
        fw, fh = self.font.size(text)
        surface = self.font.render(text, True, color)
        self.screen.blit(surface, loc)

class Cell(object):
    """Ceci est censé être une méthode pour créer une seule cellule"""
    def __init__(self, length = 50, col = 0, ligne = 0, color=(0,0,255),
                 background = pygame.Surface((600,600))):
        self.length = length
        self.x = col*55
        self.y = ligne*55
        self.color = color
        self.surface = background
        ##création de l’arrière-plan de la cellule##
        pygame.draw.rect(self.surface, (0,0,1), (self.x,self.y,self.length+10, self.length+10))
        ## création de la cellule##
        pygame.draw.rect(self.surface, self.color, (self.x+5, self.y+5, self.length, self.length))
        self.surface = self.surface.convert()

    def blit(self,background):#dessine une image sur une autre
        background.blit(self.surface, (0,0))##dessine une surface source sur cette surface

class Victoire():
    """
    Documentation :

        Description : La classe Victoire affiche une fenêtre popup lorsque le joueur arrive à gagner la partie

        Méthodes :

            -__init__() : initialisation de la fenêtre (méthode de base) : crée une popup qui nous affiche un message de victoire et le nombre de click total

            Arguments :

                Aucun argument nécessaire
    """

def __init__(self):
    global nb_clicks
    Tk().wm_withdraw() # to hide the main window
    messagebox.showinfo('Bravo !' , 'Félicitations, vous avez gagné la partie avec' + str(nb_clicks) + 'clicks !' )

####

if __name__ == '__main__':

    # appel avec largeur de fenêtre et fps
    Le_Jeu().execute()




Configuration: Windows / Firefox 94.0
A voir également:

1 réponse

mamiemando Messages postés 33407 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 29 novembre 2024 7 806
10 nov. 2021 à 16:30
Bonjour,

La fonction
Choisir
tire aléatoirement une configuration de départ (certaines cases seront noires, d'autre blanches). Mais d'après l'auteur, certaines configurations ne peuvent pas être résolues, donc plutôt que de proposer un problème insoluble au joueur, il contrôle si le puzzle peut être résolu et tire une nouvelle configuration si ça n'est pas le cas. Les détails de cette vérification sont réalisés dans la méthode
verification
.

La fonction
execute
alors je vais expliquer en gros. Si tu veux des explications détaillées, n'hésite pas à regarder dans la documentation de
numpy
/
pickle
/
pygame
.
  • l181-193 : on initialise les différentes variables du jeu ;
  • l194 : on lance la boucle qui maintient le programme ouvert (jusqu'à ce qu'il soit quitté) ;
  • l198 : on réagit sur un événement pygame (e.g. le joueur a cliqué ou utilisé son clavier) ;
  • l199-205 : le joueur veut quitter le jeu (il a cliqué sur la croix, appuyé sur echap, etc), on bascule la variable
    running
    à
    False
    ce qui va sortir de la boucle amorcée l194.
  • l205-211 : le joueur a relâché le clic de la souris, on relève la position du curseur et on déduit la ligne et la colonne de la casée sélectionnée ;
  • l212 : en gros si j'ai bien compris, il y a deux "affichages" (en fonction des valeurs de
    setup
    et
    solving
    ), celui qui correspond à la configuraiton et celui qui sert à jouer ;
  • l212 à 237, on gère la partie configuration (les commentaires parlent d'eux mêmes). Le code est assez compliqué, mais en gros, en fonction de la position de la souris, on sait sur quoi l'utilisateur à cliqué et on réagit en conséquence (ça aurait été plus simple et plus lisibles en utilisant des vrais boutons) ;
  • Bon là je passe un peu car c'est assez peu lisible et intéressant :-D
  • l290: on sauve les résultats dans un fichier pickle ;
  • l295-300: on vérifie si le problème est résolu ;
  • l301-304: on met à jour le chronomètre ;
  • l305-307: on affiche quelques statistiques ;
  • l315: on est sorti de la boucle, on quitte.


Bon après je trouve le programme parfois peu lisible, pas toujours bien architecturé et donc pas forcément idéal pour débuter ou apprendre à bien programmer, notamment car il comporte plusieurs fautes d'orthographe ou vis-à-vis des conventions python PEP8.

Un exemple plus simple est proposé ici et c'est là qu'on voit que pygame est assez limité pour faire des boutons. Si tu prends des modules comme PyQT ou GTK, tu peux créer de "vrais" boutons et lancer du code en fonction des événements qu'ils lèvent (e.g. quand on clique dessus). Cela te permettrait d'avoir un code bien plus lisible, modulaire, et qui distingue mieux la partie interface graphique et le moteur du jeu (voir architecture MVC).

Bonne chance
1