
In this article, we will build an amazing project Tetris game in Python code. In this project, we will use the pygame library to build the game. To create this project, ensure you have the latest version of Python installed in your system. Let’s get started!

Pygame
Pygame is a cross-platform set of Python modules that are used to create video games. It consists of computer graphics and sound libraries designed to be used with the Python programming language. Pygame is suitable to create client-side applications that can be potentially wrapped in a standalone executable. To learn pygame, it is required to have basic knowledge of Python.
To install pygame execute the following piece of code in the terminal
pip install pygameThat’s it, we are done with the prerequisites for the project. Let’s look into some of the important functions in this project.
This update_graphics method is used to design the game interface and make necessary updates during the execution of the game. The function sets the background color, and text to display, and sets borders and width. It displays the current score and time. It draws a small screen to show the next block. Set tiles for blocks 20 tile/square per row which is 19 horizontal lines and 10 tile/square per col which is 9 vertical lines.
The draw_small_screen method is used to design the same screen interface which displays the next block during the execution of the game. It sets the background, borders, and text and displays the next block.
The manage_events function is used to handle the blocks during the execution of the game.
Complete code for the Tetris game in Python
We can build Tetris games in Python with the help of Pygame since it has a lot of functions for this purpose. So now, let’s start with the implementation part. You can understand the code line-by-line with the help of comments.
You can download the images required for the project here.
Now create two python files and save them as util.py and tetris.py
util.py
#Import libraries
import pygame
import sys
import random
import time
# Define important global variables
pygame.init()
clock = pygame.time.Clock()
best_score = 0
longest_time = 0
width = 700
height = 750
DISPLAY_SCREEN = pygame.display.set_mode((width, height))
pygame.display.set_caption(" Tetris")
off_set_x = 10
off_set_y = 80
playing_field_width = 330 #330 / 10 = 33 width per tile
playing_field_height = 660 #600 / 20 = 33 height per tile
tile_length = 33 # tile is a square
#colors
blue = (0, 0, 255)
white = (255, 255, 255)
black = (0, 0, 0)
gray = (95, 95, 96)
orange = (249, 87, 0)
cobalt_blue = (3, 65, 174)
green_apple = (114, 203, 59)
cyber_yellow= (255, 213, 0)
beer = (255, 151, 28)
ryb_red = (255, 50, 19)
purple = (128, 0, 128)
# colors of Tetris blocks
block_colors = (cobalt_blue, blue, green_apple, purple, cyber_yellow, beer, ryb_red)
# shapes of Tetris blocks
shapes = ("i_block", "l_block", "j_block", "o_block", "s_block", "t_block", "z_block")
directions = ("vertical_1", "vertical_2", "horizontal_1", "horizontal_2")
background_img = pygame.image.load("resources/images/background_img.jpg")
instructions_img = pygame.image.load("resources/images/instructions_img.jpg")
icon_img = pygame.image.load("resources/images/icon.png")
pygame.display.set_icon(icon_img)
class Button:
def __init__(self, button_color, button_hover_over_color, x, y, width, height, text_size, text_color, text_hover_over_color = None, text_str=""):
self.button_color = button_color
self.button_hover_over_color = button_hover_over_color
self.x = x
self.y = y
self.width = width
self.height = height
self.text_size = text_size
self.text_color = text_color
if text_hover_over_color:
self.text_hover_over_color = text_hover_over_color
else:
self.text_hover_over_color = text_color
self.text_str = text_str
def blit(self, display_screen, outline_color=None):
if outline_color:
pygame.draw.rect(display_screen, outline_color, (self.x-3, self.y-3, self.width+6, self.height+6))
pygame.draw.rect(display_screen, self.button_color, (self.x, self.y, self.width, self.height))
if self.text_str != "":
font = pygame.font.Font("freesansbold.ttf", self.text_size)
text = font.render(self.text_str, True, self.text_color)
# to center the text in the middle of the button based on the size of the button
text_position = (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2))
display_screen.blit(text, text_position)
def is_hovered_over(self, mouse_position):
if self.x < mouse_position[0] < self.x+self.width and self.y < mouse_position[1] < self.y+self.height:
return True
return False
def blit_hovered_over(self, display_screen):
pygame.draw.rect(display_screen, self.button_hover_over_color, (self.x, self.y, self.width, self.height))
if self.text_str != "":
font = pygame.font.Font("freesansbold.ttf", self.text_size)
text = font.render(self.text_str, True, self.text_hover_over_color)
# to center the text in the middle of the button based on the size of the button
text_position = (self.x + (self.width/2 - text.get_width()/2), self.y + (self.height/2 - text.get_height()/2))
display_screen.blit(text, text_position)
def is_clicked(self, mouse_position, event):
if self.is_hovered_over(mouse_position):
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
return True
return False
class Tile:
def __init__(self, x, y, color = black):
self.x = x
self.y = y
self.color = color
self.empty = True
def draw_tile(self):
pygame.draw.rect(DISPLAY_SCREEN , self.color, (self.x, self.y, tile_length, tile_length) )
class PlayingField():
def __init__(self):
#y coordinate of first row = (80) off_set_y
self.tiles = {
"row1": {80: []},
"row2": {113: []},
"row3": {146: []},
"row4": {179: []},
"row5": {212: []},
"row6": {245: []},
"row7": {278: []},
"row8": {311: []},
"row9": {344: []},
"row10": {377: []},
"row11": {410: []},
"row12": {443: []},
"row13": {476: []},
"row14": {509: []},
"row15": {542: []},
"row16": {575: []},
"row17": {608: []},
"row18": {641: []},
"row19": {674: []},
"row20": {707: []},
}
self.__init_field()
def __init_field(self):
y = off_set_y
for i in range(20): #rows
x = off_set_x
for j in range(10): #cols
tile_to_add = Tile(x, y)
self.tiles["row"+str(i+1)][y].append(tile_to_add)
x += tile_length
y += tile_length
def destory_full_row(self, player):
times = 0
y = off_set_y
for i in range(20):
for tile in self.tiles["row"+str(i+1)][y]:
if tile.empty: break
elif tile.x == off_set_x+playing_field_width-tile_length:
times += 1
for j in range(800): #just for flashing the row
if j%2 == 0:
pygame.draw.rect(DISPLAY_SCREEN , black, (self.tiles["row"+str(i+1)][y][0].x+1, self.tiles["row"+str(i+1)][y][0].y+1, playing_field_width-2, tile_length-2) )
else:
for tile in self.tiles["row"+str(i+1)][y]:
pygame.draw.rect(DISPLAY_SCREEN , tile.color, (tile.x, tile.y, tile_length, tile_length) )
pygame.draw.line(DISPLAY_SCREEN , white, (off_set_x, y), (playing_field_width+off_set_x-1, y)) # horizontal line
pygame.display.update()
# let's destory this full row
self.destroy_and_replace(i+1, y)
player.score += 10*times
y += tile_length
def destroy_and_replace(self, row_number, row_y):
for i in range (row_number, 1, -1):
prev_row_number = i-1
prev_y = row_y-tile_length
self.tiles["row"+str(i)][row_y].clear() #current_row.clear()
temp_x = off_set_x
for j in range(10):
empty_tile = Tile(temp_x, row_y)
temp_x += tile_length
self.tiles["row"+str(i)][row_y].append(empty_tile)
if prev_y < 80:
break
for j in range(10):
old_tile = self.tiles["row"+str(i)][row_y][j]
new_tile = self.tiles["row"+str(prev_row_number)][prev_y][j]
old_tile.x = new_tile.x
old_tile.color = new_tile.color
old_tile.empty = new_tile.empty
row_y -= tile_length
class Block:
def __init__(self, shape:str, color = black):
self.shape = shape
self.color = color
self.direction = directions[0] #vertical_1
# tile1 , tile2 , tile3 , tile4
self.tiles = [ Tile(off_set_x+playing_field_width/2-tile_length, off_set_y, self.color), Tile(0, 0, color), Tile(0, 0, color), Tile(0, 0, color)]
self.__init_shape()
for tile in self.tiles:
tile.empty = False
def __init_shape(self):
if self.shape == "i_block":
self.tiles[1] = Tile(self.tiles[0].x, self.tiles[0].y-tile_length, self.color)
self.tiles[2] = Tile(self.tiles[0].x, self.tiles[1].y-tile_length, self.color)
self.tiles[3] = Tile(self.tiles[0].x, self.tiles[2].y-tile_length, self.color)
elif self.shape == "l_block":
self.tiles[1] = Tile(self.tiles[0].x+tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x-tile_length, self.tiles[0].y, self.color)
self.tiles[3] = Tile(self.tiles[2].x, self.tiles[2].y-tile_length, self.color)
elif self.shape == "j_block":
self.tiles[1] = Tile(self.tiles[0].x+tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x-tile_length, self.tiles[0].y, self.color)
self.tiles[3] = Tile(self.tiles[1].x, self.tiles[1].y-tile_length, self.color)
elif self.shape == "o_block":
self.tiles[1] = Tile(self.tiles[0].x+tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x, self.tiles[0].y-tile_length, self.color)
self.tiles[3] = Tile(self.tiles[1].x, self.tiles[1].y-tile_length, self.color)
elif self.shape == "s_block":
self.tiles[1] = Tile(self.tiles[0].x-tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x, self.tiles[0].y-tile_length, self.color)
self.tiles[3] = Tile(self.tiles[2].x+tile_length, self.tiles[2].y, self.color)
elif self.shape == "t_block":
self.tiles[1] = Tile(self.tiles[0].x+tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x-tile_length, self.tiles[0].y, self.color)
self.tiles[3] = Tile(self.tiles[0].x, self.tiles[0].y-tile_length, self.color)
elif self.shape == "z_block":
self.tiles[1] = Tile(self.tiles[0].x+tile_length, self.tiles[0].y, self.color)
self.tiles[2] = Tile(self.tiles[0].x, self.tiles[0].y-tile_length, self.color)
self.tiles[3] = Tile(self.tiles[2].x-tile_length, self.tiles[2].y, self.color)
else:
print("Error: wrong block name.")
pygame.quit()
sys.exit()
def complete_block(self):
self.__init_shape()
def can_fall(self, next_block, playing_field, player):
from tetris import manage_events, update_graphics
manage_events(self, next_block, playing_field, player)
#check borders
for block_tile in self.tiles:
if block_tile.y >= playing_field_height+off_set_y-tile_length:
return False
#check already existed tiles
for block_tile in self.tiles:
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.y+tile_length == tile.y and block_tile.x == tile.x:
return False
y += tile_length
return True
def block_is_falling(self, next_block, playing_field, player, faster=None):
from tetris import manage_events, update_graphics
manage_events(self,next_block, playing_field, player)
if self.can_fall(next_block, playing_field, player):
for tile in self.tiles:
tile.y += tile_length
manage_events(self, next_block, playing_field, player)
update_graphics(self, next_block, playing_field, player)
if faster:
clock.tick(40)
self.block_is_falling( next_block, playing_field, player)
else:
clock.tick(5)
manage_events(self, next_block, playing_field, player)
update_graphics(self, next_block, playing_field, player)
def get_new_block(self, next_block, playing_field, player):
if self.can_fall(next_block, playing_field, player): return (self, next_block, False)
#if the block has falled completely
for block_tile in self.tiles:
found = False
y = off_set_y
for i in range(20):
if not found:
for j in range(10):
if block_tile.x == playing_field.tiles["row"+str(i+1)][y][j].x and block_tile.y == playing_field.tiles["row"+str(i+1)][y][j].y:
playing_field.tiles["row"+str(i+1)][y][j].color = block_tile.color
playing_field.tiles["row"+str(i+1)][y][j].empty = False
found = True
break
y += tile_length
else:
break
new_block = next_block
next_rand_index1 = random.randint(0, 6)
next_rand_index2 = random.randint(0, 6)
new_next_block = Block(shapes[next_rand_index1], block_colors[next_rand_index2])
clock.tick(2)
return (new_block, new_next_block, True)
def move_left(self, playing_field):
if self.can_move_left(playing_field):
for tile in self.tiles:
tile.x -= tile_length
def move_right(self, playing_field):
if self.can_move_right(playing_field):
for tile in self.tiles:
tile.x += tile_length
def can_move_left(self, playing_field):
# whether inside the playing field or not
for tile in self.tiles:
if tile.x <= off_set_x:
return False
# whether adjacent field_tiles are occupied or not
for block_tile in self.tiles:
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x-tile_length == tile.x and block_tile.y == tile.y:
return False
y += tile_length
return True
def can_move_right(self, playing_field):
# whether inside the playing field or not
for tile in self.tiles:
if tile.x + tile_length >= off_set_x+playing_field_width:
return False
# whether adjacent field_tiles are occupied or not
for block_tile in self.tiles:
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x+tile_length == tile.x and block_tile.y == tile.y:
return False
y += tile_length
return True
def rotate(self, next_block, playing_field, player):
from tetris import manage_events, update_graphics
manage_events(self, next_block, playing_field, player)
if self.shape == "i_block":
self.rotate_i_block(playing_field)
elif self.shape == "l_block":
self.rotate_l_block(playing_field)
elif self.shape == "j_block":
self.rotate_j_block(playing_field)
elif self.shape == "o_block":
return
#no rotation for o_block.
elif self.shape == "s_block":
self.rotate_s_block(playing_field)
elif self.shape == "t_block":
self.rotate_t_block(playing_field)
elif self.shape == "z_block":
self.rotate_z_block(playing_field)
else:
print("Error: wrong block name.")
pygame.quit()
sys.exit()
manage_events(self, next_block, playing_field, player)
update_graphics(self, next_block, playing_field, player)
def rotate_i_block(self, playing_field): #done
temp_rotated_i = Block("i_block", self.color)
temp_rotated_i.tiles = self.tiles.copy()
if self.direction == directions[0] or self.direction == directions[1]:
# ----
temp_rotated_i.tiles[0] = Tile(temp_rotated_i.tiles[1].x, temp_rotated_i.tiles[0].y, temp_rotated_i.color)
temp_rotated_i.tiles[1] = Tile(temp_rotated_i.tiles[0].x-tile_length, temp_rotated_i.tiles[0].y, temp_rotated_i.color)
temp_rotated_i.tiles[2] = Tile(temp_rotated_i.tiles[0].x+tile_length, temp_rotated_i.tiles[0].y, temp_rotated_i.color)
temp_rotated_i.tiles[3] = Tile(temp_rotated_i.tiles[2].x+tile_length, temp_rotated_i.tiles[0].y, temp_rotated_i.color)
temp_rotated_i.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2] or self.direction == directions[3]:
# |
# |
# |
# |
temp_rotated_i.tiles[1] = Tile(temp_rotated_i.tiles[0].x, temp_rotated_i.tiles[0].y-tile_length, temp_rotated_i.color)
temp_rotated_i.tiles[2] = Tile(temp_rotated_i.tiles[1].x, temp_rotated_i.tiles[1].y-tile_length, temp_rotated_i.color)
temp_rotated_i.tiles[3] = Tile(temp_rotated_i.tiles[2].x, temp_rotated_i.tiles[2].y-tile_length, temp_rotated_i.color)
temp_rotated_i.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_i.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_i.direction
self.tiles = temp_rotated_i.tiles
def rotate_l_block(self, playing_field): #done
temp_rotated_l = Block("l_block", self.color)
temp_rotated_l.tiles = self.tiles.copy()
if self.direction == directions[0]:
# after rotating, the block should look like this ↓
# _
# |
# |
# |
temp_rotated_l.tiles[0] = Tile(temp_rotated_l.tiles[0].x, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[1] = Tile(temp_rotated_l.tiles[0].x, temp_rotated_l.tiles[0].y-tile_length, temp_rotated_l.color)
temp_rotated_l.tiles[2] = Tile(temp_rotated_l.tiles[1].x, temp_rotated_l.tiles[1].y-tile_length, temp_rotated_l.color)
temp_rotated_l.tiles[3] = Tile(temp_rotated_l.tiles[2].x+tile_length, temp_rotated_l.tiles[2].y, temp_rotated_l.color)
temp_rotated_l.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2]:
# after rotating, the block should look like this ↓
# ---
# |
temp_rotated_l.tiles[0] = Tile(temp_rotated_l.tiles[3].x, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[1] = Tile(temp_rotated_l.tiles[0].x, temp_rotated_l.tiles[0].y-tile_length, temp_rotated_l.color)
temp_rotated_l.tiles[2] = Tile(temp_rotated_l.tiles[1].x-tile_length, temp_rotated_l.tiles[1].y, temp_rotated_l.color)
temp_rotated_l.tiles[3] = Tile(temp_rotated_l.tiles[2].x-tile_length, temp_rotated_l.tiles[2].y, temp_rotated_l.color)
temp_rotated_l.direction = directions[1] #"vertical_2"
elif self.direction == directions[1]:
# after rotating, the block should look like this ↓
# |
# |
# _|
temp_rotated_l.tiles[0] = Tile(temp_rotated_l.tiles[3].x, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[1] = Tile(temp_rotated_l.tiles[0].x+tile_length, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[2] = Tile(temp_rotated_l.tiles[1].x, temp_rotated_l.tiles[1].y-tile_length, temp_rotated_l.color)
temp_rotated_l.tiles[3] = Tile(temp_rotated_l.tiles[2].x, temp_rotated_l.tiles[2].y-tile_length, temp_rotated_l.color)
temp_rotated_l.direction = directions[3] #"horizontal_2"
elif self.direction == directions[3]:
# after rotating, the block should look like this ↓
# |
# ---
temp_rotated_l.tiles[0] = Tile(temp_rotated_l.tiles[1].x, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[1] = Tile(temp_rotated_l.tiles[0].x+tile_length, temp_rotated_l.tiles[0].y, temp_rotated_l.color)
temp_rotated_l.tiles[2] = Tile(temp_rotated_l.tiles[0].x-tile_length, temp_rotated_l.tiles[1].y, temp_rotated_l.color)
temp_rotated_l.tiles[3] = Tile(temp_rotated_l.tiles[2].x, temp_rotated_l.tiles[2].y-tile_length, temp_rotated_l.color)
temp_rotated_l.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_l.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_l.direction
self.tiles = temp_rotated_l.tiles
def rotate_j_block(self, playing_field): #done
temp_rotated_j = Block("j_block", self.color)
temp_rotated_j.tiles = self.tiles.copy()
if self.direction == directions[0]:
temp_rotated_j.tiles[0] = Tile(temp_rotated_j.tiles[1].x, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[1] = Tile(temp_rotated_j.tiles[0].x-tile_length, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[2] = Tile(temp_rotated_j.tiles[1].x, temp_rotated_j.tiles[1].y-tile_length, temp_rotated_j.color)
temp_rotated_j.tiles[3] = Tile(temp_rotated_j.tiles[2].x, temp_rotated_j.tiles[2].y-tile_length, temp_rotated_j.color)
temp_rotated_j.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2]:
temp_rotated_j.tiles[0] = Tile(temp_rotated_j.tiles[1].x-tile_length, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[1] = Tile(temp_rotated_j.tiles[0].x, temp_rotated_j.tiles[0].y-tile_length, temp_rotated_j.color)
temp_rotated_j.tiles[2] = Tile(temp_rotated_j.tiles[1].x+tile_length, temp_rotated_j.tiles[1].y, temp_rotated_j.color)
temp_rotated_j.tiles[3] = Tile(temp_rotated_j.tiles[2].x+tile_length, temp_rotated_j.tiles[2].y, temp_rotated_j.color)
temp_rotated_j.direction = directions[1] #"vertical_2"
elif self.direction == directions[1]:
temp_rotated_j.tiles[0] = Tile(temp_rotated_j.tiles[2].x, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[1] = Tile(temp_rotated_j.tiles[0].x, temp_rotated_j.tiles[0].y-tile_length, temp_rotated_j.color)
temp_rotated_j.tiles[2] = Tile(temp_rotated_j.tiles[1].x, temp_rotated_j.tiles[1].y-tile_length, temp_rotated_j.color)
temp_rotated_j.tiles[3] = Tile(temp_rotated_j.tiles[2].x-tile_length, temp_rotated_j.tiles[2].y, temp_rotated_j.color)
temp_rotated_j.direction = directions[3] #"horizontal_2"
elif self.direction == directions[3]: #back to normal:
temp_rotated_j.tiles[0] = Tile(temp_rotated_j.tiles[0].x, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[1] = Tile(temp_rotated_j.tiles[0].x+tile_length, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[2] = Tile(temp_rotated_j.tiles[0].x-tile_length, temp_rotated_j.tiles[0].y, temp_rotated_j.color)
temp_rotated_j.tiles[3] = Tile(temp_rotated_j.tiles[1].x, temp_rotated_j.tiles[1].y-tile_length, temp_rotated_j.color)
temp_rotated_j.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_j.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_j.direction
self.tiles = temp_rotated_j.tiles
def rotate_s_block(self, playing_field): #done
temp_rotated_s = Block("s_block", self.color)
temp_rotated_s.tiles = self.tiles.copy()
if self.direction == directions[0] or self.direction == directions[1]:
temp_rotated_s.tiles[0] = Tile(temp_rotated_s.tiles[3].x, temp_rotated_s.tiles[0].y, temp_rotated_s.color)
temp_rotated_s.tiles[1] = Tile(temp_rotated_s.tiles[0].x, temp_rotated_s.tiles[0].y-tile_length, temp_rotated_s.color)
temp_rotated_s.tiles[2] = Tile(temp_rotated_s.tiles[1].x-tile_length, temp_rotated_s.tiles[1].y, temp_rotated_s.color)
temp_rotated_s.tiles[3] = Tile(temp_rotated_s.tiles[2].x, temp_rotated_s.tiles[2].y-tile_length, temp_rotated_s.color)
temp_rotated_s.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2] or self.direction == directions[3]
temp_rotated_s.tiles[0] = Tile(temp_rotated_s.tiles[2].x, temp_rotated_s.tiles[0].y, temp_rotated_s.color)
temp_rotated_s.tiles[1] = Tile(temp_rotated_s.tiles[0].x-tile_length, temp_rotated_s.tiles[0].y, temp_rotated_s.color)
temp_rotated_s.tiles[2] = Tile(temp_rotated_s.tiles[0].x, temp_rotated_s.tiles[0].y-tile_length, temp_rotated_s.color)
temp_rotated_s.tiles[3] = Tile(temp_rotated_s.tiles[2].x+tile_length, temp_rotated_s.tiles[2].y, temp_rotated_s.color)
temp_rotated_s.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_s.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_s.direction
self.tiles = temp_rotated_s.tiles
def rotate_t_block(self, playing_field): #done
temp_rotated_t = Block("j_block", self.color)
temp_rotated_t.tiles = self.tiles.copy()
if self.direction == directions[0]:
temp_rotated_t.tiles[0] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[1] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y-tile_length, temp_rotated_t.color)
temp_rotated_t.tiles[2] = Tile(temp_rotated_t.tiles[1].x, temp_rotated_t.tiles[1].y-tile_length, temp_rotated_t.color)
temp_rotated_t.tiles[3] = Tile(temp_rotated_t.tiles[1].x+tile_length, temp_rotated_t.tiles[1].y, temp_rotated_t.color)
temp_rotated_t.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2]:
temp_rotated_t.tiles[0] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[1] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y-tile_length, temp_rotated_t.color)
temp_rotated_t.tiles[2] = Tile(temp_rotated_t.tiles[1].x-tile_length, temp_rotated_t.tiles[1].y, temp_rotated_t.color)
temp_rotated_t.tiles[3] = Tile(temp_rotated_t.tiles[1].x+tile_length, temp_rotated_t.tiles[2].y, temp_rotated_t.color)
temp_rotated_t.direction = directions[1] #"vertical_2"
elif self.direction == directions[1]:
temp_rotated_t.tiles[0] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[1] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y-tile_length, temp_rotated_t.color)
temp_rotated_t.tiles[2] = Tile(temp_rotated_t.tiles[1].x, temp_rotated_t.tiles[1].y-tile_length, temp_rotated_t.color)
temp_rotated_t.tiles[3] = Tile(temp_rotated_t.tiles[1].x-tile_length, temp_rotated_t.tiles[1].y, temp_rotated_t.color)
temp_rotated_t.direction = directions[3] #"horizontal_2"
elif self.direction == directions[3]: #back to normal:
temp_rotated_t.tiles[0] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[1] = Tile(temp_rotated_t.tiles[0].x+tile_length, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[2] = Tile(temp_rotated_t.tiles[0].x-tile_length, temp_rotated_t.tiles[0].y, temp_rotated_t.color)
temp_rotated_t.tiles[3] = Tile(temp_rotated_t.tiles[0].x, temp_rotated_t.tiles[0].y-tile_length, temp_rotated_t.color)
temp_rotated_t.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_t.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_t.direction
self.tiles = temp_rotated_t.tiles
def rotate_z_block(self, playing_field): #done
temp_rotated_z = Block("z_block", self.color)
temp_rotated_z.tiles = self.tiles.copy()
if self.direction == directions[0] or self.direction == directions[1]:
temp_rotated_z.tiles[0] = Tile(temp_rotated_z.tiles[3].x, temp_rotated_z.tiles[0].y, temp_rotated_z.color)
temp_rotated_z.tiles[1] = Tile(temp_rotated_z.tiles[0].x, temp_rotated_z.tiles[0].y-tile_length, temp_rotated_z.color)
temp_rotated_z.tiles[2] = Tile(temp_rotated_z.tiles[1].x+tile_length, temp_rotated_z.tiles[1].y, temp_rotated_z.color)
temp_rotated_z.tiles[3] = Tile(temp_rotated_z.tiles[2].x, temp_rotated_z.tiles[2].y-tile_length, temp_rotated_z.color)
temp_rotated_z.direction = directions[2] # "horizontal_1"
elif self.direction == directions[2] or self.direction == directions[3]:
temp_rotated_z.tiles[0] = Tile(temp_rotated_z.tiles[3].x, temp_rotated_z.tiles[0].y, temp_rotated_z.color)
temp_rotated_z.tiles[1] = Tile(temp_rotated_z.tiles[0].x+tile_length, temp_rotated_z.tiles[0].y, temp_rotated_z.color)
temp_rotated_z.tiles[2] = Tile(temp_rotated_z.tiles[0].x, temp_rotated_z.tiles[0].y-tile_length, temp_rotated_z.color)
temp_rotated_z.tiles[3] = Tile(temp_rotated_z.tiles[2].x-tile_length, temp_rotated_z.tiles[2].y, temp_rotated_z.color)
temp_rotated_z.direction = directions[0] #"vertical_1"
for block_tile in temp_rotated_z.tiles:
if block_tile.x <= off_set_x or block_tile.x >= playing_field_width:
return
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.x == tile.x and block_tile.y == tile.y:
return
y += tile_length
self.direction = temp_rotated_z.direction
self.tiles = temp_rotated_z.tiles
def fall_completely(self, next_block, playing_field, player):
from tetris import update_graphics
fall= True
while fall:
for block_tile in self.tiles:
if block_tile.y >= playing_field_height+off_set_y-tile_length:
fall = False
break
#check already existed tiles
for block_tile in self.tiles:
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and block_tile.y+tile_length == tile.y and block_tile.x == tile.x:
fall = False
break
y += tile_length
if not fall:
break
for tile in self.tiles:
tile.y += tile_length
update_graphics(self, next_block, playing_field, player)
clock.get_rawtime()
clock.tick(50)
class Player:
def __init__(self, start_time):
self.start_time = start_time
self.time_since_start = 0
self.score = 0 tetris.py
#import libraries
import pygame
from util import *
def update_graphics(block, next_block, playing_field, player):
#Sets black background and text
DISPLAY_SCREEN.blit(background_img, (0, 0))
pygame.draw.rect(DISPLAY_SCREEN , black, (off_set_x, off_set_y, playing_field_width, playing_field_height) )
font = pygame.font.SysFont("comicsansms", 48)
rendered_text = font.render("Tetris", 1, orange)
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, 10))
#Displays Current score and time
player.time_since_start = pygame.time.get_ticks() - player.start_time
font = pygame.font.SysFont("comicsansms", 20)
rendered_text_time = font.render("Time: " + str(player.time_since_start), 1, orange)
DISPLAY_SCREEN.blit(rendered_text_time, (playing_field_width+tile_length*2, playing_field_height-80))
rendered_text_score = font.render("Score: " + str(player.score), 1, orange)
DISPLAY_SCREEN.blit(rendered_text_score, (playing_field_width+tile_length*2, playing_field_height-50))
#Draw the small screen for the next block
draw_small_screen(next_block)
#Set tiles
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
tile.draw_tile()
y += tile_length
#Blocks while falling
for tile in block.tiles:
if tile.y >= off_set_y:
tile.draw_tile()
#Sets borders
pygame.draw.line(DISPLAY_SCREEN , blue, (off_set_x-2, off_set_y-3), (playing_field_width+off_set_x+1, off_set_y-3), 4) # horizontal line top
pygame.draw.line(DISPLAY_SCREEN , blue, (off_set_x-2, off_set_y+playing_field_height+1), (playing_field_width+off_set_x+1, off_set_y+playing_field_height+1), 4) # horizontal line bottom
pygame.draw.line(DISPLAY_SCREEN , blue, (off_set_x-3, off_set_y-3), (off_set_x-3, off_set_y+playing_field_height+1), 4) # vertical line left
pygame.draw.line(DISPLAY_SCREEN , blue, (playing_field_width+off_set_x+1, off_set_y-3), (playing_field_width+off_set_x+1, off_set_y+playing_field_height+1), 4) # vertical line right
#Sets Grid
current_y_horizontal_lines = off_set_y
current_x_vertical_lines = off_set_x
for i in range(19):
current_y_horizontal_lines += 33
pygame.draw.line(DISPLAY_SCREEN , white, (off_set_x, current_y_horizontal_lines), (playing_field_width+off_set_x-1, current_y_horizontal_lines)) # horizontal line top
for j in range(9):
current_x_vertical_lines += 33
pygame.draw.line(DISPLAY_SCREEN , white, (current_x_vertical_lines-1, off_set_y), (current_x_vertical_lines-1, playing_field_height+off_set_y)) # horizontal line top
pygame.display.update()
def draw_small_screen(next_block):
#Sets background
pygame.draw.rect(DISPLAY_SCREEN , black, (playing_field_width+tile_length*2, height/2-20, 6*tile_length, 6*tile_length) )
#Sets borders
pygame.draw.line(DISPLAY_SCREEN , blue, (playing_field_width+tile_length*2-2, height/2-20-2), ((6*tile_length)+(playing_field_width+tile_length*2), (height/2-20-2)), 3) # horizontal line top
pygame.draw.line(DISPLAY_SCREEN , blue, (playing_field_width+tile_length*2-2, height/2-20+(6*tile_length)), ((6*tile_length)+(playing_field_width+tile_length*2), height/2-20+(6*tile_length)), 3) # horizontal line bottom
pygame.draw.line(DISPLAY_SCREEN , blue, (playing_field_width+tile_length*2-2, height/2-20-2), (playing_field_width+tile_length*2-2, height/2-20+(6*tile_length)), 3) # vertical line left
pygame.draw.line(DISPLAY_SCREEN , blue, ((6*tile_length)+(playing_field_width+tile_length*2), height/2-20-2), ((6*tile_length)+(playing_field_width+tile_length*2), height/2-20+(6*tile_length)), 3) # vertical line right
#Sets text
font = pygame.font.SysFont("comicsansms", 30)
rendered_text = font.render("Next Block", 1, orange)
DISPLAY_SCREEN.blit(rendered_text, (playing_field_width+tile_length*2, height/2-70))
#Displays next block
temp_block = Block(next_block.shape, next_block.color)
temp_block.tiles = [Tile(playing_field_width+tile_length*2+2*tile_length, height/2-20+4*tile_length, next_block.color), Tile(0, 0, next_block.color), Tile(0, 0, next_block.color), Tile(0, 0, next_block.color)]
temp_block.complete_block()
for tile in temp_block.tiles:
tile.draw_tile()
def is_game_over(playing_field, player):
y = off_set_y
for i in range(20):
for tile in playing_field.tiles["row"+str(i+1)][y]:
if not tile.empty and tile.y <= off_set_y:
temp_y = off_set_y
for j in range(20):
for tile in playing_field.tiles["row"+str(j+1)][temp_y]:
tile.draw_tile()
temp_y += tile_length
font = pygame.font.SysFont("comicsansms", 48)
rendered_text = font.render("GAME OVER", 1, white)
DISPLAY_SCREEN.blit(rendered_text, (off_set_x+20, playing_field_height/2))
pygame.display.update()
time.sleep(2)
introduction(player)
y += tile_length
def start_game():
global best_score
global longest_time
rand_index = random.randint(0, 6)
block = Block(shapes[rand_index], block_colors[rand_index])
next_rand_index = random.randint(0, 6)
next_block = Block(shapes[next_rand_index], block_colors[next_rand_index])
playing_field = PlayingField()
start_time = pygame.time.get_ticks()
player = Player(start_time)
while True:
update_graphics(block, next_block, playing_field, player)
(block, next_block, is_new) = block.get_new_block(next_block, playing_field, player)
if is_new:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.event.clear()
manage_events(block, next_block, playing_field, player)
update_graphics(block, next_block, playing_field, player)
block.block_is_falling(next_block, playing_field, player)
update_graphics(block, next_block, playing_field, player)
playing_field.destory_full_row(player)
update_graphics(block, next_block, playing_field, player)
if player.score > best_score:
best_score = player.score
if player.time_since_start > longest_time:
longest_time = player.time_since_start
is_game_over(playing_field, player)
update_graphics(block, next_block, playing_field, player)
pygame.display.update()
clock.tick(60)
def manage_events(block, next_block, playing_field, player):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#move the block to the left
block.move_left(playing_field)
elif event.key == pygame.K_RIGHT:
#move the block to the right
block.move_right(playing_field)
elif event.key == pygame.K_UP:
# rotate block
block.rotate(next_block, playing_field, player)
if event.key == pygame.K_SPACE:
# let the block fall completely
block.fall_completely(next_block, playing_field, player)
if event.key == pygame.K_DOWN:
# let the block fall down faster
block.block_is_falling(next_block, playing_field, player, "faster")
update_graphics(block, next_block, playing_field, player)
def introduction(player = None):
button_width = 300
button_height = 90
#start_x_button = width/2-button_width/2
play_button = Button(blue, orange, -400, height/2, button_width, button_height, 32, black, white, "PLAY")
instructions_button = Button(blue, orange, width+150, height/2+button_height+10, button_width,button_height, 32, black, white, "INSTRUCTIONS")
quit_button = Button(blue, orange, -400, height/2+button_height*2+20, button_width,button_height, 32, black, white, "QUIT")
font = pygame.font.SysFont("comicsansms", 48)
rendered_text = font.render("Tetris", 1, black)
rendered_text_y = height
#To draw the Tetris text in an animated way
while rendered_text_y > 10:
DISPLAY_SCREEN.blit(background_img, (0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
rendered_text_y -= 1.5
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, rendered_text_y))
pygame.display.update()
#To draw the score and time texts in an animated way
if player:
font_small = pygame.font.SysFont("comicsansms", 30)
rendered_current_score = font_small.render("Current Score: " + str(player.score), 1, orange)
rendered_best_score = font_small.render("Best Score: " + str(best_score), 1, orange)
rendered_current_time = font_small.render("Current Time: " + str(player.time_since_start), 1, orange)
rendered_longest_time = font_small.render("Longest Time: " + str(longest_time), 1, orange)
rendered_current_score_y = height
rendered_best_score_y = height+40
rendered_current_time_y = height+80
rendered_longest_time_y = height+120
while rendered_current_score_y > 150:
DISPLAY_SCREEN.blit(background_img, (0, 0))
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, rendered_text_y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
rendered_current_score_y -= 1.5
rendered_best_score_y -= 1.5
rendered_current_time_y -= 1.5
rendered_longest_time_y -= 1.5
DISPLAY_SCREEN.blit(rendered_current_score, (off_set_x, rendered_current_score_y))
DISPLAY_SCREEN.blit(rendered_best_score, (off_set_x+45, rendered_best_score_y))
DISPLAY_SCREEN.blit(rendered_current_time, (off_set_x+15, rendered_current_time_y))
DISPLAY_SCREEN.blit(rendered_longest_time, (off_set_x+15, rendered_longest_time_y))
pygame.display.update()
#To draw the buttons in an animated way
while play_button.x < width/2-button_width/2 or instructions_button.x > width/2-button_width/2:
DISPLAY_SCREEN.blit(background_img, (0, 0))
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, rendered_text_y))
if player:
DISPLAY_SCREEN.blit(rendered_current_score, (off_set_x, rendered_current_score_y))
DISPLAY_SCREEN.blit(rendered_best_score, (off_set_x+45, rendered_best_score_y))
DISPLAY_SCREEN.blit(rendered_current_time, (off_set_x+15, rendered_current_time_y))
DISPLAY_SCREEN.blit(rendered_longest_time, (off_set_x+15, rendered_longest_time_y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if play_button.x < width/2-button_width/2:
play_button.x += 3
quit_button.x += 3
if instructions_button.x > width/2-button_width/2 :
instructions_button.x -= 3
play_button.blit(DISPLAY_SCREEN)
instructions_button.blit(DISPLAY_SCREEN)
quit_button.blit(DISPLAY_SCREEN)
pygame.display.update()
run = True
while run:
DISPLAY_SCREEN.blit(background_img, (0, 0))
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, rendered_text_y))
if player:
DISPLAY_SCREEN.blit(rendered_current_score, (off_set_x, rendered_current_score_y))
DISPLAY_SCREEN.blit(rendered_best_score, (off_set_x+45, rendered_best_score_y))
DISPLAY_SCREEN.blit(rendered_current_time, (off_set_x+15, rendered_current_time_y))
DISPLAY_SCREEN.blit(rendered_longest_time, (off_set_x+15, rendered_longest_time_y))
# Get the position of the mouse
mouse_position = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if play_button.is_clicked(mouse_position, event):
start_game()
run = False
elif instructions_button.is_clicked(mouse_position, event):
instructions(player)
run = False
elif quit_button.is_clicked(mouse_position, event):
pygame.quit()
sys.exit()
if play_button.is_hovered_over(mouse_position):
play_button.blit_hovered_over(DISPLAY_SCREEN)
else:
play_button.blit(DISPLAY_SCREEN, gray)
if instructions_button.is_hovered_over(mouse_position):
instructions_button.blit_hovered_over(DISPLAY_SCREEN)
else:
instructions_button.blit(DISPLAY_SCREEN, gray)
if quit_button.is_hovered_over(mouse_position):
quit_button.blit_hovered_over(DISPLAY_SCREEN)
else:
quit_button.blit(DISPLAY_SCREEN, gray)
clock.tick(60)
pygame.display.update()
def instructions(player = None):
button_width = 150
button_height = 60
play_button = Button(blue, orange, width-150-10, height-80, button_width, button_height, 32, black, white, "PLAY >>")
back_button = Button(blue, orange, 10, height-80, button_width, button_height, 32, black, white, "<< BACK")
run = True
while run:
DISPLAY_SCREEN.blit(instructions_img, (0, 0))
font = pygame.font.SysFont("comicsansms", 48)
rendered_text = font.render("Tetris", 1, orange)
DISPLAY_SCREEN.blit(rendered_text, (width/2-80, 10))
# Get the position of the mouse
mouse_position = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if play_button.is_clicked(mouse_position, event):
start_game()
run = False
elif back_button.is_clicked(mouse_position, event):
introduction(player)
run = False
instructions_label = "Instructions"
font = pygame.font.SysFont("comicsansms", 40)
rendered_text = font.render(instructions_label, 1, orange)
DISPLAY_SCREEN.blit(rendered_text, (width/2 - rendered_text.get_width()/2, 100))
instructions1 = " Move Right: right arrow >"
instructions2 = " Move Left: left arrow <"
instructions3 = " Rotate: up arrow ^"
instructions4 = " Soft Drop: down arrow"
instructions5 = " Hard Drop: space"
font = pygame.font.SysFont("comicsansms", 20)
rendered_text1 = font.render(instructions1, 1, orange)
rendered_text2 = font.render(instructions2, 1, orange)
rendered_text3 = font.render(instructions3, 1, orange)
rendered_text4 = font.render(instructions4, 1, orange)
rendered_text5 = font.render(instructions5, 1, orange)
DISPLAY_SCREEN.blit(rendered_text1, (20, 200))
DISPLAY_SCREEN.blit(rendered_text2, (20, 240))
DISPLAY_SCREEN.blit(rendered_text3, (20, 280))
DISPLAY_SCREEN.blit(rendered_text4, (20, 320))
DISPLAY_SCREEN.blit(rendered_text5, (20, 360))
if play_button.is_hovered_over(mouse_position):
play_button.blit_hovered_over(DISPLAY_SCREEN)
else:
play_button.blit(DISPLAY_SCREEN, gray)
if back_button.is_hovered_over(mouse_position):
back_button.blit_hovered_over(DISPLAY_SCREEN)
else:
back_button.blit(DISPLAY_SCREEN, gray)
clock.tick(60)
pygame.display.update()
if __name__ == "__main__":
introduction()
Output


Also Read:
- Create your own ChatGPT with Python
- Bakery Management System in Python | Class 12 Project
- SQLite | CRUD Operations in Python
- Event Management System Project in Python
- Ticket Booking and Management in Python
- Hostel Management System Project in Python
- Sales Management System Project in Python
- Bank Management System Project in C++
- Python Download File from URL | 4 Methods
- Python Programming Examples | Fundamental Programs in Python
- Spell Checker in Python
- Portfolio Management System in Python
- Stickman Game in Python
- Contact Book project in Python
- Loan Management System Project in Python
- Cab Booking System in Python
- Brick Breaker Game in Python
- Tank game in Python
- GUI Piano in Python
- Ludo Game in Python
- Rock Paper Scissors Game in Python
- Snake and Ladder Game in Python
- Puzzle Game in Python
- Medical Store Management System Project in Python
- Creating Dino Game in Python
- Tic Tac Toe Game in Python
- Test Typing Speed using Python App
- MoviePy: Python Video Editing Library
- Scientific Calculator in Python
- GUI To-Do List App in Python Tkinter



