A-star algorithm freezing seemlingly random after some time in Snake - python-3.x

I'm making an AI for the game Snake, here's my Code:
import math
class Node():
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
def astar(maze, start, end):
# Initialize the open list
open_list = []
start_node = Node(None, start)
start_node.g = start_node.h = start_node.f = 0
open_list.append(start_node)
# Initialize the closed list
closed_list = []
end_node = Node(None, end)
# While the open list is not empty
while len(open_list) > 0:
current_node = open_list[0]
current_index = 0
# Find the node with the least f on the open list
for index, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = index
# Pop current node off the open list
open_list.pop(current_index)
# Push current node on the closed list
closed_list.append(current_node)
# If current node is goal, then stop search
if current_node == end_node:
path = []
current = current_node
while current is not None:
path.append(current.position)
current = current.parent
return path[::-1]
# Find the 4 neighboring squares
children = []
for new_position in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
# Make sure it's within the maze
if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0:
continue
# Make sure it's not a wall
if maze[node_position[0]][node_position[1]] != 0:
continue
new_node = Node(current_node, node_position)
children.append(new_node)
# For each child
flag = 0
for child in children:
if child in closed_list:
continue
child.g = current_node.g + 1
child.h = hueristic_estimate(child, end_node)
child.f = child.g + child.h
for open_node in open_list:
if child.position == open_node.position and child.f > open_node.f:
flag = 1
break
if flag == 1:
continue
open_list.append(child)
return None
def hueristic_estimate(start_node, end_node):
return ((start_node.position[0] - end_node.position[0]) ** 2 + (start_node.position[1] - end_node.position[1]) ** 2) * 4
I have no idea why, but after a bit, around when the snake get's about 15 in length, it will freeze. I don't know whether it's running very, very, very slow or if it's in an infinite loop. I'm pretty sure this should be all correct, yet the problem persists.

Related

I keep getting "pygame.sprite.Sprite.add() argument after * must be an iterable, not int" error

I am trying to make a platforming game and I keep getting this error when I tried to add particles for when the character jumps, and I can't figure it out. Everytime I try and jump with the character the error pops up. I will need some help in order to fix this.
Code for the Level
import pygame
# Importing the necessary files
from tiles import Tile
from settings import tile_size, screen_width
from player import Player
from particles import ParticleEffect
class Level:
def __init__(self, level_data, surface):
# Level setup
self.display_surface = surface
self.setup_level(level_data)
self.world_shift = 0
self.current_x = 0
# Particles
self.dust_sprite = pygame.sprite.GroupSingle()
def setup_level(self, layout):
self.tiles = pygame.sprite.Group()
self.player = pygame.sprite.GroupSingle()
for row_index, row in enumerate(layout):
for column_index, cell in enumerate(row):
x = column_index * tile_size
y = row_index * tile_size
if cell == "X":
tile = Tile((x, y), tile_size)
self.tiles.add(tile)
if cell == "P":
player_sprite = Player((x, y), self.display_surface, self.jump_particles)
self.player.add(player_sprite)
def jump_particles(self, pos):
jump_particle_sprite = ParticleEffect(pos, 'Jump')
self.dust_sprite.add(jump_particle_sprite)
def scroll_x(self):
player = self.player.sprite
player_x = player.rect.centerx
direction_x = player.direction.x
if player_x < screen_width / 4 and direction_x < 0:
self.world_shift = 8
player.speed = 0
elif player_x > screen_width - (screen_width / 4) and direction_x > 0:
self.world_shift = -8
player.speed = 0
else:
self.world_shift = 0
player.speed = 8
# Checks for horizontal collisions with the tiles
def horizontal_collision(self):
player = self.player.sprite
player.rect.x += player.direction.x * player.speed
for sprite in self.tiles.sprites():
if sprite.rect.colliderect((player.rect)):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
player.on_left = True
self.current_x = player.rect.left
elif player.direction.x > 0:
player.rect.right = sprite.rect.left
player.on_right = True
self.current_x = player.rect.right
if player.on_left == True and (player.rect.left < self.current_x or player.direction.x >= 0):
player.on_left = False
if player.on_right == True and (player.rect.left > self.current_x or player.direction.x <= 0):
player.on_right = False
def vertical_collision(self):
player = self.player.sprite
player.apply_gravity()
for sprite in self.tiles.sprites():
if sprite.rect.colliderect((player.rect)):
if player.direction.y > 0:
player.rect.bottom = sprite.rect.top
player.direction.y = 0
player.on_ground = True
elif player.direction.y < 0:
player.rect.top = sprite.rect.bottom
player.direction.y = 0
player.on_ceiling = True
if player.on_ground == True and player.direction.y < 0 or player.direction.y > 1:
player.on_ground = False
if player.on_ceiling == True and player.direction.y > 0:
player.on_ceiling = False
def run(self):
# Tiles for the level
self.tiles.update(self.world_shift)
self.tiles.draw(self.display_surface)
self.scroll_x()
# Dust Particles
self.dust_sprite.update(self.world_shift)
self.dust_sprite.draw(self.display_surface)
# Player for the level
self.player.update()
self.horizontal_collision()
self.vertical_collision()
self.player.draw(self.display_surface)
Code for the Class ParticleEffect
import pygame
from support import import_folder
class ParticleEffect(pygame.sprite.Sprite):
def _init__(self, pos, type):
super().__init__()
self.frame_index = 0
self.animation_speed = 0.5
if type == 'Jump':
self.frames = import_folder('Hero/Dust Particles/Jump')
if type == 'Land':
self.frames = import_folder('Hero/Dust Particles/Land')
self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect(center = pos)
def animate(self):
self.frame_index += self.animation_speed
if self.frame_index >= len(self.frames):
self.kill()
else:
self.image = self.frames[int(self.frame_index)]
def update(self, x_shift):
self.animate()
self.rect.x += x_shift
import_folder is just a class used for loading images.
The full error is
File "C:\School\KS5\Computer Science\Project\Coding\pythonProject\player.py", line 109, in get_input
self.jump_particles(self.rect.midbottom)
File "C:\School\KS5\Computer Science\Project\Coding\pythonProject\level.py", line 35, in jump_particles
jump_particle_sprite = ParticleEffect(pos, 'Jump')
File "C:\School\KS5\Computer Science\Project\Coding\pythonProject\venv\lib\site-packages\pygame\sprite.py", line 115, in __init__
self.add(*groups)
File "C:\School\KS5\Computer Science\Project\Coding\pythonProject\venv\lib\site-packages\pygame\sprite.py", line 133, in add
self.add(*group)
File "C:\School\KS5\Computer Science\Project\Coding\pythonProject\venv\lib\site-packages\pygame\sprite.py", line 133, in add
self.add(*group)
TypeError: pygame.sprite.Sprite.add() argument after * must be an iterable, not int
You have a typo in the __init__() method in your ParticleEffect class that is causing this issue.
The def _init__(self, pos, type) is missing a underscore _ at the beginning.

Bug causes player to collide with the first two obstacles and nothing else [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I'm making a game environment for NEAT. The first two obstacles seem to collide with the players just fine, but none of the other obstacles do anything. The visuals won't work, but based on the size of the player list, yeah no it's not working. The collide class also wouldn't work before I started implementing NEAT, so there's that.
Anyway here's some (probably) irrelevant code:
import pygame
from pygame.locals import *
import random
import os
import neat
debugfun = 0
W = 300
H = 300
win = pygame.display.set_mode((W, H))
pygame.display.set_caption("bruv moment")
coords = [0, 60, 120, 180, 240]
class Player(object):
def __init__(self, x):
self.x = x
self.y = 600 - 60
self.width = 60
self.height = 60
self.pos = 0
self.left = False
self.right = False
def move(self):
if self.left:
self.pos -= 1
if self.right:
self.pos += 1
if self.pos < 0:
self.pos = 4
if self.pos > 4:
self.pos = 0
self.x = coords[self.pos]
class Block(object):
def __init__(self, pos, vel):
self.pos = pos
self.x = coords[self.pos]
self.y = -60
self.width = 60
self.height = 60
self.vel = vel
def move(self):
self.y += self.vel
def redraw_window():
pygame.draw.rect(win, (0, 0, 0), (0, 0, W, H))
for ob in obs:
pygame.draw.rect(win, (0, 255, 0), (ob.x, ob.y, ob.width, ob.height))
for ind in homies:
pygame.draw.rect(win, (255, 0, 0), (ind.x, ind.y, ind.width, ind.height))
pygame.display.update()
obs = []
homies = []
player_collision = False
ge = []
nets = []
And here's some relevant code:
def collide():
for ob in obs:
for x, ind in enumerate(homies):
if ind.y < ob.y + ob.height and ind.y + ind.height > ob.y and ind.x + ind.width > ob.x and ind.x < ob.x + ob.width:
ge[x].fitness -= 1
homies.pop(x)
nets.pop(x)
ge.pop(x)
def main(genomes, config):
speed = 30
pygame.time.set_timer(USEREVENT + 1, 550)
pygame.time.set_timer(USEREVENT + 2, 1000)
for _, g in genomes:
net = neat.nn.FeedForwardNetwork.create(g, config)
nets.append(net)
homies.append(Player(0))
g.fitness = 0
ge.append(g)
clock = pygame.time.Clock()
ran = True
while ran and len(homies) > 0:
clock.tick(27)
collide()
for x, ind in enumerate(homies):
ind.move()
ge[x].fitness += 0.1
try:
output = nets[x].activate((ind.x, abs(obs[0].x - ind.x)))
except IndexError:
output = 50
try:
if output in range(5, 25):
ind.left = True
else:
ind.left = False
if output > 25:
ind.right = True
else:
ind.right = False
except TypeError:
if output[0] in range(5, 25):
ind.left = True
else:
ind.left = False
if output[1] > 25:
ind.right = True
else:
ind.right = False
for ob in obs:
ob.move()
if ob.x > H:
obs.pop(obs.index(ob))
for event in pygame.event.get():
if event.type == pygame.QUIT:
ran = False
pygame.quit()
if event.type == USEREVENT+1:
if speed <= 200:
speed += 3
if event.type == USEREVENT+2:
for g in ge:
g.fitness += 0.5
if len(obs) == 0:
obs.append(Block(round(random.randint(0, 4)), speed))
print(len(homies))
print(len(obs))
redraw_window()
def run(config_file):
config = neat.config.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet,
neat.DefaultStagnation, config_path)
p = neat.Population(config)
p.add_reporter(neat.StdOutReporter(True))
stats = neat.StatisticsReporter()
p.add_reporter(stats)
winner = p.run(main, 50)
if __name__ == "__main__":
local_dir = os.path.dirname(__file__)
config_path = os.path.join(local_dir, "avoidpedofiles.txt")
run(config_path)

List in object gets overwritten

After some work I finished my first algorithm for a maze generator and now I am trying to make it visual in Pygame. I first let the algorithm generate a maze and then I make a visual representation of it.
Here I get into multiple problems, but I think they are all linked to the same thing which is that the first cell of the maze gets overwritten in some way. Because of this, what I get is totally not a maze at all but just some lines everywhere.
I tried putting the removal of walls in a seperate method, but that does not seem to work also. I looked if the remove walls method gets called at the first cell and it says it is true, but in some way the values of that cell gets overwritten.
Code:
import pygame
import random
WIDTH = 300
HEIGHT = 300
CellSize = 30
Rows = int(WIDTH/30)
Columns = int(HEIGHT/30)
current = None
grid = []
visited = []
DISPLAY = pygame.display.set_mode((WIDTH, HEIGHT))
class Cell:
def __init__(self, r, c):
self.r = r
self.c = c
self.x = CellSize * c
self.y = CellSize * r
self.sides = [True, True, True, True] # Top, Bottom, Left, Right
self.visited = False
self.neighbours = []
self.NTop = None
self.NBottom = None
self.NRight = None
self.NLeft = None
self.NTopIndex = None
self.NBottomIndex = None
self.NRightIndex = None
self.NLeftIndex = None
self.nr = None
self.nc = None
self.random = None
self.neighbour = None
def index(self, nr, nc):
self.nr = nr
self.nc = nc
if self.nr < 0 or self.nc < 0 or self.nr > Rows-1 or self.nc > Columns-1:
return -1
return self.nr + self.nc * Columns
def neighbour_check(self):
# Get neighbour positions in Grid
self.NTopIndex = self.index(self.r, self.c - 1)
self.NBottomIndex = self.index(self.r, self.c + 1)
self.NRightIndex = self.index(self.r + 1, self.c)
self.NLeftIndex = self.index(self.r - 1, self.c)
# Look if they are truly neighbours and then append to neighbour list
if self.NTopIndex >= 0:
self.NTop = grid[self.NTopIndex]
if not self.NTop.visited:
self.neighbours.append(self.NTop)
if self.NBottomIndex >= 0:
self.NBottom = grid[self.NBottomIndex]
if not self.NBottom.visited:
self.neighbours.append(self.NBottom)
if self.NRightIndex >= 0:
self.NRight = grid[self.NRightIndex]
if not self.NRight.visited:
self.neighbours.append(self.NRight)
if self.NLeftIndex >= 0:
self.NLeft = grid[self.NLeftIndex]
if not self.NLeft.visited:
self.neighbours.append(self.NLeft)
# Choose random neighbour
if len(self.neighbours) > 0:
self.random = random.randint(0, len(self.neighbours) - 1)
self.neighbour = self.neighbours[self.random]
# Remove the wall between self and neighbour
if self.neighbour == self.NTop:
if self == grid[0]:
print('TOP')
self.sides[0] = False
self.NTop.sides[1] = False
elif self.neighbour == self.NBottom:
if self == grid[0]:
print('BOTTOM')
self.sides[1] = False
self.NBottom.sides[0] = False
elif self.neighbour == self.NLeft:
if self == grid[0]:
print('LEFT')
self.sides[2] = False
self.NLeft.sides[3] = False
elif self.neighbour == self.NRight:
if self == grid[0]:
print('RIGHT')
self.sides[3] = False
self.NRight.sides[2] = False
else:
print('SIDES ERROR')
return self.neighbours[self.random]
else:
return -1
def draw(self):
global DISPLAY, CellSize
# Top
if self.sides[0]:
pygame.draw.line(DISPLAY, (0, 0, 0), (self.x, self.y), (self.x + CellSize, self.y))
# Bottom
if self.sides[1]:
pygame.draw.line(DISPLAY, (0, 0, 0), (self.x, self.y + CellSize), (self.x + CellSize, self.y + CellSize))
# Left
if self.sides[2]:
pygame.draw.line(DISPLAY, (0, 0, 0), (self.x, self.y), (self.x, self.y + CellSize))
# Right
if self.sides[3]:
pygame.draw.line(DISPLAY, (0, 0, 0), (self.x + CellSize, self.y), (self.x + CellSize, self.y + CellSize))
class Maze:
def __init__(self):
global current
self.next = None
self.running = True
self.DISPLAY = None
self.display_running = True
def init_cells(self):
# Make grid and make cell 0 the begin of the algorithm
global current
for i in range(0, Columns):
for j in range(0, Rows):
cell = Cell(j, i)
grid.append(cell)
current = grid[0]
def init_maze(self):
global current, visited
print(grid[0].sides)
# Start Algorithm
while self.running:
# Check if the current cell is visited, if not make it visited and choose new neighbour
if not current.visited:
current.visited = True
visited.append(current)
self.next = current.neighbour_check()
if not self.next == -1:
# If it finds a neighbour then make it the new current cell
# self.next.visited = True
current = self.next
elif self.next == -1 and len(visited) > 0:
# If it doesn't then look trough the path and backtrack trough it to find a possible neighbour
if len(visited) > 1:
del visited[-1]
current = visited[-1]
# If the last cell of the visited list is Cell 0 then remove it
elif len(visited) <= 1:
del visited[-1]
elif len(visited) <= 0:
# Stop the Algorithm
self.running = False
print('Done')
def draw(self):
DISPLAY.fill((255, 255, 255))
# Get the maze made by the algorithm and draw it on the screen
for i in range(0, len(grid)):
grid[i].draw()
pygame.display.update()
while self.display_running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.display_running = False
maze = Maze()
maze.init_cells()
maze.init_maze()
maze.draw()
I put some print methods in it for debugging purposes.
And am still a beginner in programming, I know it could probably be way cleaner or that some naming of methods could be better.
What I want to happen is that in def init_maze the maze blueprints gets written out and that in def draw the blueprint gets drawn on the screen.

Game of Life in pygame: How to be able to manually click on cells to activate/deactivate them?

Working on this Game of Life version in pygame.
In the end I want to have two options to run the game: one where you can start the game and it runs automatically with a random grid and a game where the user starts with an empty grid and can activate/deactivate cells.
Currently I only have the random grid mode and Im struggling with the activation/deactivation of cells.
In handle_events() there is a function that allows the user to click on cells to activate them in the random grid if the game is paused, but for some reason it doesn't allow the user to deactivate those cells again. Also, it is overwritten in the next iteration.
Does anyone know how i can fix this? And what would be the best way to create two different game modes ("random game" and "user selects cell mode").
Thanks in advance
I have posted the code below.
import pygame
import random
import sys
grid_size = width, height = 400, 400
cell_size = 10
color_dead = 0, 0, 0 # Background
color_alive = 255, 255, 255 # alive cell, can be any color. #orange = 255, 100, 0 #yellow = 255,255,0, # red=255,0,0 #Green 0,200,0
fps_max = 10
class GameOfLife:
def __init__(self):
#The screen
pygame.init()
pygame.display.set_caption("Game of Life - Created by ")
self.FPSCLOCK = pygame.time.Clock()
self.screen = pygame.display.set_mode(grid_size)
self.clear_screen() # you clear the screen before it starts running
pygame.display.flip() #Update the full display Surface to the screen
self.last_update_completed = 0
#self.desired_milliseconds_between_updates = (1.0 / fps_max) * 1000
self.active_grid = 0
self.num_cols = int(width / cell_size)
self.num_rows = int(height / cell_size)
self.grids = []
self.init_grids()
self.set_grid()
self.paused = False
self.game_over = False
def is_in_range(self, x, y):
if x in range(self.x, self.x + self.size + 1) and y in range(self.y, self.y + self.size + 1):
return True
else:
return False
def init_grids(self):
def create_grid():
rows = []
for row_num in range(self.num_rows):
list_of_columns = [0] * self.num_cols
rows.append(list_of_columns)
return rows
self.grids.append(create_grid())
self.grids.append(create_grid())
self.active_grid = 0
#print(self.grids[0])
#print(rows)
#print(type(rows))
#set_grid(0) = all dead
#set_grid(1) = all alive
#set_grid() = random
#set_grid(None) = random
def set_grid(self, value=None, grid =0):
for r in range(self.num_rows):
for c in range(self.num_cols):
if value is None:
cell_value = random.choice([0,1])
else:
cell_value = value
self.grids[grid][r][c] = cell_value
def draw_grid(self):
self.clear_screen() # you clear the screen before it starts running
for c in range(self.num_cols):
for r in range(self.num_rows):
if self.grids[self.active_grid][r][c] == 1:
color = color_alive
else:
color = color_dead
#pygame.draw.rect(self.screen, color, ((c * cell_size + (cell_size / 2)),(r * cell_size + (cell_size / 2)), cell_size, cell_size) )
posn = (int(c * cell_size + cell_size / 2),
int(r * cell_size + cell_size / 2))
pygame.draw.circle(self.screen, color, posn, int(cell_size / 2), 0)
pygame.display.flip()
def clear_screen(self):
self.screen.fill(color_dead)
def get_cell(self, r, c):
try:
cell_value = self.grids[self.active_grid][r][c]
except:
#print("Couldn't get cell value: row: %d, col %d" % (r, c))
cell_value = 0
return cell_value
def check_cell_neighbors(self, row_index, col_index):
# Get the number of alive cells surrounding the current cell
# self.grids[self.active_grid][r][c] #is the current cell
num_alive_neighbors = 0
num_alive_neighbors += self.get_cell(row_index - 1, col_index - 1)
num_alive_neighbors += self.get_cell(row_index - 1, col_index)
num_alive_neighbors += self.get_cell(row_index - 1, col_index + 1)
num_alive_neighbors += self.get_cell(row_index, col_index - 1)
num_alive_neighbors += self.get_cell(row_index, col_index + 1)
num_alive_neighbors += self.get_cell(row_index + 1, col_index - 1)
num_alive_neighbors += self.get_cell(row_index + 1, col_index)
num_alive_neighbors += self.get_cell(row_index + 1, col_index + 1)
#print(num_alive_neighbors)
#print("alive neighbors: %d")
# Rules
#1 Any live cell with fewer than two live neighbours dies, as if by underpopulation.
#2 Any live cell with two or three live neighbours lives on to the next generation.
#3 Any live cell with more than three live neighbours dies, as if by overpopulation.
#4 Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
if self.grids[self.active_grid][row_index][col_index] == 1: #Alive
if num_alive_neighbors > 3:
return 0 # it dies of overpopulation # More than three live neighbors, rule number 3.
if num_alive_neighbors < 2:
return 0 # it dies of underpopulation = Rule number 1 = fewer than two live neighbors
if num_alive_neighbors == 2 or num_alive_neighbors == 3: # If there are 3 or 4 neighbors, and the cell is alive, it stays alive.
return 1 # Rule number 2. Two or three live neighbours, it continuous to live.
elif self.grids[self.active_grid][row_index][col_index] == 0: #Dead
if num_alive_neighbors ==3:
return 1 #It comes to life.
return self.grids[self.active_grid][row_index][col_index]
def update_generation(self):
"""
Inspect current generation state, prepare next generation
:return:
"""
self.set_grid(0, self.inactive_grid())
for r in range(self.num_rows - 1):
for c in range(self.num_cols - 1):
next_gen_state = self.check_cell_neighbors(r, c)
# Set inactive grid future cell state
self.grids[self.inactive_grid()][r][c] = next_gen_state # if it is zero, than is is 1. if it is 1, it is gonna be 0. Picks the offgrid.
self.active_grid = self.inactive_grid()
#inspect the current active generation
# update the inactive grid to store next generation
#swap out the active grid
#self.set_grid(None) #This means that you randomize the grid
def inactive_grid(self):
return (self.active_grid + 1) % 2
def handle_events(self):
for event in pygame.event.get():
if self.paused:
if event.type == pygame.MOUSEBUTTONDOWN:
if(event.button==1):
mousepos_x, mousepos_y = event.pos
r, c = ((mousepos_x - cell_size / 2) // cell_size,
(mousepos_y - cell_size / 2) // cell_size)
print(event.pos, '->', (r, c)) # Show result.
mousepos_x, mousepos_y = event.pos# Index Y rows down, X columns to the right
for col in range(self.num_cols):
for row in range(self.num_rows):
if self.grids[self.active_grid][col][row] == 1:
color = color_dead
elif self.grids[self.active_grid][col][row] == 0:
color = 255,0,255#color_alive
posn = (int(r * cell_size + cell_size / 2),
int(c * cell_size + cell_size / 2))
print(posn)
pygame.draw.circle(self.screen, color, posn, int(cell_size / 2), 0)
pygame.display.flip()
if event.type == pygame.KEYDOWN:
if event.unicode == 's':
if self.paused:
self.paused = False
print("unpaused")
else:
self.paused = True
print("paused")
#Randomizin the grid
elif event.unicode == 'r':
print("randomizing the grid")
self.active_grid = 0
self.set_grid(None, self.active_grid) #randomizing
self.set_grid(0,self.inactive_grid()) #set to 0.
self.draw_grid() #Even if it is paused.
# Quitfunction
elif event.unicode == 'q': #If I press q, game_over becomes TRUE, which returns/ends in the def run().
print("Quitting the grid")
self.game_over = True
# print(event.unicode)
# print("Key pressed")
# print(event.unicode)
# if event is keypress of "s" then pause the loop/game.
#if event is keypress "r" then randomize grid
# if event is keypress of "q"then quit
if event.type == pygame.QUIT:
sys.exit()
def run(self):
while True:
if self.game_over:
return #So if it is game_over by pressing Q, you leave the loop.
self.handle_events() # when you run, you want to handle the events
if self.paused:
continue
self.update_generation() # Upgrade the generation
self.draw_grid() # and draw the grid
self.FPSCLOCK.tick(fps_max)
if __name__ == "__main__":
game = GameOfLife()
game.run()
It looks like you're simply changing the color of the cells instead of directly changing the values themselves. So add the specific indexing and set the value to their opposite and that should fix both problems.
mousepos_x, mousepos_y = event.pos# Index Y rows down, X columns to the right
if self.grids[self.active_grid][int(mousepos_x / 10)][int(mousepos_y / 10)] == 1:
color = color_dead
self.grids[self.active_grid][int(mousepos_x / 10)][int(mousepos_y / 10)] = 0
elif self.grids[self.active_grid][int(mousepos_x / 10)][int(mousepos_y / 10)] == 0:
color = 255,0,255#color_alive
self.grids[self.active_grid][int(mousepos_x / 10)][int(mousepos_y / 10)] = 1
This should fix both problems.
I'm not sure what you're trying to do in the handle_events() function for handling mouse button clicks. Here's a version that will allow cells to be activated and deactived when clicked, and the changes are overwritten in the next generation (unless they all happened to die due to application of the generation update rules).
I don't think you need to have the different modes (e.g. "random" vs "user selects") because that can be accomplished via the various single-key commands you've already implemented, although you woujld need to add one more that allowed manual clearing of the grid.
def handle_events(self):
for event in pygame.event.get():
if self.paused:
if event.type == pygame.MOUSEBUTTONDOWN:
if(event.button==1):
mousepos_x, mousepos_y = event.pos
c, r = (int((mousepos_x - cell_size / 2) // cell_size),
int((mousepos_y - cell_size / 2) // cell_size))
#print(event.pos, '->', (r, c)) # Show result.
if r in range(self.num_rows) and c in range(self.num_cols):
# Toggle state of cell: active <--> inactive
if self.grids[self.active_grid][r][c] == 1:
self.grids[self.active_grid][r][c] = 0
color = color_dead
else:
self.grids[self.active_grid][r][c] = 1
color = color_alive
# Redraw cell in its new color.
posn = (int(c * cell_size + cell_size / 2),
int(r * cell_size + cell_size / 2))
#print(' posn:', posn)
pygame.draw.circle(self.screen, color, posn, int(cell_size / 2), 0)
pygame.display.flip()
if event.type == pygame.KEYDOWN:
if event.unicode == 's':
.
.
.

pygame sets position of all instances of class to the same value for some reason?

I'm making a snake game in pygame and i've run into a problem. Whenever the snake eats food, the player gains a point. From that point on a tail is created behind the snake whenever it moves. Only the tails the number of player points away from the snake head will be drawn while the rest are deleted. The problem arises when i'm creating the tails. Whenever i create an instance of the tail, i have to get the position of the snake head and subtract away a value equal to the snake size in the opposite direction. That's where the tail will be drawn. However the position of all the tails are set to the same value for some reason and i cant't figure out why that is. I'm using my own library so i cant post it in here but iv'e determined it's not the cause.
import pygame as pg
from random import randrange
import widget
# disp -> display properties
disp = widget.get_json("config", ["display"])
food = widget.Surface(image="../images/food.png", pos=[0, 0])
def set_food_pos(snake):
while True:
pos = [randrange(0, disp["size"][_], disp["cell"]) for _ in range(2)]
safe = 0
for tail in snake.tails:
if tail.pos != pos: safe += 1
if safe == len(snake.tails):
food.pos = pos
food.rect.topleft = food.pos
break
class Snake(widget.Sprite):
""" Snake: main playable sprite """
SIZE = [disp["cell"]] * 2
KEYS = [[276, 275], [273, 274]]
def __init__(self):
self.image = pg.image.load("../images/snake_head.png")
self.pos = widget.VEC(0, 0)
super().__init__(pg.sprite.GroupSingle)
self.axis, self.orient, self.do_move = 0, 1, False
self.past, self.delay = pg.time.get_ticks(), 150
self.speed, self.vel = disp["cell"], [-1, 1]
self.alive, self.points = True, 0
self.tails = [self]
def control(self, key):
axis = [0 if key in Snake.KEYS[0] else 1][0]
if axis != self.axis:
if self.do_move:
self.axis = axis
self.orient = Snake.KEYS[axis].index(key)
self.do_move = False
def time_base_movement(self):
now = pg.time.get_ticks()
if now - self.past >= self.delay:
self.do_move = True
self.pos[self.axis] += self.vel[self.orient] * self.speed
self.past = pg.time.get_ticks()
def eat_food(self):
if food.rect.contains(self.rect):
set_food_pos(self)
self.points += 1
def create_tail(self):
if self.points:
if self.do_move:
pos = [_ for _ in self.rect.topleft]
pos[self.axis] += self.vel[::-1][self.orient] * 20
tail = widget.Sprite(image="../images/snake_head.png", pos=pos)
self.tails.insert(0, tail)
def render_tails(self, surface):
if self.points > 0:
tails = self.tails[:-1]
for tail in tails[0:self.points]: tail.group.draw(surface)
[self.tails.remove(tail) for tail in tails[self.points:]]
def check_boundary_collision(self):
for _ in range(2):
if self.pos[_] > disp["size"][_] - Snake.SIZE[_]:self.alive = False
elif self.pos[_] < 0: self.alive = False
for tail in self.tails[:-1]:
if tail.rect.contains(self.rect): self.alive = False
def reset_properties(self):
if self.alive == False:
print([tail.pos for tail in self.tails[:-1]])
self.tails = [self]
self.do_move = False
self.pos = widget.VEC([0, 0])
self.rect.topleft = self.pos
self.axis, self.orient = 0, 1
self.points, self.alive = 0, True
set_food_pos(self)
def update(self):
if self.alive:
self.time_base_movement()
self.check_boundary_collision()
self.reset_properties()
self.rect.topleft = self.pos
I figured it out, it seems python doesn't create a new copy of an attribute each time it is assigned to a different attribue. Instead the new attribute points to the assigned attribute. The fix for this is the "deepcopy" method in the built in module "copy".
Exe: new_value = copy.deepcopy(old_value)

Resources