Conways Game of Life in PyGame - python

So I read about Conways Game of Life and tried to implement it with PyGame.
I tried to make it object-oriented. The way it works is that I have a list of cell-instances, which then check how many neighbours they have and then either stay alive or die, based on their neighbours. Then the process repeats itself.
The problem is that when I test it with some known starting patterns (e.g. in the code below (CELL_MAP)) it does not work the way it should.
I read the code over and over and I dont really get what I'm missing here. I posted the whole code below as I dont know where my mistake is, but I'd highly appreciate if someone would point me in the right direction.
Thanks in advance!
import pygame
class Cell:
def __init__(self, live, xcor, ycor):
self.alive = live
self.x = xcor
self.y = ycor
self.neighbours = 0
def checkNeighbours(self, cellList):
for cell in cellList:
#left
if cell.x == self.x-1 and cell.y == self.y and cell.alive == True:
self.neighbours += 1
#right
elif cell.x == self.x+1 and cell.y == self.y and cell.alive == True:
self.neighbours += 1
#upleft
elif cell.x == self.x-1 and cell.y == self.y-1 and cell.alive == True:
self.neighbours += 1
#up
elif cell.x == self.x and cell.y == self.y-1 and cell.alive == True:
self.neighbours += 1
#upright
elif cell.x == self.x+1 and cell.y == self.y-1 and cell.alive == True:
self.neighbours += 1
#downleft
elif cell.x == self.x-1 and cell.y == self.y+1 and cell.alive == True:
self.neighbours += 1
#down
elif cell.x == self.x and cell.y == self.y+1 and cell.alive == True:
self.neighbours += 1
#downright
elif cell.x == self.x+1 and cell.y == self.y+1 and cell.alive == True:
self.neighbours += 1
def breed(self):
if self.alive == False and self.neighbours == 3:
#dead cell ressurects if neighbours equals 3
self.alive = True
elif self.alive and self.neighbours < 2:
#die from loneliness
self.alive = False
elif self.alive and self.neighbours == 2:
#stay alive
pass
elif self.alive and self.neighbours == 3:
#stay alive
pass
elif self.alive and self.neighbours > 3:
#die from overpopulation
self.alive = False
def render(self, display):
if self.alive:
pygame.draw.rect(display, (0,0,0), [self.x*10, self.y*10, 10, 10])
elif self.alive == False:
pygame.draw.rect(display, (0,0,255), [self.x*10, self.y*10, 10, 10])
WID = 33
HEI = 20
CELL_MAP = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
CELL_LIST = []
xc = -1
yc = -1
for yList in CELL_MAP:
yc += 1
for x in yList:
xc += 1
if x == 0:
#create dead cell
newCell = Cell(False, xc, yc)
CELL_LIST.append(newCell)
elif x == 1:
#create alive cell
newCell = Cell(True, xc, yc)
CELL_LIST.append(newCell)
xc = -1
#pygame init
pygame.init()
(width, height) = (WID*10, HEI*10)
pygame.display.set_caption('Game of Life')
screen = pygame.display.set_mode((width, height))
#game loop
def gameLoop():
gameLoop = True
while gameLoop:
#check for exit
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameLoop = False
pygame.quit()
#render cells
for cell in CELL_LIST:
cell.render(screen)
#check neighbours
for cell in CELL_LIST:
cell.checkNeighbours(CELL_LIST)
pygame.display.flip()
#breed
for cell in CELL_LIST:
cell.breed()
pygame.time.wait(5)
quit()
if __name__ == "__main__":
gameLoop()

I don't have pygame installed, so I can't run your code. However, the bug that's causing your error is that you don't reset a cell's neighbour count to zero after you've determined whether it'll be alive or dead in the next generation. So in each generation each cell's new neighbour count gets added to the previous accumulated neighbour count. You should probably do that resetting in the .breed method.
Here's a more compact version of that method:
def breed(self):
self.alive = self.neighbours == 3 or self.alive and self.neighbours == 2
self.neighbours = 0
I have a few more comments about your code.
Your checkNeighbours method is extremely inefficient: for each cell, it scans the entire grid looking for a cell's neighbours! A simple alternative is to store your cells in a 2D list so you can quickly locate a cell's neighbours.
Here's a more compact way to build your CELL_LIST than what your code currently does:
CELL_LIST = []
for y, row in enumerate(CELL_MAP):
for x, v in enumerate(row):
CELL_LIST.append(Cell(v == 1, x, y))
Here's the same thing as a list comprehension:
CELL_LIST = [Cell(bool(v), x, y)
for y, row in enumerate(CELL_MAP)
for x, v in enumerate(row)
]
But as I said earlier, it's probably a good idea to make CELL_LIST a 2D list:
cell_list = [[Cell(bool(v), x, y) for x, v in enumerate(row)]
for y, row in enumerate(CELL_MAP)]
Your CELL_MAP isn't a convenient way to put Life patterns into your program, but I guess it's ok for testing purposes. Take a look at this answer I wrote earlier this month for an alternative.
Eventually, you should give your program the ability to read the common RLE format used by many Life programs.
You may also like to check out this moderately efficient version I wrote that uses Numpy: numpy_life.py. Like the other version I linked it displays the output in the Linux terminal, but both versions should be easy to adapt to pygame or another GUI framework.

Related

Optimizing Negamax Function with 5x5 Hexapawn

I need to improve the speed of this program, because at the moment it is pretty slow. I know that representing game states in binary can be very effective, however, I don't know how to do that. I have also tried using numba, however that seems to make it slower. I have attached the code below. Thank you to anyone who can help!
import pygame, sys, time, hashlib
from copy import deepcopy
pygame.init()
red = pygame.Color(255,0,0)
white = pygame.Color(255,255,255)
black = pygame.Color(0,0,0)
pygame.display.set_caption('Hexapawn AI')
width, height = 700,700
game_window = pygame.display.set_mode((width, height))
def set_pawns():
global game_window, board
for y in range(5):
for x in range(5):
if board[y][x] == 1:
game_window.blit( blue_pawn, ( (width/5)*x, (height/5)*(4-y) ))
if board[y][x] == -1:
game_window.blit( red_pawn, ( (width/5)*x , (height/5)*(4-y) ))
def build_lines():
global game_window
for x in range(1,5):
pygame.draw.line(game_window, black, (width/5 * x, 0), (width/5 * x, height), 7)
pygame.draw.line(game_window, black, (0, height/5 * x), (width, height/5 * x), 7)
def get_possible_moves(board, player):
possible_moves = []
forward = 1 if player == 1 else -1
opponent = -1 if player == 1 else 1
for y in range(5):
for x in range(5):
if board[y][x] != player:
continue
if x-1 >= 0 and y+forward < 5 and board[y+forward][x-1] == opponent:
possible_moves.append([x,y,x-1,y+forward])
if x+1 < 5 and y+forward < 5 and board[y+forward][x+1] == opponent:
possible_moves.append([x,y,x+1,y+forward])
if (y+1 < 5 and player == 1) or (y+1 > -1 and player == -1):
if board[y+forward][x] == " ":
possible_moves.append([x,y,x,y+forward])
return possible_moves
def make_move(board,move,player):
global game_window, width, height
game_window.fill(white)
build_lines()
board[move[1]][move[0]] = " "
board[move[3]][move[2]] = player
set_pawns()
def neg_make_move(board, move, player):
x1, y1, x2, y2 = move
board = deepcopy(board)
board[y1][x1] = " "
board[y2][x2] = player
return board
def check_for_win(board,player):
if player == -1:
if -1 in board[0]:
return True
if get_possible_moves(board,1) == []:
return True
elif player == 1:
if 1 in board[4]:
return True
if get_possible_moves(board,-1) == []:
return True
return False
TRANSPOSITION_TABLE = {}
def state_hash(board):
serialized = str(board).encode()
return hashlib.sha256(serialized).hexdigest()
def store(table, board, alpha, beta, best, depth):
state = state_hash(board)
if best[1] <= alpha:
flag = 'UPPERCASE'
elif best[1] >= beta:
flag = 'LOWERCASE'
else:
flag = 'EXACT'
table[state] = [best, flag, depth]
def negamax(board, depth, turn, alpha, beta):
alpha_org = alpha
state = state_hash(board)
if state in TRANSPOSITION_TABLE:
tt_entry = TRANSPOSITION_TABLE[state]
if tt_entry[2] >= depth:
if tt_entry[1] == 'EXACT':
return tt_entry[0]
elif tt_entry[1] == 'LOWERCASE':
alpha = max(alpha, tt_entry[0][1])
elif tt_entry[1] == 'UPPERCASE':
beta = min(beta, tt_entry[0][1])
if alpha >= beta:
return tt_entry[0]
if check_for_win(board, -turn):
return None, -(25+depth)
if depth == 0:
return get_possible_moves(board,turn)[0], (depth)
best_score = -200
for move in get_possible_moves(board,turn):
new_board = neg_make_move(board, move, turn)
score = -negamax(new_board, depth - 1, -turn, -beta, -alpha)[1]
alpha = max(alpha,score)
if score > best_score:
best_score, best_move = score, move
if alpha >= beta:
break
store(TRANSPOSITION_TABLE, board, alpha_org, beta, [best_move,best_score], depth)
return best_move, best_score
# Build board
board = [[1 for x in range(5)]]
for x in range(3):
board.append([" " for x in range(5)])
board.append([-1 for x in range(5)])
game_window.fill(white)
# Draw game board lines
build_lines()
# Load sprites with correct sizes
tile_size = (width/5,height/5)
blue_pawn = pygame.transform.scale(pygame.image.load("blue_pawn.png"), tile_size)
red_pawn = pygame.transform.scale(pygame.image.load("red_pawn.png"), tile_size)
# Draw the pawns to the board
set_pawns()
pygame.display.update()
while True:
for event in pygame.event.get():
# if user clicks the X or they type esc then the screen will close
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
start = time.time()
move = negamax(board,12,1,-10000,10000)[0]
print(f"Blue move took {time.time()-start} seconds to calculate.")
make_move(board,move,1)
pygame.display.update()
if check_for_win(board,1):
print("Blue Wins!")
pygame.quit()
sys.exit()
time.sleep(1)
start = time.time()
move = negamax(board,12,-1,-10000,10000)[0]
print(f"Red move took {time.time()-start} seconds to calculate.")
make_move(board,move,-1)
pygame.display.update()
if check_for_win(board,-1):
print("Red Wins!")
pygame.quit()
sys.exit()
pygame.display.update()
time.sleep(1)

How to correctly update the grid in falling sand simulation?

So, recently I started doing some python programming, and came across a video on Youtube in which guy showcases some of his simulations made in pygame (https://www.youtube.com/watch?v=M39R2A8kuh8).
I decided to do the easiest one, the Falling Sand Simulation. I implemented eveything correctly, but when it came to updating the grid I just couldn't do it right. In the end cells are positioned correctly at the bottom of screen, but they don't fall slowly, instead they just instantly teleport there. That's happening because when for loop comes across the cell it is being updated and falling down one row down, then loop comes across that same cell once more and same thing happens
I tried fixing it with second array which holds old grid and for some reason it didn't work.
Here's the code (please ignore my bad code, just a beginner xd):
import pygame
import random
from time import sleep
pygame.init()
WIDTH, HEIGHT = 800, 800
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Falling Sand Simulation")
BLACK = (0, 0, 0)
ORANGE = (158, 103, 32)
class Grid:
def __init__(self, width, height):
self.rows = int(width / 2)
self.columns = int(width / 2)
self.PreviousGrid = [[0 for i in range(self.columns)] for j in range(self.rows)]
self.CurrentGrid = [[0 for i in range(self.columns)] for j in range(self.rows)]
def add_cell(self, xpos, ypos):
xcell = int(xpos / 2)
ycell = int(ypos / 2)
self.CurrentGrid[xcell][ycell] = 1
def update_grid(self):
self.PreviousGrid = self.CurrentGrid
for i in range(self.rows):
if (i+1) != self.rows:
for j in range(self.columns):
if (j+1) != self.columns:
if self.PreviousGrid[i][j] == 0:
pass
else:
if self.PreviousGrid[i][j+1] == 0:
self.CurrentGrid[i][j] = 0
self.CurrentGrid[i][j+1] = 1
elif self.PreviousGrid[i-1][j+1] == 0 and self.PreviousGrid[i+1][j+1] == 0:
self.CurrentGrid[i][j] = 0
choice = random.randint(0, 1)
if choice == 0:
self.CurrentGrid[i-1][j+1] = 1
else:
self.CurrentGrid[i+1][j+1] = 1
elif self.PreviousGrid[i-1][j+1] == 0:
self.CurrentGrid[i][j] = 0
self.CurrentGrid[i-1][j+1] = 1
elif self.PreviousGrid[i+1][j+1] == 0:
self.CurrentGrid[i][j] = 0
self.CurrentGrid[i+1][j+1] = 1
def draw_grid(self, win):
for i in range(self.rows):
for j in range(self.columns):
if self.CurrentGrid[i][j] == 0:
pass
elif self.CurrentGrid[i][j] == 1:
pygame.draw.rect(win, ORANGE, pygame.Rect(int(i*2), int(j*2), 4, 4))
def main():
run = True
clock = pygame.time.Clock()
grid = Grid(WIDTH, HEIGHT)
update_rate = 0.05
countdownMS = update_rate
paused = False
while run:
clock.tick(30)
WIN.fill(BLACK)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sec = clock.get_rawtime()/100;
countdownMS -= sec;
if countdownMS < 0.0:
grid.update_grid()
countdownMS = update_rate
grid.draw_grid(WIN)
if pygame.mouse.get_pressed()[0]:
xpos, ypos = event.pos
grid.add_cell(xpos, ypos)
pygame.display.update()
pygame.quit()
if __name__ == '__main__':
main()
You have to create a new empty grid in update_grid. Copy the bottom line of the old grid and fill the rest of the new grid depending on the previous grid:
class Grid:
# [...]
def update_grid(self):
self.PreviousGrid = self.CurrentGrid
# create a new and empty grid
self.CurrentGrid = [[0 for i in range(self.columns)] for j in range(self.rows)]
for i in range(self.rows):
self.CurrentGrid[i][self.columns-1] = self.PreviousGrid[i][self.columns-1]
# fill the new grid depending on the previous grid
for i in range(self.rows):
if i+1 < self.rows:
for j in range(self.columns):
if j+1 < self.columns:
if self.PreviousGrid[i][j] == 1:
if self.PreviousGrid[i][j+1] == 0:
self.CurrentGrid[i][j+1] = 1
elif self.PreviousGrid[i-1][j+1] == 0 and self.PreviousGrid[i+1][j+1] == 0:
self.CurrentGrid[i+random.choice([-1, 1])][j+1] = 1
elif self.PreviousGrid[i-1][j+1] == 0:
self.CurrentGrid[i-1][j+1] = 1
elif self.PreviousGrid[i+1][j+1] == 0:
self.CurrentGrid[i+1][j+1] = 1
else:
self.CurrentGrid[i][j] = 1

I've been making conway's game of life in python, why doesn't it work?

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")
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)
Some other remarks
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.
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.

Wall Collision doesnt work even though iteration of list is in place

my Wall Collision code doesnt work. I dont get any error message, the player just goes through the walls. This is my code:
def drawRect(x, y):
pygame.draw.rect(screen, BLUE, [pxl(x), pxl(y), pxl(1), pxl(1)])
def drawLevel(level):
global gameDrawn, wallsCoord
x = y = 0
wallsCoord = []
walls = []
ends = []
if gameDrawn == False:
screen.fill(WOODY)
drawRect(1, 8)
showTimer()
for row in levels[level]:
for col in row:
if col == "W":
wall = Wall((x, y))
walls.append(wall)
wallsCoord.append((x, y))
if col == "E":
end = End((x, y))
ends.append(end)
if col == "P":
drawRect(1, 9)
x += 80
y += 80
x = 0
for wall in walls:
pygame.draw.rect(screen, BLACK, wall.rect)
for end in ends:
pygame.draw.rect(screen, RED, end.rect)
gameDrawn = True
return walls, ends
def drawPlayerPath(players):
global wallsCoord
for x, y in players:
if (x, y) in wallsCoord:
state = "gameover"
init_state = True
else:
drawRect(x, y)
def getPlayerPath(players):
global move_list, player_x, player_y
player_x, player_y = players[0]
for i in move_list:
if i == 1:
player_y -= 1
elif i == 2:
player_y += 1
elif i == 3:
player_x += 1
elif i == 4:
player_x -= 1
players.append((player_x, player_y))
I dont really get, why it doesnt work since I already iterated the x and y values of the players list over the coordinates list of the walls.
I suggest you add your walls in a sprite group and then you can iterate using the built-in collide method pygame.Rect.colliderect
You have to use the global statement, when you want to change a variable in global namespace within a function:
def drawPlayerPath(players):
global state, init_state # <---
for x, y in players:
if (x, y) in wallsCoord:
state = "gameover"
init_state = True
else:
drawRect(x, y)

Conway's game of life list index error

So I'm trying to make Conway's game of life in Python/pygame, and the first iteration of making the new grid works, but the second wont because of a list index out of range error. I have been trying to figure out what's wrong, but the list index shouldn't be out of range. This is my code, the mistake is supposedly in changevalue() but i suspect it isn't, since the first iteration works:
import pygame
import random
width = 400
height = 400
blocksize = 10
white = (255, 255, 255)
black = (0, 0, 0)
visual = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
IsOn = True
grid = []
templist = []
tempgrid = []
class square(object):
def __init__(self, x, y, alive):
self.x = x
self.y = y
self.alive = alive
for y in range(height/blocksize):
templist = []
for x in range(width/blocksize):
templist.append(square(x, y, random.choice([True, False, False, False])))
grid.append(templist)
def changevalue(cx, cy, cgrid):
neighbours = []
for dy in range(3):
ddy = dy - 1
for dx in range(3):
ddx = dx - 1
if not (dx - 1 == 0 and dy - 1 == 0):
#print cgrid[(cy + ddy)%len(cgrid)][(cx + ddx)%len(cgrid[y])].alive
#NO ERRORS
#print len(cgrid) > (cy + ddy)%len(cgrid), len(cgrid[y]) > (cx + ddx)%len(cgrid[cy])
#NO ERRORS
neighbours.append(cgrid[(cy + ddy)%len(cgrid)][(cx + ddx)%len(cgrid[cy])].alive)
return len(filter(lambda p: p == True, neighbours))
while IsOn:
for event in pygame.event.get():
if event.type == pygame.QUIT:
IsOn = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_c:
proceed = True
tempgrid = []
for times in range(len(grid)):
tempgrid.append([])
for ty in range(len(grid)):
for tx in range(len(grid[ty])):
if changevalue(tx, ty, grid) < 2 and grid[ty][tx].alive == True:
tempgrid[ty].append(square(tx, ty, False))
elif changevalue(tx, ty, grid) > 3 and grid[ty][tx].alive == True:
tempgrid[ty].append(square(tx, ty, False))
elif changevalue(tx, ty, grid) == 3 and grid[ty][tx].alive == False:
tempgrid[ty].append(square(tx, ty, True))
grid = list(tempgrid)
visual.fill(white)
for y in range(len(grid)):
for x in range(len(grid[y])):
if grid[y][x].alive == True:
pygame.draw.rect(visual, black, (grid[y][x].x*blocksize, grid[y][x].y*blocksize, blocksize, blocksize))
pygame.display.update()
clock.tick(2)
pygame.quit()
quit()
Thanks for your help!
You don't copy square which doesn't change value - so new rows have different length - and later you have problem with index
You need something like this
if changevalue ...:
...
elif changevalue ...:
...
elif changevalue ...:
...
else:
# copy other elements
tempgrid[ty].append(grid[ty][tx])

Categories