I was trying to recreate Conway's Game of Life in python using Tkinter, but I got stuck while checking for the neighbors. I can't seem to find the solution to my problem. If a cell is alive it is stored using numpy, 0 for the dead cells and 1 for the living cells. The function "get_neighbours" checks if the cell is not in a corner or next to a side and it returns the ammount of neighbours each cell has. The function "recalculate" basically makes a new board with 0s and 1s which then replaces the original board and redraws everything. But when compared to an already existing game the progress is different.
import tkinter as tk
import numpy as np
win = tk.Tk()
WIDTH = 500
HEIGHT = 500
vs = 10
absvs = vs
cells = np.zeros((WIDTH//vs, HEIGHT//vs), dtype=int)
cells_new = np.zeros((WIDTH//vs, HEIGHT//vs), dtype=int)
def get_neighbours(x, y):
total = 0
if x > 0:
total += cells[x - 1, y]
if x > 0 and y > 0:
total += cells[x - 1, y - 1]
if y > 0:
total += cells[x, y - 1]
if x > 0 and y < (HEIGHT // absvs - 1):
total += cells[x - 1, y + 1]
if x < (WIDTH // absvs - 1):
total += cells[x + 1, y]
if x < (WIDTH // absvs - 1) and y < (HEIGHT // absvs - 1):
total += cells[x + 1, y + 1]
if y < (HEIGHT // absvs - 1):
total += cells[x, y + 1]
if x > 0 and y < (HEIGHT // absvs - 1):
total += cells[x - 1, y + 1]
return total
def recalculate():
global cells, cells_new
for y in range(HEIGHT//absvs):
for x in range(WIDTH//absvs):
temp = get_neighbours(x, y)
if (temp == 2 and cells[x, y] == 1) or (temp == 3 and cells[x, y] == 1):
cells_new[x, y] = 1
elif temp == 3 and cells[x, y] == 0:
cells_new[x, y] = 1
elif temp < 2 or temp > 3:
cells_new[x, y] = 0
cells = cells_new
canvas.delete("all")
create_stage()
redraw_cell()
def slider_changer(e):
global vs
canvas.delete("all")
vs = w.get()
create_stage()
redraw_cell()
def create_cell(e):
global cells
tx = e.x // vs
ty = e.y // vs
x = tx * vs
y = ty * vs
canvas.create_rectangle(x, y, x + vs, y + vs, fill="gray")
cells[tx, ty] = 1
print(get_neighbours(tx, ty))
def redraw_cell():
for x in range(WIDTH//vs):
for y in range(HEIGHT//vs):
if cells[x, y] == 1:
canvas.create_rectangle(x * vs, y * vs, x * vs + vs, y * vs + vs, fill="gray")
def create_stage():
for x in range(WIDTH//vs):
canvas.create_line(x*vs, 0, x*vs, HEIGHT)
for y in range(HEIGHT//vs):
canvas.create_line(0, y*vs, WIDTH, y*vs)
canvas = tk.Canvas(width = WIDTH, height = HEIGHT, bg = "white")
canvas.pack()
w = tk.Scale(win, from_=10, to=50, orient="horizontal", command = slider_changer, length = 500)
w.pack()
w2 = tk.Button(win, text = "PRESS ME!!!", command = recalculate)
w2.pack()
create_stage()
canvas.bind("<Button-1>", create_cell)
win.mainloop()
There are these issues in your code:
In get_neighbours
the last if
block is the same as the fourth if
block, and so there is a neighbor that is counted twice and another neighbor that isn't counted at all (the one at x + 1
and y - 1
). So replace this:
if x > 0 and y < (HEIGHT // absvs - 1):
total += cells[x - 1, y + 1]
with:
if x < (WIDTH // absvs - 1) and y > 0:
total += cells[x + 1, y - 1]
In recalculate
the assignment to cells
at the end is making you lose one of the two arrays. Now both cells
and new_cells
reference the very same array1. This means the next iteration will not be calculated correctly. Instead, you should copy the content of cells_new
into cells
. So replace:
cells = cells_new
with:
cells = cells_new.copy()
In recalculate
the if...elif..elif
construct (inside the double loop) does not deal with the case where temp == 2 and cells[x, y] == 1
and so cells_new[x, y]
might not be reset to 0 when it should. In fact, the last part of this construct should not have a condition; it should be a catch-all for all states that the previous checks did not deal with. So replace:
elif temp < 2 or temp > 3:
cells_new[x, y] = 0
with:
else:
cells_new[x, y] = 0