Step up your coding game with AI-powered Code Explainer. Get insights like never before!
Tetris is a classic puzzle game where the objective is to arrange falling geometric pieces, called tetrominoes, to fill horizontal lines without leaving gaps. When a line is completely filled, it is cleared, and the player earns points. The game becomes progressively more challenging as the tetrominoes fall faster, making it harder to arrange them in time.
In this tutorial, we will walk you through the process of creating a simple Tetris game step by step using PyGame. We will cover the following topics:
By the end of this tutorial, you will have a fully functional Tetris game that you can play and customize. This project will also help you understand the basics of game development with Python and Pygame and serve as a foundation for creating other types of games. Here's how it'll look like:
Table of content:
Tetromino
and Tetris
Classes to Manage Game Logic
Once you have Python installed, open your terminal or command prompt and enter the following command to install the Pygame library:
$ pip install pygame
Now that our development environment is ready, let's start by creating the game window and defining the tetromino shapes.
First, we need to import the necessary modules for our Tetris game. Add the following lines to your Python script:
import sys
import pygame
import random
To use the Pygame library, we need to initialize it. Add the following line after the import statements:
pygame.init()
Next, we need to define some constants for our game, such as screen dimensions, grid size, colors, and the shapes of the tetrominoes. Add the following lines to your script:
# Screen dimensions
WIDTH, HEIGHT = 800, 600
GRID_SIZE = 25
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
COLORS = [RED, BLUE, GREEN]
# Tetromino shapes
SHAPES = [
[
['.....',
'.....',
'.....',
'OOOO.',
'.....'],
['.....',
'..O..',
'..O..',
'..O..',
'..O..']
],
[
['.....',
'.....',
'..O..',
'.OOO.',
'.....'],
['.....',
'..O..',
'.OO..',
'..O..',
'.....'],
['.....',
'.....',
'.OOO.',
'..O..',
'.....'],
['.....',
'..O..',
'..OO.',
'..O..',
'.....']
],
[
[
'.....',
'.....',
'..OO.',
'.OO..',
'.....'],
['.....',
'.....',
'.OO..',
'..OO.',
'.....'],
['.....',
'.O...',
'.OO..',
'..O..',
'.....'],
['.....',
'..O..',
'.OO..',
'.O...',
'.....']
],
[
['.....',
'..O..',
'..O.',
'..OO.',
'.....'],
['.....',
'...O.',
'.OOO.',
'.....',
'.....'],
['.....',
'.OO..',
'..O..',
'..O..',
'.....'],
['.....',
'.....',
'.OOO.',
'.O...',
'.....']
],
]
Here, we define the width and height of the game window, the grid size for the tetrominoes, and the colors we will use for the game (white, black, and red). The SHAPES
list contains the tetromino shapes represented as strings, you're free to add/edit as you wish. You can also play around with the colors and grid size.
Tetromino
and Tetris
Classes to Manage Game LogicNow that we have our game window and tetromino shapes defined, let's create two classes to manage the game logic: the Tetromino class and the Tetris class.
Tetromino
ClassThe Tetromino
class will represent a single tetromino piece, with properties for its position, shape
, color
, and rotation
. Add the following code to your script:
class Tetromino:
def __init__(self, x, y, shape):
self.x = x
self.y = y
self.shape = shape
self.color = random.choice(COLORS) # You can choose different colors for each shape
self.rotation = 0
Tetris
ClassThe Tetris
class will handle the main game logic. It will have methods for creating a new piece, checking if a move is valid, clearing lines, locking a piece, updating the game state, and drawing the game. Add the following code to your script:
class Tetris:
def __init__(self, width, height):
self.width = width
self.height = height
self.grid = [[0 for _ in range(width)] for _ in range(height)]
self.current_piece = self.new_piece()
self.game_over = False
self.score = 0 # Add score attribute
Let's make a method for making a new Tetromino
piece:
def new_piece(self):
# Choose a random shape
shape = random.choice(SHAPES)
# Return a new Tetromino object
return Tetromino(self.width // 2, 0, shape)
Next, the valid_move()
method checks whether a piece can move to the given position:
def valid_move(self, piece, x, y, rotation):
"""Check if the piece can move to the given position"""
for i, row in enumerate(piece.shape[(piece.rotation + rotation) % len(piece.shape)]):
for j, cell in enumerate(row):
try:
if cell == 'O' and (self.grid[piece.y + i + y][piece.x + j + x] != 0):
return False
except IndexError:
return False
return True
The clear_lines()
method clears the lines that are full and returns the number of cleared lines so we can calculate the score later:
def clear_lines(self):
"""Clear the lines that are full and return the number of cleared lines"""
lines_cleared = 0
for i, row in enumerate(self.grid[:-1]):
if all(cell != 0 for cell in row):
lines_cleared += 1
del self.grid[i]
self.grid.insert(0, [0 for _ in range(self.width)])
return lines_cleared
Now the method for locking the pieces:
def lock_piece(self, piece):
"""Lock the piece in place and create a new piece"""
for i, row in enumerate(piece.shape[piece.rotation % len(piece.shape)]):
for j, cell in enumerate(row):
if cell == 'O':
self.grid[piece.y + i][piece.x + j] = piece.color
# Clear the lines and update the score
lines_cleared = self.clear_lines()
self.score += lines_cleared * 100 # Update the score based on the number of cleared lines
# Create a new piece
self.current_piece = self.new_piece()
# Check if the game is over
if not self.valid_move(self.current_piece, 0, 0, 0):
self.game_over = True
return lines_cleared
The update()
function to move the tetromino one cell down:
def update(self):
"""Move the tetromino down one cell"""
if not self.game_over:
if self.valid_move(self.current_piece, 0, 1, 0):
self.current_piece.y += 1
else:
self.lock_piece(self.current_piece)
Finally, we make the method for drawing the game grid and the current piece:
def draw(self, screen):
"""Draw the grid and the current piece"""
for y, row in enumerate(self.grid):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, cell, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
if self.current_piece:
for i, row in enumerate(self.current_piece.shape[self.current_piece.rotation % len(self.current_piece.shape)]):
for j, cell in enumerate(row):
if cell == 'O':
pygame.draw.rect(screen, self.current_piece.color, ((self.current_piece.x + j) * GRID_SIZE, (self.current_piece.y + i) * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
Now that we've made the Tetris
class, let's make two helper functions that draw text, one for the score, and another for the "Game Over!":
def draw_score(screen, score, x, y):
"""Draw the score on the screen"""
font = pygame.font.Font(None, 36)
text = font.render(f"Score: {score}", True, WHITE)
screen.blit(text, (x, y))
def draw_game_over(screen, x, y):
"""Draw the game over text on the screen"""
font = pygame.font.Font(None, 48)
text = font.render("Game Over", True, RED)
screen.blit(text, (x, y))
These functions take four arguments (three in the case of draw_game_over()
):
screen
: the Pygame surface to draw on.score
: the current game score.x
: the x-coordinate of the position where the score will be displayed.y
: the y-coordinate of the position where the score will be displayed.The functions create a font object using pygame.font.Font(None, 36)
, render the score text with the font.render()
method, and then use screen.blit()
to display the text on the screen.
Before running the loop, we start by initializing Pygame, creating the clock object, and creating the game screen and the game, the Tetris
object:
def main():
# Initialize pygame
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Tetris')
# Create a clock object
clock = pygame.time.Clock()
# Create a Tetris object
game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)
fall_time = 0
fall_speed = 50 # You can adjust this value to change the falling speed, it's in milliseconds
You can adjust the fall_speed
to lower it (make it faster), or increase it to make it slower.
Here's the game loop:
while True:
# Fill the screen with black
screen.fill(BLACK)
for event in pygame.event.get():
# Check for the QUIT event
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Check for the KEYDOWN event
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if game.valid_move(game.current_piece, -1, 0, 0):
game.current_piece.x -= 1 # Move the piece to the left
if event.key == pygame.K_RIGHT:
if game.valid_move(game.current_piece, 1, 0, 0):
game.current_piece.x += 1 # Move the piece to the right
if event.key == pygame.K_DOWN:
if game.valid_move(game.current_piece, 0, 1, 0):
game.current_piece.y += 1 # Move the piece down
if event.key == pygame.K_UP:
if game.valid_move(game.current_piece, 0, 0, 1):
game.current_piece.rotation += 1 # Rotate the piece
if event.key == pygame.K_SPACE:
while game.valid_move(game.current_piece, 0, 1, 0):
game.current_piece.y += 1 # Move the piece down until it hits the bottom
game.lock_piece(game.current_piece) # Lock the piece in place
# Get the number of milliseconds since the last frame
delta_time = clock.get_rawtime()
# Add the delta time to the fall time
fall_time += delta_time
if fall_time >= fall_speed:
# Move the piece down
game.update()
# Reset the fall time
fall_time = 0
# Draw the score on the screen
draw_score(screen, game.score, 10, 10)
# Draw the grid and the current piece
game.draw(screen)
if game.game_over:
# Draw the "Game Over" message
draw_game_over(screen, WIDTH // 2 - 100, HEIGHT // 2 - 30) # Draw the "Game Over" message
# You can add a "Press any key to restart" message here
# Check for the KEYDOWN event
if event.type == pygame.KEYDOWN:
# Create a new Tetris object
game = Tetris(WIDTH // GRID_SIZE, HEIGHT // GRID_SIZE)
# Update the display
pygame.display.flip()
# Set the framerate
clock.tick(60)
The main game loop is a while
loop that continuously executes the following steps:
That's it! This simple implementation of a Tetris game should now be functional. You can run the script to play the game and make any desired customizations to the gameplay, graphics, or controls.
You can get the complete code here.
Here are some related games built with Python:
Happy coding ♥
Want to code smarter? Our Python Code Assistant is waiting to help you. Try it now!
View Full Code Build My Python Code
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!