感染效率 - Pygame
Efficiency in Infections - Pygame
我目前正在为我的 A-level 课程制作一个感染生存游戏,我正在努力研究如何使它更有效率。
当细胞被感染时,我需要检查越来越多的细胞,我的计算机科学老师建议我将感染保存为一个布尔值,因为我以后可以用它做更多的事情,但是由于我最终必须检查它,所以效率更低导致大量帧率问题的平方单元格数量。
我最初的想法是将未感染和感染的存储在单独的列表中,但我的 comp sci 老师说我把它复杂化了,但是这没有任何帧率问题。
我的很多代码都从这个问题中获得了灵感,尤其是在涉及到单元格移动时。
TLDR:我想让我的代码更有效率,但我想不出怎么做
我的代码:
import sys, random, pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
pygame.init()
#Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1,windowWidth)
self.yPos = random.randrange(1,windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if self.isInfected == False:
pygame.draw.rect(screen, (255,255,255), (self.xPos,self.yPos,pixSize,pixSize),0)
else:
pygame.draw.rect(screen, (0,255,0), (self.xPos,self.yPos,pixSize,pixSize),0)
def cellMovement(self):
directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
if random.randrange(0,5) == 2: #move about once every 5 frames
if self.direction == None: #if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) #get the index of direction in directions list
b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: #if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.move[0] != None: #add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self):
for i in cellList:
if (self.xPos > i.xPos - self.infectionRange and self.xPos < i.xPos + self.infectionRange) and (self.yPos > i.yPos - self.infectionRange and self.yPos < i.yPos + self.infectionRange):
i.isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
while True:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
for i in cellList:
i.cellDraw()
i.cellMovement()
for i in cellList:
if i.isInfected == True:
i.Infect()
infectList.append(i)
xgraph.append(time.time()-startTime)
ygraph.append(len(infectList))
plt.plot(xgraph,ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
if len(infectList) == 1000:
plt.show()
pygame.display.update() #update display
pygame.time.Clock().tick(FPS) #limit FPS
gameLoop()
首先,我更改了您的一些代码:
if self.isInfected == False:
if self.direction == None:
到
if not self.isInfected:
if self.direction is None:
只是这样读起来更好看。
其次,我对 Infect
函数进行了向量化:
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
这个单元格数量需要相同的时间,但应该更好地缩放。
事实证明,创建数组几乎需要所有时间。因此,您可以创建一次,将其从循环中拉出并节省大量时间:
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
# To prevent errors with empty arrays
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
# To prevent errors when everyone is infected
if infected == 0:
infected = len(cellList) - len(uninfected)
最后,你似乎并没有真正使用infectList
,所以我用一个计数器代替它:
infected = 0
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
infected += 1
作为旁注,我会稍微更改 UI 控件,以便更容易绘制图形,而不是使用 sys.exit
退出,而是更好地跳出 while 循环。您也只绘制一次结果:
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
...
pygame.quit()
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
实施所有这些结果:
import random
import pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
import numpy as np
pygame.init()
# Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1, windowWidth)
self.yPos = random.randrange(1, windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if not self.isInfected:
pygame.draw.rect(screen, (255, 255, 255), (self.xPos, self.yPos, pixSize, pixSize), 0)
else:
pygame.draw.rect(screen, (0, 255, 0), (self.xPos, self.yPos, pixSize, pixSize), 0)
def cellMovement(self):
directions = {"S": ((-1, 2), (1, self.speed)), "SW": ((-self.speed, -1), (1, self.speed)),
"W": ((-self.speed, -1), (-1, 2)), "NW": ((-self.speed, -1), (-self.speed, -1)),
"N": ((-1, 2), (-self.speed, -1)), "NE": ((1, self.speed), (-self.speed, -1)),
"E": ((1, self.speed), (-1, 2)),
"SE": ((1, self.speed), (1, self.speed))} # ((min x, max x)(min y, max y))
directionsName = ("S", "SW", "W", "NW", "N", "NE", "E", "SE") # possible directions
if random.randrange(0, 5) == 2: # move about once every 5 frames
if self.direction is None: # if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) # get the index of direction in directions list
b = random.randrange(a - 1,
a + 2) # set the direction to be the same, or one next to the current direction
if b > len(directionsName) - 1: # if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: # if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.move[0] is not None: # add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
screen.fill((0, 0, 0))
for i in cellList:
i.cellDraw()
i.cellMovement()
infected = 0
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
infected += 1
if infected == 0:
infected = len(cellList) - len(uninfected)
xgraph.append(time.time() - startTime)
ygraph.append(infected)
pygame.display.update() # update display
pygame.time.Clock().tick(FPS) # limit FPS
pygame.quit()
# figured this is what you wanted to do ;)
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
gameLoop()
而且运行流畅
我目前正在为我的 A-level 课程制作一个感染生存游戏,我正在努力研究如何使它更有效率。
当细胞被感染时,我需要检查越来越多的细胞,我的计算机科学老师建议我将感染保存为一个布尔值,因为我以后可以用它做更多的事情,但是由于我最终必须检查它,所以效率更低导致大量帧率问题的平方单元格数量。
我最初的想法是将未感染和感染的存储在单独的列表中,但我的 comp sci 老师说我把它复杂化了,但是这没有任何帧率问题。
我的很多代码都从这个问题中获得了灵感
TLDR:我想让我的代码更有效率,但我想不出怎么做
我的代码:
import sys, random, pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
pygame.init()
#Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1,windowWidth)
self.yPos = random.randrange(1,windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if self.isInfected == False:
pygame.draw.rect(screen, (255,255,255), (self.xPos,self.yPos,pixSize,pixSize),0)
else:
pygame.draw.rect(screen, (0,255,0), (self.xPos,self.yPos,pixSize,pixSize),0)
def cellMovement(self):
directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
if random.randrange(0,5) == 2: #move about once every 5 frames
if self.direction == None: #if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) #get the index of direction in directions list
b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: #if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0],directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0],directions[self.direction][1][1]) + 0.35
if self.move[0] != None: #add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self):
for i in cellList:
if (self.xPos > i.xPos - self.infectionRange and self.xPos < i.xPos + self.infectionRange) and (self.yPos > i.yPos - self.infectionRange and self.yPos < i.yPos + self.infectionRange):
i.isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
while True:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
for i in cellList:
i.cellDraw()
i.cellMovement()
for i in cellList:
if i.isInfected == True:
i.Infect()
infectList.append(i)
xgraph.append(time.time()-startTime)
ygraph.append(len(infectList))
plt.plot(xgraph,ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
if len(infectList) == 1000:
plt.show()
pygame.display.update() #update display
pygame.time.Clock().tick(FPS) #limit FPS
gameLoop()
首先,我更改了您的一些代码:
if self.isInfected == False:
if self.direction == None:
到
if not self.isInfected:
if self.direction is None:
只是这样读起来更好看。
其次,我对 Infect
函数进行了向量化:
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
这个单元格数量需要相同的时间,但应该更好地缩放。
事实证明,创建数组几乎需要所有时间。因此,您可以创建一次,将其从循环中拉出并节省大量时间:
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
# To prevent errors with empty arrays
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
# To prevent errors when everyone is infected
if infected == 0:
infected = len(cellList) - len(uninfected)
最后,你似乎并没有真正使用infectList
,所以我用一个计数器代替它:
infected = 0
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
infected += 1
作为旁注,我会稍微更改 UI 控件,以便更容易绘制图形,而不是使用 sys.exit
退出,而是更好地跳出 while 循环。您也只绘制一次结果:
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
...
pygame.quit()
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
实施所有这些结果:
import random
import pygame
import matplotlib.pyplot as plt
from pygame.locals import *
import time
import numpy as np
pygame.init()
# Window details
windowWidth = 400
windowHeight = 400
pixSize = 2
FPS = 60
screen = pygame.display.set_mode((windowWidth, windowHeight))
pygame.display.set_caption("Infection Game")
class Cell:
def __init__(self):
self.xPos = random.randrange(1, windowWidth)
self.yPos = random.randrange(1, windowHeight)
self.speed = 2
self.isInfected = False
self.infectionRange = 5
self.move = [None, None]
self.direction = None
def cellDraw(self):
if not self.isInfected:
pygame.draw.rect(screen, (255, 255, 255), (self.xPos, self.yPos, pixSize, pixSize), 0)
else:
pygame.draw.rect(screen, (0, 255, 0), (self.xPos, self.yPos, pixSize, pixSize), 0)
def cellMovement(self):
directions = {"S": ((-1, 2), (1, self.speed)), "SW": ((-self.speed, -1), (1, self.speed)),
"W": ((-self.speed, -1), (-1, 2)), "NW": ((-self.speed, -1), (-self.speed, -1)),
"N": ((-1, 2), (-self.speed, -1)), "NE": ((1, self.speed), (-self.speed, -1)),
"E": ((1, self.speed), (-1, 2)),
"SE": ((1, self.speed), (1, self.speed))} # ((min x, max x)(min y, max y))
directionsName = ("S", "SW", "W", "NW", "N", "NE", "E", "SE") # possible directions
if random.randrange(0, 5) == 2: # move about once every 5 frames
if self.direction is None: # if no direction is set, set a random one
self.direction = random.choice(directionsName)
else:
a = directionsName.index(self.direction) # get the index of direction in directions list
b = random.randrange(a - 1,
a + 2) # set the direction to be the same, or one next to the current direction
if b > len(directionsName) - 1: # if direction index is outside the list, move back to the start
b = 0
self.direction = directionsName[b]
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.xPos < 5 or self.xPos > windowWidth - 5 or self.yPos < 5 or self.yPos > windowHeight - 5: # if cell is near the border of the screen, change direction
if self.xPos < 5:
self.direction = "E"
elif self.xPos > windowWidth - 5:
self.direction = "W"
elif self.yPos < 5:
self.direction = "S"
elif self.yPos > windowHeight - 5:
self.direction = "N"
self.move[0] = random.randrange(directions[self.direction][0][0], directions[self.direction][0][1]) + 0.35
self.move[1] = random.randrange(directions[self.direction][1][0], directions[self.direction][1][1]) + 0.35
if self.move[0] is not None: # add the relative coordinates to the cells coordinates
self.xPos += self.move[0]
self.yPos += self.move[1]
def Infect(self, uninfected, uninfected_array):
indices = np.greater(uninfected_array[:, 0], self.xPos - self.infectionRange) * \
np.greater(self.xPos + self.infectionRange, uninfected_array[:, 0]) * \
np.greater(uninfected_array[:, 1], self.yPos - self.infectionRange) * \
np.greater(self.yPos + self.infectionRange, uninfected_array[:, 1])
for i in np.where(indices)[0]:
uninfected[i].isInfected = True
xgraph = []
ygraph = []
cellList = []
startTime = time.time()
for i in range(1000):
cell = Cell()
cellList.append(cell)
cellList[0].isInfected = True
def gameLoop():
running = True
while running:
infectList = []
for event in pygame.event.get():
if event.type == QUIT:
running = False
screen.fill((0, 0, 0))
for i in cellList:
i.cellDraw()
i.cellMovement()
infected = 0
uninfected = [i for i in cellList if not i.isInfected]
uninfected_array = np.array([[i.xPos, i.yPos] for i in uninfected])
if len(uninfected) > 0:
for i in cellList:
if i.isInfected:
i.Infect(uninfected, uninfected_array)
infected += 1
if infected == 0:
infected = len(cellList) - len(uninfected)
xgraph.append(time.time() - startTime)
ygraph.append(infected)
pygame.display.update() # update display
pygame.time.Clock().tick(FPS) # limit FPS
pygame.quit()
# figured this is what you wanted to do ;)
plt.plot(xgraph, ygraph)
plt.xlabel('time (s)')
plt.ylabel('infected')
plt.show()
gameLoop()
而且运行流畅