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 pygame
That’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