I have begun creating a Tetris game in Python. My problem is when I try to detect collisions with other blocks, the blocks get stuck at the top and spawn over and over again, like this:

Heres the code:
import pygame
import random
# Define Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
ORANGE = (255, 165, 0)
CYAN = (0, 255, 255)
# Define shapes
SHAPES = [
[[0, 1, 1, 1],
[0, 0, 1, 0]],
[[0, 0, 2, 2],
[0, 2, 2, 0]],
[[0, 3, 3, 0],
[0, 0, 3, 3]],
[[0, 4, 0, 0],
[0, 4, 4, 4]],
[[0, 0, 5, 0],
[0, 5, 5, 5]],
[[0, 6, 6],
[0, 6, 6]],
[[0, 7, 0, 0],
[0, 7, 7, 7]]
]
# Define shape class
class Shape:
def __init__(self, shape):
self.shape = shape
self.color = random.choice([RED, BLUE, GREEN, YELLOW, ORANGE, CYAN])
self.x = (WINDOW_WIDTH // 2) // block_size - len(self.shape[0]) // 2
self.y = 1
def draw(self, screen, block_size):
for i in range(len(self.shape)):
for j in range(len(self.shape[0])):
if self.shape[i][j] != 0:
pygame.draw.rect(screen, self.color, (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size))
pygame.draw.rect(screen, 'white', (self.x * block_size + j * block_size, self.y * block_size + i * block_size, block_size, block_size), 1)
def move_down(self):
self.y += 1
def move_up(self):
self.y -= 1
def collides_with(self, other):
shape_width = len(self.shape[0])
shape_height = len(self.shape)
for i in range(shape_height):
for j in range(shape_width):
if self.shape[i][j] != 0:
x_cor, y_cor = self.x + j, self.y + i
if y_cor >= len(other.shape) or x_cor < 0 or x_cor >= len(other.shape[0]) \
or (other.shape[y_cor][x_cor] != 0):
return True
return False
# Initialize Pygame
pygame.init()
# Set up window
WINDOW_WIDTH = 220
WINDOW_HEIGHT = 500
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption('Tetris')
# Set up clock
clock = pygame.time.Clock()
# Set up game variables
block_size = 20
shapes = []
current_shape = Shape(random.choice(SHAPES))
# Game loop
while True:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and current_shape.x > 0:
current_shape.x -= 1
if keys[pygame.K_RIGHT] and current_shape.x + len(current_shape.shape[0]) < 200 / block_size:
current_shape.x += 1
# Move current shape down
current_shape.move_down()
for shape in shapes:
if current_shape.collides_with(shape):
current_shape.move_up()
if current_shape.y <= 0:
pygame.quit()
quit()
else:
shapes.append(current_shape)
current_shape = Shape(random.choice(SHAPES))
break
# Check if current shape has collided with another shape or reached the bottom
if current_shape.y + len(current_shape.shape) >= 24:
shapes.append(current_shape)
current_shape = Shape(random.choice(SHAPES))
# Draw current shape and all other shapes on screen
screen.fill('dark gray')
for x in range(1, 10):
for y in range(1, 24):
# First creates square with gray color. Second draws black border for square
pygame.draw.rect(screen, 'light gray', (10 * x * 2, 10 * y * 2, 20, 20), 0)
pygame.draw.rect(screen, 'black', (10 * x * 2, 10 * y * 2, 20, 20), 1)
for shape in shapes:
shape.draw(screen, block_size)
current_shape.draw(screen, block_size)
pygame.display.update()
# Tick clock
clock.tick(10)
I've tried editing the outside barriers and changing some of the variables to see if I was slightly off, but it either continues to do this or the blocks fall to the bottom without any collision detection with other blocks.
This is the method that checks for collision with current shape and old shapes. The code below calls the function.
def collides_with(self, other):
shape_width = len(self.shape[0])
shape_height = len(self.shape)
for i in range(shape_height):
for j in range(shape_width):
if self.shape[i][j] != 0:
x_cor, y_cor = self.x + j, self.y + i
if y_cor >= len(other.shape) or x_cor < 0 or x_cor >= len(other.shape[0]) \
or (other.shape[y_cor][x_cor] != 0):
return True
return False
for shape in shapes:
if current_shape.collides_with(shape):
current_shape.move_up()
if current_shape.y <= 0:
pygame.quit()
quit()
else:
shapes.append(current_shape)
current_shape = Shape(random.choice(SHAPES))
break
It stops when it hits the bottom of the tiles and will stop when hitting the sides. but when I added the collide_with() method it either doesn't do anything and the block go to the bottom and go inside each other or they everyone after the first one gets stuck at the top and continues to spawn more and more. I want it so when the second block is falling it will stack ontop of the first one and so on.
I see two major issues in
collides_with:other.xandother.yare not used to get the right offset.x_corandy_corto be out of range are inverted: if they don't fall within the shape, that should mean there is no collision for that square, but now a single square that is outside of the other shape would cause a collision.This means that as soon as a shape is in
shapes, every framecollides_withwill returnTrue.