Conway's Game of Life Python / PyGame printing error [closed] - python

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I'm new to python and PyGame and wanted to get some experience by doing what i thought would be a simple project. I can't tell if my error is in my game logic or my PyGame printing. I created two function, one that fills the grid with random values and one that fills the grid with a "Blinker". The program runs without error, however, the rules of the game are not followed. For example, When the "blinker" is set, the program's second frame clears the screen instead of rotating the "blinker".
Any help diagnosing this problem would be appreciated!
import pygame
import random
pygame.init()
# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
# Sizes
size = (600, 600)
width = 20
height = 20
margin = 1
x_size = 600 / width
y_size = 600 / height
def init_grid():
return [[0 for x in range(x_size)] for y in range(y_size)]
def make_spinner(grind):
grid[0][0] = 1
grid[10][10] = 1
grid[10][11] = 1
grid[10][12] = 1
def random_grid(grid):
for x in range(x_size):
for y in range(y_size):
grid[x][y] = random.randint(0, 1)
def print_grid(screen, grid):
for x in range(x_size):
for y in range(y_size):
if grid[x][y] == 1:
pygame.draw.rect(
screen, BLACK, (x * width, y * height, width, height))
else:
pygame.draw.rect(
screen, BLACK, (x * width, y * height, width, height), margin)
def count_neighbours(grid, x, y):
count = 0
for i in range(-1, 1):
for j in range(-1, 1):
count += grid[x + i][y + j]
return count - grid[x][y]
def update_grid(grid):
next_grid = init_grid()
for x in range(x_size):
for y in range(y_size):
if x == 0 or x == x_size - 1 or y == 0 or y == y_size - 1:
next_grid[x][y] = 0
else:
count = count_neighbours(grid, x, y)
value = grid[x][y]
if value == 1 and (count == 2 or count == 3):
next_grid[x][y] = 1
elif value == 0 and count == 3:
next_grid[x][y] = 1
else:
next_grid[x][y] = 0
return next_grid
# Initialise game engine
screen = pygame.display.set_mode(size)
pygame.display.set_caption("The Game of Life")
running = True
clock = pygame.time.Clock()
grid = init_grid()
# random_grid(grid)
make_spinner(grid)
# Game loop
while running:
# Check for exit
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(WHITE)
print_grid(screen, grid)
next_grid = update_grid(grid)
pygame.display.update()
grid = next_grid
clock.tick(2)
pygame.quit()

Your count_neighbors function doesn't iterate over the right cells. range(-1,1) iterates over {-1,0} not {-1,0,1}.
Instead, use:
def count_neighbours(grid, x, y):
count = 0
for i in range(-1,2):
for j in range(-1,2):
count += grid[x + i][y + j]
return count - grid[x][y]

Related

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

Python random.randint(a,b) not producing random values for a 2d grid

I'm new to coding and making my own MineSweeper game. I'm using PyGame.
When I use the code below I would use the list Tiles to draw a 2d grid with red pixels for the Items and gray pixels for the others. I would get a image similar to this: Image of the result. As you can see the pixels are not arranged in a random manner. Re-seeding the random number generator doesn't help, I just get a different not-random pattern.
import pygame
import random
Height = 30
Length = 40
pygame.init()
win = pygame.display.set_mode((Length * 20 + 40,Height * 20 + 60))
Items = 50
Tiles = []
for i in range(Height * Length):
Tiles.append(99)
for i in range(Items):
index = random.randint(1,Height * Length-1)
if Tiles[index] == 99:
Tiles[index] = -1
else:
while not Tiles[index] == 99:
index = random.randint(1,Height * Length-1)
Tiles[index] = -1
def Render():
global Tiles
win.fill((100,100,100))
for x in range(Length):
for y in range(Height):
if Tiles[x*4 + y] == 99:
if (x*3 + y) % 2 == 0:
pygame.draw.rect(win, (150,150,150), (x*20+20,y*20+40,20,20))
else:
pygame.draw.rect(win, (130,130,130), (x*20+20,y*20+40,20,20))
elif Tiles[x*4 + y] == -1:
if (x*3 + y) % 2 == 0:
pygame.draw.rect(win, (255,0,0), (x*20+20,y*20+40,20,20))
else:
pygame.draw.rect(win, (225,0,0), (x*20+20,y*20+40,20,20))
run = True
while run is True:
pygame.time.delay(15)
keys = pygame.key.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if keys[pygame.K_ESCAPE]:
run = False
Render()
pygame.display.update()
pygame.quit()
Why is random.randint() not giving random values and if they are why are they like this in the image? I have searched other posts saying things like the birthday paradox but surely this can't happen every single time I run it?
Change Tiles[x*4 + y] to Tiles[x*Height + y].

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.

Python pygame noob question about animation [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
could you help me with a problem I have? I am a new person with regard to programming and to guide me I am using the book: How to think like a computer scientist 3rd edition. And it could not solve exercise 2 of chapter 17. This says that an error occurs when clicking on any frame that is on the right side of the sprite, which causes the animation to start, in theory it should only do the animation if it is you click directly on the sprite, I tried to solve it in many ways but I couldn't, could you help me ?, I think the error occurs in this part
return ( x >= my_x and x + my_width and y >= my_y and y < my_y + my_height)
but I'm not sure, for anything I leave you all the code I have
import pygame
gravity = 0.025
my_clock = pygame.time.Clock()
class QueenSprite:
def __init__(self, img, target_posn):
self.image = img
self.target_posn = target_posn
(x, y) = target_posn
self.posn = (x, 0) # Start ball at top of its column
self.y_velocity = 0 # with zero initial velocity
def update(self):
self.y_velocity += gravity
(x, y) = self.posn
new_y_pos = y + self.y_velocity
(target_x, target_y) = self.target_posn # Unpack the position
dist_to_go = target_y - new_y_pos # How far to our floor?
if dist_to_go < 0: # Are we under floor?
self.y_velocity = -0.65 * self.y_velocity # Bounce
new_y_pos = target_y + dist_to_go # Move back above floor
self.posn = (x, new_y_pos) # Set our new position.
def draw(self, target_surface): # Same as before.
target_surface.blit(self.image, self.posn)
def contains_point(self, pt):
""" Return True if my sprite rectangle contains point pt """
(my_x, my_y) = self.posn
my_width = self.image.get_width()
my_height = self.image.get_height()
(x, y) = pt
return ( x >= my_x and x < my_x + my_width and
y >= my_y and y < my_y + my_height)
def handle_click(self):
self.y_velocity += -2 # Kick it up
class DukeSprite:
def __init__(self, img, target_posn):
self.image = img
self.posn = target_posn
self.anim_frame_count = 0
self.curr_patch_num = 0
def update(self):
if self.anim_frame_count > 0:
self.anim_frame_count = (self.anim_frame_count + 1 ) % 60
self.curr_patch_num = self.anim_frame_count // 6
def draw(self, target_surface):
patch_rect = (self.curr_patch_num * 50, 0,
50, self.image.get_width())
target_surface.blit(self.image, self.posn, patch_rect)
def contains_point(self, pt):
""" Return True if my sprite rectangle contains pt """
(my_x, my_y) = self.posn
my_width = self.image.get_width()
my_height = self.image.get_height()
(x, y) = pt
return ( x >= my_x and x + my_width and y >= my_y and y < my_y + my_height)
def handle_click(self):
if self.anim_frame_count == 0:
self.anim_frame_count = 5
def draw_board(the_board):
""" Draw a chess board with queens, as determined by the the_board. """
pygame.init()
colors = [(255,0,0), (0,0,0)] # Set up colors [red, black]
n = len(the_board) # This is an NxN chess board.
surface_sz = 480 # Proposed physical surface size.
sq_sz = surface_sz // n # sq_sz is length of a square.
surface_sz = n * sq_sz # Adjust to exactly fit n squares.
# Create the surface of (width, height), and its window.
surface = pygame.display.set_mode((surface_sz, surface_sz))
ball = pygame.image.load("ball.png")
# Use an extra offset to centre the ball in its square.
# If the square is too small, offset becomes negative,
# but it will still be centered :-)
ball_offset = (sq_sz-ball.get_width()) // 2
all_sprites = [] # Keep a list of all sprites in the game
# Create a sprite object for each queen, and populate our list.
for (col, row) in enumerate(the_board):
a_queen = QueenSprite(ball,
(col*sq_sz+ball_offset, row*sq_sz+ball_offset))
all_sprites.append(a_queen)
# Load the sprite sheet
duke_sprite_sheet = pygame.image.load("duke_spritesheet.png")
# Instantiate two duke instances, put them on the chessboard
duke1 = DukeSprite(duke_sprite_sheet,(sq_sz*2, 0))
duke2 = DukeSprite(duke_sprite_sheet,(sq_sz*5, sq_sz))
# Add them to the list of sprites which our game loop manages
all_sprites.append(duke1)
all_sprites.append(duke2)
while True:
# Look for an event from keyboard, mouse, etc.
ev = pygame.event.poll()
if ev.type == pygame.QUIT:
break;
if ev.type == pygame.KEYDOWN:
key = ev.dict["key"]
if key == 27: # On Escape key ...
break # leave the game loop.
if key == ord("r"):
colors[0] = (255, 0, 0) # Change to red + black.
elif key == ord("g"):
colors[0] = (0, 255, 0) # Change to green + black.
elif key == ord("b"):
colors[0] = (0, 0, 255) # Change to blue + black.
if ev.type == pygame.MOUSEBUTTONDOWN: # Mouse gone down?
posn_of_click = ev.dict["pos"] # Get the coordinates.
for sprite in all_sprites:
if sprite.contains_point(posn_of_click):
sprite.handle_click()
break
for sprite in all_sprites:
sprite.update()
# Draw a fresh background (a blank chess board)
for row in range(n): # Draw each row of the board.
c_indx = row % 2 # Alternate starting color
for col in range(n): # Run through cols drawing squares
the_square = (col*sq_sz, row*sq_sz, sq_sz, sq_sz)
surface.fill(colors[c_indx], the_square)
# Now flip the color index for the next square
c_indx = (c_indx + 1) % 2
# Ask every sprite to draw itself.
for sprite in all_sprites:
sprite.draw(surface)
my_clock.tick(60) # Waste time so that frame rate becomes 60 fps
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
draw_board([0, 5, 3, 1, 6, 4, 2]) # 7 x 7 to test window size
There is < my_x missing in the comparisons expression in the method contains_point of the class DukeSprite:
return ( x >= my_x and x + my_width and y >= my_y and y < my_y + my_height)
return ( x >= my_x and x < my_x + my_width and y >= my_y and y < my_y + my_height)
Anyway in python you should use chained comparisons:
return my_x <= x < my_x + my_width and my_y <= y < my_y + my_height
In pygame you should use pygame.Rect and collidepoint(). The rectangle of the object you can get from the pygame.Surface with the method get_rect and the position can be set by an keyword argument:
def contains_point(self, pt):
""" Return True if my sprite rectangle contains pt """
my_rect = self.image.get_rect(topleft = self.posn)
return my_rect.collidepoint(pt)

Bouncing Ball doesn't come back pygame

import pygame
pygame.init()
width = 400
hight = 600
screen = pygame.display.set_mode((width, hight))
pygame.display.set_caption("Engine")
dot = pygame.image.load("KreisSchwarz.png")
clock = pygame.time.Clock()
running = True
WHITE = (255, 255, 255)
# Set (x, y) for Dot
def updateDot(x, y):
screen.blit(dot, (x, y))
# Display Dot at (x, y)
def update(fps=30):
screen.fill(WHITE)
updateDot(x, y)
pygame.display.flip()
return clock.tick(fps)
# Quit if User closes the window
def evHandler():
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
running = False
yKoords = []
(x, y) = (300, 200)
t = 1 # time variable
a = 2 # acceleration constant
tol = 40 # tolerance
i = 0 # just some iterator
# MAIN LOOP
while running:
evHandler()
update()
y += a * (t ^ 2)
t += 1
yKoords.append(int(y))
i += 1
if (y < (hight + tol)) and (y > (hight - tol)):
y = 580
yKoords.reverse()
update()
for q in range(i):
evHandler()
y = yKoords[q]
update()
if q == i - 1: # Because i didn't write the Part for the Dot coming back down
running = False
This is my Code for a Ball accelerating down and then jumping back up.
My Problem is, that the code works fine until the if statement. There the Programm just displays the Ball at the last position in yKoords and waits until the for loop finishes. If i remove the for loop the Ball gets displayed at y=580 and stops but thats fine.
Please help i have no idea whats wrong about this.
Don't do a separate process loop in the main loop.
It is sufficient to invert the direction, when the ball bounce on the ground (abs(y - hight)) or the ball reaches the top (t == 0).
direction = 1
while running:
evHandler()
update()
y += (a * (t ^ 2)) * direction
t += direction
if abs(y - hight) < tol:
y = 580
t -= 1
direction *= -1
elif t == 0:
direction *= -1

Categories