Reputation: 55
So there must be something wrong with the code which detects wether it should be alive or not in Cell.update(), but the glider i hardcoded in is not working as intended. The first and second generations work as intended, but on the third it dies out. Anyone know what the problem is? I know the code is a bit messy, but I'm quite new to python so it is expected. Thanks in advance!
Here is the code:
import pygame
"""
rules:
1. Any live cell with two or three live neighbours survives.
2. Any dead cell with three live neighbours becomes a live cell.
3. All other live cells die in the next generation. Similarly, all other dead cells stay dead.
number cells on screen = 32x18
cell size = 30x30
"""
# variables
WIDTH = 960
HEIGHT = 540
TITLE = "Conway's Game Of Life"
GRID_COLOUR = (200, 200, 200)
BG_COLOUR = (255, 255, 255)
grid = [[False] * 32] * 18
cells = []
live_queue = []
die_queue = []
# window
wn = pygame.display.set_mode((WIDTH, HEIGHT), vsync=1)
pygame.display.set_caption(TITLE)
# classes
class Cell:
def __init__(self, x, y, alive, index_x, index_y):
self.x = x
self.y = y
self.alive = alive
self.indexX = index_x
self.indexY = index_y
def die(self):
self.alive = False
def live(self):
self.alive = True
def get_index(self):
return self.indexX, self.indexY
def update(self):
grid_temp = grid[self.indexY]
grid_temp.pop(self.indexY)
grid_temp.insert(self.indexY, self.alive)
grid.pop(self.indexY)
grid.insert(self.indexX, grid_temp)
adjacent_alive = 0
for i in cells:
if i.x == self.x - 30 and i.y == self.y and i.alive:
adjacent_alive += 1
elif i.x == self.x + 30 and i.y == self.y and i.alive:
adjacent_alive += 1
elif i.x == self.x and i.y == self.y - 30 and i.alive:
adjacent_alive += 1
elif i.x == self.x and i.y == self.y + 30 and i.alive:
adjacent_alive += 1
elif i.x == self.x - 30 and i.y == self.y - 30 and i.alive:
adjacent_alive += 1
elif i.x == self.x - 30 and i.y == self.y + 30 and i.alive:
adjacent_alive += 1
elif i.x == self.x + 30 and i.y == self.y - 30 and i.alive:
adjacent_alive += 1
elif i.x == self.x + 30 and i.y == self.y + 30 and i.alive:
adjacent_alive += 1
if self.alive:
if adjacent_alive < 2:
return False
elif adjacent_alive > 3:
return False
else:
return True
if not self.alive:
if adjacent_alive == 3:
return True
else:
return False
def render(self):
if self.alive:
pygame.draw.rect(wn, (0, 0, 0), (self.x, self.y, 30, 30))
# functions
def render_grid():
for y in range(0, HEIGHT, 30):
pygame.draw.line(wn, GRID_COLOUR, (0, y), (WIDTH, y))
for x in range(0, WIDTH, 30):
pygame.draw.line(wn, GRID_COLOUR, (x, 0), (x, HEIGHT))
def parse_to_x_y(x, y):
return x * 30, y * 30
def parse_to_index(x, y):
return int(x / 30), int(y / 30)
indexX = 0
indexY = 0
x_pos = 0
y_pos = 0
for y_ in range(18):
for x_ in range(32):
cells.append(Cell(x_pos, y_pos, False, indexX, indexY))
indexX += 1
x_pos += 30
y_pos += 30
x_pos = 0
indexY += 1
cells[2].live()
cells[35].live()
cells[65].live()
cells[66].live()
cells[67].live()
# main loop
fps = 1
clock = pygame.time.Clock()
while True:
# start_time = time.time()
wn.fill(BG_COLOUR)
for item in cells:
item.render()
render_grid()
# events loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
for item in cells:
if item.update():
live_queue.append(item)
else:
die_queue.append(item)
for i in live_queue:
i.live()
for i in die_queue:
i.die()
pygame.display.update()
clock.tick(fps)
# end_time = time.time()
# print(round(1 / (end_time - start_time)), "fps")
Upvotes: 1
Views: 436
Reputation: 980
I agree with commenter Rabbid76, in that you need to update the entire grid at once, not cell by cell. This is usually done by using two separate grids, an "previous state" grid and a "new state grid". Loop through each position in the "new state" grid and calculate its number of live neighbors using the "previous state" grid. After the entire "new state" grid is calculated, you can copy to "new state" grid to the "old state" grid.
Another fatal flaw in your algorithm is grid = [[False] * 32] * 18
. This will not work as expected in Python. With this code, each row is a reference to the same array. For instance, the expression grid[0] is grid[1]
will evaluate to True
. If you set a certain cell in the grid to true, the entire column will be set to true. You can fix this via the code:
grid=[]
for r in range(18):
row=[]
for c in range(32):
row.append(False)
grid.append(row)
Though it isn't directly related to the bug, I suggest a bit of a redesign of your algorithm. It is not really needed to encapsulate each cell in a class; doing so creates a redundancy between the grid and list of cells. This also leads you to identify cells by their pixel position (hence the i.x == self.x - 30
clause), which can easily lead to bugs. I suggest checking adjacent indices in the grid variable instead. Try looking into https://www.geeksforgeeks.org/conways-game-life-python-implementation/ for some inspiration.
Upvotes: 1
Reputation: 350310
The problem is that you don't reset your queues in the main loop.
So add this before adding to the queues:
live_queue = [] # <----
die_queue = [] # <----
for item in cells:
if item.update():
live_queue.append(item)
else:
die_queue.append(item)
You never use grid
or grid_temp
in a useful way. Even the operations you make on them are strange. Any way, you can just remove all references to them.
You never use the indexX
or indexY
attributes, nor the method around it, nor the corresponding arguments to the constructor. All that can go.
You should avoid scanning all the cells just to find the (up to) 8 neighbors of one cell: this has a bad impact on performance.
Upvotes: 5