Используйте скудные матрицы Scipy для игры жизни Конвея - PullRequest
1 голос
/ 05 апреля 2020

В эти дни я пытался сделать Игру жизни Конвея в Pygame. Он уже закончен и работает хорошо, но я не хотел ограничивать себя в том, чтобы запускать игру с небольшой сеткой, поэтому я решил попробовать что-то вроде сетки 274x147 (так что она почти заполняет весь экран). Проблема в том, что с python нормальными «матрицами» (массивами массивов) он работал невероятно медленно. После некоторого чтения я пришел к выводу, что было бы лучше использовать Numpy матрицы, но после этого ничего существенно не изменилось. Проводя дальнейшие исследования, я наткнулся на разреженные матрицы Scipy, которые идеально вписались в проект, потому что в игровой жизни матрица в большинстве случаев разрежена, но когда я попытался реализовать их в коде, FPS значительно упал, даже когда Я не перебирал их. Почему это? Любой совет, чтобы помочь мне оптимизировать мой код?

Я прилагаю свой основной скрипт здесь:

import math
import numpy
import scipy.sparse as sparse
import pygame.math as m
from text import Text
from button import Button
pygame.init()
clock = pygame.time.Clock()

drawing = False
erasing = False
started = False
timer = False
time_since_last_iter = 0

screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
pygame.display.set_caption("Game of Life")
icon = pygame.image.load("res/icon.png")
cell_img = pygame.image.load("res/cell.png")
font = pygame.font.Font("res/arcadeclassic.ttf", 32)
pygame.display.set_icon(icon)

bg = pygame.image.load("res/grid.png")
fps_txt = Text(screen, (255, 255, 255), font, m.Vector2(10, 10)) #Fps text object, so I can see how's the game running
button = Button(screen, m.Vector2(screen.get_width() - 110, 10), m.Vector2(100, 30), (95, 95, 95), (80, 80, 80), "Run") #Button to start the game

mat_dim = (274, 147)

cell = sparse.lil_matrix(mat_dim, dtype=bool)
running = True
while running:

    screen.fill((0,0,0))
    screen.blit(bg, (0, 0))

    mp = pygame.mouse.get_pos()
    mouse_pos = m.Vector2(mp[0], mp[1])

    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

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1 and button.check_pos(mouse_pos):
                started = True
            elif not started:
                if event.button == 1 and not button.check_pos(mouse_pos):
                    drawing = True
                elif event.button == 3 and not started:
                    erasing = True

        if event.type == pygame.MOUSEBUTTONUP and not started:
            if event.button == 1:
                drawing = False                          
            elif event.button == 3:
                erasing = False

    if not started:
        if drawing:
            if mouse_pos.y >= 50:                  
                grid_mouse_y = mouse_pos.y - 50
                cell[int(mouse_pos.x / 7), int(grid_mouse_y / 7)] = True
        elif erasing:
            if mouse_pos.y >= 50:                  
                grid_mouse_y = mouse_pos.y - 50
                cell[int(mouse_pos.x / 7), int(grid_mouse_y / 7)] = False

    global_time = pygame.time.get_ticks()
    if global_time - time_since_last_iter > 200:
        time_since_last_iter = global_time
        timer = True

    if started:      
        global_time = pygame.time.get_ticks()
        if global_time - time_since_last_iter > 200:
            time_since_last_iter = global_time
            timer = True

        if timer:     
            next_matrix = sparse.lil_matrix(mat_dim, dtype=bool)       
            for i in range(mat_dim[0]):
                for j in range(mat_dim[1]):
                    neighbours = 0                
                    # Neighbour count
                    for s_i in range(-1, 2):
                        for s_j in range(-1, 2):
                            try:
                                if cell[i + s_i, j + s_j] and not (s_i == 0 and s_j == 0):
                                    neighbours += 1
                            except IndexError:
                                continue

                    if cell[i, j]:
                        if neighbours > 3 or neighbours < 2:
                            next_matrix[i, j] = False
                        else:
                            next_matrix[i, j] = True

                    elif neighbours == 3:
                        next_matrix[i, j] = True
                    else:
                        next_matrix[i, j] = False

            for i in range(mat_dim[0]):
                for j in range(mat_dim[1]):
                    if not next_matrix[i, j] and cell[i, j]:
                        cell[i, j] = False
                    elif next_matrix[i, j] and not cell[i, j]:
                        cell[i, j] = True

            timer = False

    for i in range(mat_dim[0]):
        for j in range(mat_dim[1]):
            if cell[i, j]:
                screen.blit(cell_img, (i * 7, j * 7 + 50))

    button.draw(mouse_pos)

    clock.tick()  
    fps_txt.draw("fps       " + str(math.ceil(clock.get_fps())))

    pygame.display.update()```
...