Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème mise à jour Game of Life

Sujet résolu
3 janvier 2022 à 18:57:38

Bonjour à tous,

Je suis débutant et je me suis mis comme défi de créer un Game of Life avec des fonctionnalité de base.

J'ai créer la majeur partie du programme mais il reste un problème.

Le moteur du jeu qui dois mettre à jour un tableau avec les rêgle de Conway ne fonctionne pas et met à jour n'importe comment.

J'ai essayer de chercher sur internet mais aucunne solution répond à ma question.

# Pour l'interface graphique
from tkinter import *
import random

#Dimension matrice
width = 50
height = 50
# Taille pixels à l'écran (en pixels)
size_pix = 12

#Tableaux des pixels en 2D
matrix = [[0 for i in range(width)] for j in range(height)]
matrix2 = [[0 for i in range(width)] for j in range(height)]

# Couleurs
colors = ["white", "blue"]
# gestion de la pause (par defaut, le jeu est en pause)
pause = True
# vitesse de la simulation
speed = 100

# dernière pixel modifier (pour éviter de faire clignoter le pixel quand on clique dessus)
last_click = [0, 0]
# nombre de pas de la simulation
steps = 0

#-------------------------Espace pour le moteur du jeu-------------------------

def Display(matrix):

    # on met à jour le nombre de pas
    label_step.config(text="Steps: " + str(steps))

    # en éfface tout les pixels
    Canvas.delete(ALL)
    # et on les retraces
    for i in range(height):
        for j in range(width):

            #Si la case est vivante (1), on dessine un carré noir
            if matrix[i][j] == 1:
                Canvas.create_rectangle(j * size_pix + 2, i * size_pix + 2, (j + 1) * size_pix + 2, (i + 1) * size_pix + 2, fill=colors[1], outline="black")
            #Si la case est morte (0), on dessine un carré blanc
            else:
                Canvas.create_rectangle(j * size_pix + 2, i * size_pix + 2, (j + 1) * size_pix + 2, (i + 1) * size_pix + 2, fill=colors[0], outline="black")

def Break():
    global pause
    pause = not pause

    #on relance le moteur du jeu
    if not pause:
        Engine(False)

def Step():
    global pause

    if pause:
        pause = False
    Engine(True)

def Reset():
    global matrix, matrix2, width, height

    matrix = [[0 for i in range(width)] for j in range(height)]
    matrix2 = matrix.copy()
    Display(matrix)

def RandomFill():
    global matrix, matrix2, width, height

    for i in range(height):
        for j in range(width):
            matrix[i][j] = random.randint(0, 1)

    matrix2 = matrix.copy()
    
    Display(matrix)

# fonction pour ajouter une cellule
def addCell(event):
    global last_click

    pos_x = int(event.x / size_pix)
    pos_y = int(event.y / size_pix)

    # on évite les débordements
    if pos_x >= 0 and pos_x < width and pos_y >= 0 and pos_y < height:
        if last_click != [pos_x, pos_y]:
            if matrix[pos_y][pos_x] == 0:
                matrix[pos_y][pos_x] = 1
            else:
                matrix[pos_y][pos_x] = 0

            Display(matrix)

            last_click = [pos_x, pos_y]

def Foreach(matrix, x, y):
    global width, height

    sum = 0

    # on va partire du principe que comme la taille de la matrice est de 50x50,
    # on ne va pas cosidére que le monde est torique (tu entre d'un coté et tu sort de l'autre)

    for i in range(x - 1, x + 2):
        for j in range(y - 1, y + 2):

            # on évite les débordements
            if i >= 0 and i < width and j >= 0 and j < height:

                # on évite de faire la somme de la case sur laquelle on est
                if not (i == x and j == y):
                    sum += matrix[j][i]

    return sum

def Engine(step = False):
    global matrix, matrix2, width, height, speed, pause, steps

    # si on est en pause, on ne fait rien
    if not pause:
        steps += 1

        for i in range(height):
            for j in range(width):
                sum = Foreach(matrix, j, i)

                if sum == 3:
                    matrix2[i][j] = 1
                elif sum == 2:
                    matrix2[i][j] = matrix[i][j]
                elif sum < 2 or sum > 3:
                    matrix2[i][j] = 0

        matrix = matrix2.copy()
        Display(matrix)

        # appele de la fonction Engine après un certain temps (fonction TKinter)
        # si on dois faire un pas on ne rappelle pas la fonction Engine et on met en pause
        if step:
            pause = True
        else:
            window.after(speed, Engine)

#--------------------------Espace interface graphique--------------------------

window = Tk()
# Tire de la fenêtre
window.title("Game of Life")
# Taille de la fenêtre (x, y)
window.size = (width * size_pix + 51, height * size_pix + 51)
# Couleur de la fenêtre (format hexadécimal pour plus de couleur)
window.configure(background="#e4e4e4")

#Décalage de 2 pour la bordure des pixels s'affiche bien
Canvas = Canvas(window, bg="white", height=height * size_pix + 1, width=width * size_pix + 1)
Canvas.pack(padx=5, pady=5)

#--------------------------Espace gestion des événements--------------------------
# clic/drag gauche
Canvas.bind("<Button-1>", addCell)
Canvas.bind("<B1-Motion>", addCell)

bouton_break = Button(window, text="Break", command=Break)
bouton_break.pack(side=LEFT, padx=5, pady=5)

bouton_step = Button(window, text="Step", command=Step)
bouton_step.pack(side=LEFT, padx=5, pady=5)

label_step = Label(window, text="Steps : 0")
label_step.pack(side=LEFT, padx=5, pady=5)

bouton_reset = Button(window, text="Reset", command=Reset)
bouton_reset.pack(side=LEFT, padx=5, pady=5)

bouton_random = Button(window, text="Random", command=RandomFill)
bouton_random.pack(side=LEFT, padx=5, pady=5)

bouton_qui = Button(window, text="Quit", command=quit)
bouton_qui.pack(side=RIGHT, padx=5, pady=5)

# fonction pricipale du programme
def main():
    # appele de la fonction Display pour afficher la matrice
    Display(matrix)
    # appele de la fonction Engine pour lancer le moteur du jeu
    Engine(False)

# Démmarage du jeu
main()

#boucle principale
window.mainloop()

Merci d'avance pour votre aide.

  • Partager sur Facebook
  • Partager sur Twitter
4 janvier 2022 à 2:25:24

J'ai trouvé ceci en français:
https://fr.wikipedia.org/wiki/Jeu_de_la_vie
Je n'ai pas eu le courage de regarder ton code ...
Il faut fonctionner avec deux grilles, l'ancienne et la nouvelle.
Le plus compliqué en général est de tester tous les voisins.
compte = 0
for x, y in ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)):
    if 0 <= i+x < len(grille) and 0 <= j+y < len(grille[0]):
        compte += grille[i+x][j+y]   # je suppose 1 pour les vivants et 0 pour les morts
# on teste compte et on modifie dans la nouvelle grille

J'utiliserais deepcopy au lieu de copy

Méthode de paresseux pour modifier dans la nouvelle grille:
grille2[i][j] = {0: {3: 1}, 1: {2: 1, 3: 1}}[grille[i][j]].get(compte, 0)

edit:
Si on ramène sur la ligne suivante la définition de chaque ligne, on peut mettre tout sur une seule ligne.
On peut redéfinir la matrice avec une seule ligne, et pas besoin d'une seconde matrice.
Le processus peut boucler indéfiniment si on se retrouve avec des îlots du genre:
1 1
1 1
-
voisins=((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
compte=sum(grille[i+x][j+y] for x, y in voisins if 0<=i+x<height and 0<=j+y<width)
update={0: {3: 1}, 1: {2: 1, 3: 1}}[grille[i][j]].get(compte, 0)
grille = [[update for j in range(width)] for i in range(height)]
-
height=4
width=4
grille = [[1 for j in range(width)] for i in range(height)]
grille = [[{0: {3: 1}, 1: {2: 1, 3: 1}}[grille[i][j]].get(sum(grille[i+x][j+y] for x, y in ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)) if 0<=i+x<height and 0<=j+y<width), 0) for j in range(width)] for i in range(height)]
print(grille)

Voici ce que ça donne, et c'est ce à quoi on s'attend:
[[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 1]]

Par contre, utiliser deux grilles a un avantage.

Si les grilles sont identiques, on peut s'arrêter car on est dans un processus infini.

On peut également alterner indéfiniment entre les deux configurations suivantes:
0100  0110 | 010  000
0110  1000 | 010  111
0110  0001 | 010  000

0010  0110 |

Autre configurations qui se répètent (en incluant leurs rotations):
010  0110  110  110
101  1001  101  101
010  0110  010  011

Dans certains cas, des configurations qui se répètent ou alternent peuvent se trouver à proximité.
Il peut en résulter des cycles de configurations.
Par exemple, on peut passer de la configuration A à la configuration B, puis de B à C, et de C à A, et on recommence.
Je n'ai pas analysé de tells cycles.
En conclusion, il n'est pas suffisant de vérifier si deux grilles successives sont identiques.
Il faut se fixer une limite d'itérations, ce qui n'est pas évident à évaluer.

-
Edité par PierrotLeFou 6 janvier 2022 à 5:28:48

  • Partager sur Facebook
  • Partager sur Twitter

Le Tout est souvent plus grand que la somme de ses parties.

6 janvier 2022 à 19:49:03

Merci pour l'aide, en faite c'ètait l'utilisation du deepcopy qui à aider. Merci
  • Partager sur Facebook
  • Partager sur Twitter
6 janvier 2022 à 20:19:29

Rien à voir avec ton problème mais tu as déjà entendu parler des classes?

Parce que définir une fonction avec des variables globales de cette manière c'est pas très propre :c

  • Partager sur Facebook
  • Partager sur Twitter