I recently created game about life in python. First version of code was console, but recently I rewrote this game in pygame. I decied upgrade game and was added age of cells. But now I can see that my game when I setting up size of array more than 25*25 cells starts to lag. I think that problem can be in method "add_life_to_array" and increase array "cells" to value 250 and more values. Also I want to understood how I can update this program in architecture level, if it is possible. I try to use JIT compiling, but it does not help me(maybe because I very bad underrstand how it work and often get errors). Github of this project
Code of game:
import random
from collections import Counter
import pygame as p
from pygame.locals import *
import sys
# for compilation to exe enter: pyinstaller --noconsole game_of_life.py
class LifeGame:
def __init__(self):
"""initialization variables"""
self.width = 25
self.height = 25
# more - worse for cells
self.death_level = 3
# less - better for cells
self.birth_level = 15
# max age for cell
self.max_age = 100
# how many lives will be spawn on start
self.start_spawn = 1
self.cells = []
self.matrix = []
self.life = 1
self.null = 0
self.iteration = 0
self.matrix = [[0 for _ in range(self.width)] for _ in range(self.height)]
def draw(self):
"""this method draw simulation"""
# fill layers
root.fill(BLACK)
capture.fill(WHITE)
description.fill(WHITE)
# width and height of cell
width = (root.get_width() // self.width)
height = (root.get_height() // self.height)
life_cells = 0
empty_cells = 0
for y in range(0, self.height):
for x in range(0, self.width):
if self.matrix[y][x] == self.life:
p.draw.rect(root, RED, [width * x, height * y, width, height])
elif self.matrix[y][x] == self.null:
p.draw.rect(root, WHITE, [width * x, height * y, width, height])
else:
p.draw.rect(root, BLACK, [width * x, height * y, width, height])
# draw lines
p.draw.line(root, BLACK, (0, 0), (root.get_width(), 0))
p.draw.line(root, BLACK, (0, root.get_height()-1), (root.get_width(), root.get_height()-1))
# count cells
cnt = Counter()
for row in self.matrix:
cnt.update(row)
life_cells = cnt[1]
empty_cells = cnt[1]
# texts
if empty_cells != self.null:
txt_percent_of_life = font.render(str(round((life_cells / 100) * (100 / ((self.width * self.height) / 100)), 2))+"% of life", True, BLACK, WHITE)
else:
txt_percent_of_life = font.render("100% of life", True, BLACK, WHITE)
txt_life_cells = medium_font.render("Count of cells: {}/{}".format(life_cells, self.width * self.height), True, BLACK, WHITE)
txt_fps = small_font.render("FPS: {}".format(round(clock.get_fps(), 1)), True, BLACK, WHITE)
txt_average_age = medium_font.render("Average age: {}/{}".format(round(self.count_average_age(), 2), self.max_age), True, BLACK, WHITE)
txt_year = medium_font.render("Year: {}".format(self.iteration), True, BLACK, WHITE)
pos = txt_percent_of_life.get_rect(center=(capture.get_width()//2, capture.get_height()//2))
# capture
capture.blit(txt_percent_of_life, pos)
# description
description.blit(txt_life_cells, (5, 5))
description.blit(txt_fps, (430, 130))
description.blit(txt_average_age, (5, 35))
description.blit(txt_year, (5, 65))
# display
display.blit(capture, (0, 0))
display.blit(description, (0, 550))
display.blit(root, (0, 50))
def spawn_life(self):
"""this method spawn life by random coordinates by given count of times"""
rand_coords = []
counter = 0
for _ in range(lg.start_spawn):
rand_coords.append([])
rand_coords[counter].append(random.randint(0, self.width-1))
rand_coords[counter].append(random.randint(0, self.height-1))
counter += 1
for i in range(len(rand_coords)):
self.matrix[rand_coords[i][1]][rand_coords[i][0]] = self.life
return rand_coords
def get_available_coords(self, x, y):
"""this method return available count of coord for given coordinate"""
available_coords = [[x-1, y], [x+1, y], [x, y+1], [x, y-1]]
coords = []
for i in range(len(available_coords)):
if available_coords[i][0] <= self.width-1 and available_coords[i][0] >= 0\
and available_coords[i][1] <= self.height-1 and available_coords[i][1] >= 0\
and self.matrix[available_coords[i][1]][available_coords[i][0]] == self.null:
coords.append([available_coords[i][0], available_coords[i][1]])
return coords
def count_average_age(self):
"""return average age for all lifes"""
average_age = 0
counter = 1
for cell in self.cells:
counter += 1
average_age += self.iteration-cell[2]
return average_age / counter
def add_life_to_matrix(self, coords):
"""this method add new coords to matrix and cells"""
cells_arr = []
for x, y in coords:
chance = random.randint(0, self.birth_level)
if chance == 0:
self.matrix[y][x] = self.life
if [x, y] not in cells_arr:
cells_arr.append([x, y])
self.cells.append([x, y, self.iteration])
def del_life(self):
"""this method randomly delete life"""
for _ in range(lg.death_level):
del_x = random.randint(0, lg.width-1)
del_y = random.randint(0, lg.height-1)
self.matrix[del_y][del_x] = self.null
def del_dead_cells(self):
"""this method delete cells value which in matrix equal '0'"""
for y in range(self.height):
for x in range(self.width):
if self.matrix[y][x] == self.null:
for i in range(len(self.cells)):
if self.cells[i][0] == x and self.cells[i][1] == y:
del self.cells[i]
break
for _ in range(len(self.cells)):
for i in range(len(self.cells)):
if self.iteration-self.cells[i][2] >= self.max_age:
chance_of_die = random.randint(0, 4)
if chance_of_die == 0:
del self.cells[i]
break
elif self.iteration-self.cells[i][2] >= self.max_age//2:
chance_of_die = random.randint(0, 16)
if chance_of_die == 0:
del self.cells[i]
break
elif self.iteration-self.cells[i][2] >= self.max_age//4:
chance_of_die = random.randint(0, 256)
if chance_of_die == 0:
del self.cells[i]
break
elif self.iteration-self.cells[i][2] >= self.max_age//8:
chance_of_die = random.randint(0, 1024)
if chance_of_die == 0:
del self.cells[i]
break
lg = LifeGame()
lg.spawn_life()
p.init()
# RGB
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
font = p.font.SysFont('lucida console', 50)
medium_font = p.font.SysFont('lucida console', 25)
small_font = p.font.SysFont('lucida console', 12)
# create window
display = p.display.set_mode((500, 700))
root = p.Surface((500, 500))
capture = p.Surface((500, 50))
description = p.Surface((500, 200))
display.fill(WHITE)
p.display.set_caption("Game of life")
FPS = 10
clock = p.time.Clock()
while True:
coords = []
lg.draw()
for y in range(lg.height):
for x in range(lg.width):
if lg.matrix[y][x] == lg.life:
coords.append(lg.get_available_coords(x, y))
coords = sum(coords, [])
lg.del_life()
lg.iteration += 1
lg.add_life_to_matrix(coords)
lg.del_dead_cells()
# for exit
for i in p.event.get():
if i.type == QUIT:
sys.exit(1)
p.display.update()
clock.tick(FPS)