Search code examples
pythonpython-3.xpygametetris

Pygame Tetris Issue


I've been writing a very rudimentary version of Tetris for Pygame in python 3.6. The only features so far are the blocks falling, making them fall faster, moving left and right, and when a block hits the ground, a new one spawns.

However, there is the issue. When the first block hits the ground, blocks spawn indefinitely at the top of the screen. I scoured the code and also showed it to my friend, and we couldn't find the issue. I scrapped the code and rewrote it, and the problem persisted. Does anyone here see it?

Thanks

P.S. I'm pretty sure that the top 2/3 of the code is not the issue.

import pygame, random

screen = pygame.display.set_mode((400,600))
pygame.display.set_caption("Tetris")
done = False
fast = False
locked = False
fallingblocks = []
setblocks = []
clock = pygame.time.Clock()
fallcooldown = 0

class Block:

  def __init__(self, x, y, color):
    self.x = x
    self.y = y
    self.color = color
    fallingblocks.append(self)
    self.rect = pygame.Rect(self.x + 1, self.y, 23, 25) #game only cares if falling block collides on the top or bottom, not side

  def fall(self):
    self.y += 25

  def move(self):
    if pressed[pygame.K_a] or pressed[pygame.K_LEFT]: self.x -= 25
    if pressed[pygame.K_d] or pressed[pygame.K_RIGHT]: self.x += 25

  def drawblock(self): #just to make it look nice
    pygame.draw.rect(screen, self.color[0], pygame.Rect(self.x,self.y,25,25))
    pygame.draw.polygon(screen, self.color[1], ((self.x,self.y),(self.x+3,self.y+3),(self.x+21,self.y+3),(self.x+24,self.y)))
    pygame.draw.polygon(screen, self.color[2], ((self.x,self.y),(self.x+3,self.y+3),(self.x+3,self.y+21),(self.x,self.y+24)))
    pygame.draw.polygon(screen, self.color[3], ((self.x,self.y+24),(self.x+3,self.y+21),(self.x+21,self.y+21),(self.x+24,self.y+24)))
    pygame.draw.polygon(screen, self.color[4], ((self.x+24,self.y+24),(self.x+21,self.y+21),(self.x+21,self.y+3),(self.x+24,self.y)))

def spawn():
  blocknum = random.randint(0,6)

  if blocknum == 0:
    #I block
    colors = [(129,184,231),
    (179,223,250),
    (146,202,238),
    (76,126,189),
    (96,157,213)]

    Block(175,0,colors)
    Block(175,25,colors)
    Block(175,50,colors)
    Block(175,75,colors)
  elif blocknum == 1:
    #J block
    colors = [(77,110,177),
    (149,178,229),
    (104,145,203),
    (49,63,136),
    (63,85,158)]

    Block(200,0,colors)
    Block(200,25,colors)
    Block(200,50,colors)
    Block(175,50,colors)
  elif blocknum == 2:
    #L block
    colors = [(219,127,44),
    (243,191,122),
    (229,158,69),
    (166,71,43),
    (193,98,44)]

    Block(175,0,colors)
    Block(175,25,colors)
    Block(175,50,colors)
    Block(200,50,colors)  
  elif blocknum == 3:
    #O block
    colors = [(248,222,49),
    (246,243,139),
    (245,235,86),
    (183,160,54),
    (213,190,55)]

    Block(175,0,colors)
    Block(175,25,colors)
    Block(200,0,colors)
    Block(200,25,colors)
  elif blocknum == 4:
    #S block
    colors = [(156,195,76),
    (204,218,127),
    (174,208,79),
    (109,157,75),
    (140,183,93)]

    Block(175,0,colors)
    Block(175,25,colors)
    Block(200,0,colors)
    Block(150,25,colors)   
  elif blocknum == 5:
    #Z block
    colors = [(204,42,40),
    (226,138,132),
    (213,90,69),
    (151,34,42),
    (181,37,43)]

    Block(175,0,colors)
    Block(225,25,colors)
    Block(200,0,colors)
    Block(200,25,colors)
  else:
    #T block
    colors = [(147,68,149),
    (187,145,194),
    (156,101,167),
    (108,45,123),
    (128,47,135)]

    Block(175,0,colors)
    Block(175,25,colors)
    Block(200,0,colors)
    Block(150,0,colors)

spawn()

#Pretty sure that everything above here is not the issue

while not done: #main loop

  screen.fill((0,0,32))
  pressed = pygame.key.get_pressed()

  for fallingblock in fallingblocks:
    fallingblock.drawblock()
    fallingblock.move()

  for setblock in setblocks:
    setblock.drawblock()

  if fallcooldown >= 50: #makes all pieces fall at once
    for fallingblock in fallingblocks:
      fallingblock.fall()
    fallcooldown = 0
  pygame.display.flip()

  if pressed[pygame.K_SPACE]: #if you want the piece to go the ground instantly
    fast = True

  if fast: fallcooldown = 50 #falling movements
  elif pressed[pygame.K_DOWN]: fallcooldown += 8 #goes faster
  else: fallcooldown += 1 #default speed

  for fallingblock in fallingblocks:
    for setblock in setblocks:
      if fallingblock.rect.colliderect(setblock.rect): #if fallingblock collides with setblock
        locked = True
    if fallingblock.y >= 575 and not locked: #if block hits the bottom
      locked = True

  if locked: #if block is in final state
    setblocks += fallingblocks 
    fallingblocks = []
    spawn()
    locked = False

  clock.tick(50)
  pygame.display.flip()

  for event in pygame.event.get():
      if event.type == pygame.QUIT:
          done = True

Solution

  • The problem is caused in the fall method of the Block. You only change the y attribute but never move the rect of the block, so it actually stays at the top of the screen the whole time. So remove the x, y attributes and just use self.rect.x and self.rect.y, or alternatively set self.rect.y = self.y.

    To debug the code I first printed print(locked, len(fallingblocks), len(setblocks)) above the if locked: line to confirm that locked was always True after the first block touched the ground. Then I tried to comment out the collision detection with the setblocks and the continuous spawning stopped. The next step was to print the rects of the setblocks and fallingblocks and it revealed that the y-pos of the rects was always 0 or 25 and never changed. I looked at the movement code in the fall method and noticed that the rect doesn't get moved.

    There are more issues, but I think you should try to continue with the debugging first and ask new questions if you still have trouble.