How to create the game «Tetris» in Python?

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Creating a Tetris Game in Python: A Step-by-Step Guide

Developing your own game is an effective way to improve your programming skills. Learning Python through practical projects allows for a deeper understanding of the language's capabilities. The classic Tetris game combines interesting game mechanics with a relatively simple graphics implementation.

This guide provides a detailed analysis of creating a full-fledged Tetris game in Python. We use the Pygame library to handle graphics and user events. You will receive a ready-made working code and an understanding of all stages of development.

Preparing for Development

System requirements

To create a Tetris game, you will need:

  • Python version 3.x or higher
  • Pygame library for rendering graphics
  • Basic knowledge of object-oriented programming

Installing the necessary components

The Pygame library is installed through the pip package manager:

pip install pygame

After installation, the library will be available for import into the project. Pygame provides tools for creating windows, handling events, drawing graphic primitives, and playing sound.

Tetris Game Architecture

Main development stages

The process of creating a game includes several key steps:

  • Initializing the game window and configuring parameters
  • Defining the geometry of tetromino shapes
  • Implementing the mechanics of falling and moving shapes
  • Checking for collisions with field boundaries and other blocks
  • System for removing filled horizontal lines
  • Handling user input for control
  • Adding a scoring system and game over screen

Data structure of the playing field

The playing field is represented by a two-dimensional array. Each cell contains information about the color of the block or remains empty. The dimensions of the field are determined by constants, which makes it easy to change the scale of the game.

Initializing the Game Window

Setting basic settings

import pygame
import random

# Pygame initialization
pygame.init()

# Window and grid sizes
SCREEN_WIDTH = 300
SCREEN_HEIGHT = 600
BLOCK_SIZE = 30

# Color palette
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
COLORS = [
    (0, 255, 255),  # I
    (255, 165, 0),  # L
    (0, 0, 255),    # J
    (255, 255, 0),  # O
    (0, 255, 0),    # S
    (128, 0, 128),  # T
    (255, 0, 0)     # Z
]

# Window settings
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Tetris on Python")

Constants determine the size of the game window and blocks. The COLORS array contains colors for each type of shape. Pygame.init() initializes all library modules.

Creating a game screen

The pygame.display.set_mode() function creates a window with the specified dimensions. The set_caption() method sets the window title. These settings form the visual basis of the game.

Defining Tetromino Shapes

Geometry of classic shapes

FIGURES = [
    [[1, 1, 1, 1]],  # I
    [[1, 0, 0],
     [1, 1, 1]],    # J
    [[0, 0, 1],
     [1, 1, 1]],    # L
    [[1, 1],
     [1, 1]],       # O
    [[0, 1, 1],
     [1, 1, 0]],    # S
    [[0, 1, 0],
     [1, 1, 1]],    # T
    [[1, 1, 0],
     [0, 1, 1]]     # Z
]

Each shape is represented by a two-dimensional array. Ones indicate filled blocks, zeros indicate empty cells. This data structure simplifies collision detection and shape drawing.

Shape rotation system

Some shapes have multiple rotation states. To implement rotation, you can expand the FIGURES array by adding all possible orientations of each shape.

Game Piece Class

Basic class structure Figure

class Figure:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.type = random.randint(0, len(FIGURES) - 1)
        self.color = COLORS[self.type]
        self.rotation = 0

    def shape(self):
        return FIGURES[self.type][self.rotation % len(FIGURES[self.type])]

    def rotate(self):
        self.rotation = (self.rotation + 1) % len(FIGURES[self.type])

The Figure class encapsulates data about the shape's position, its type, and its current rotation angle. The shape() method returns the current geometry of the shape taking into account the rotation.

Shape control methods

The rotate() method changes the rotation state of the shape. The use of modular arithmetic ensures a cyclic transition between states. The x and y coordinates define the position of the shape on the playing field.

Main Game Class

Initializing the game state

class Tetris:
    def __init__(self, height, width):
        self.height = height
        self.width = width
        self.field = [[0 for _ in range(width)] for _ in range(height)]
        self.figure = None
        self.score = 0

    def new_figure(self):
        self.figure = Figure(self.width // 2 - 1, 0)

The Tetris class manages the overall state of the game. The two-dimensional array field represents the playing field. The new_figure() method creates a new shape in the upper central part of the field.

Collision Detection System

def intersects(self):
    shape = self.figure.shape()
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                if (self.figure.y + y >= self.height or
                    self.figure.x + x < 0 or
                    self.figure.x + x >= self.width or
                    self.field[self.figure.y + y][self.figure.x + x]):
                    return True
    return False

The intersects() method checks for intersection of the shape with the boundaries of the field and placed blocks. The function returns True if a collision is detected.

Game Mechanics

Fixing shapes on the field

def freeze(self):
    shape = self.figure.shape()
    for y, row in enumerate(shape):
        for x, cell in enumerate(row):
            if cell:
                self.field[self.figure.y + y][self.figure.x + x] = self.figure.color
    self.clear_lines()
    self.new_figure()
    if self.intersects():
        self.__init__(self.height, self.width)  # Game Over

The freeze() method places the blocks of the fallen shape on the playing field. After fixing, the filled lines are checked, a new shape is created. If it is impossible to place a new shape, the game restarts.

Removing filled lines

def clear_lines(self):
    lines_cleared = 0
    for i in range(self.height - 1, -1, -1):
        if all(self.field[i]):
            del self.field[i]
            self.field.insert(0, [0 for _ in range(self.width)])
            lines_cleared += 1
    self.score += lines_cleared ** 2

The scoring system is based on the number of lines removed simultaneously. A quadratic dependence encourages removing multiple lines in one move. New blank lines are added to the top of the field.

Main Game Loop

Initializing the gameplay

game = Tetris(SCREEN_HEIGHT // BLOCK_SIZE, SCREEN_WIDTH // BLOCK_SIZE)
clock = pygame.time.Clock()
fall_speed = 500
last_move_down = pygame.time.get_ticks()

running = True
game.new_figure()

A game instance is created with dimensions calculated from the screen and block sizes. The clock object controls the screen refresh rate. The fall_speed variable determines the speed at which shapes fall.

Handling user input

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        running = False
    elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
            game.figure.x -= 1
            if game.intersects():
                game.figure.x += 1
        elif event.key == pygame.K_RIGHT:
            game.figure.x += 1
            if game.intersects():
                game.figure.x -= 1
        elif event.key == pygame.K_DOWN:
            game.figure.y += 1
            if game.intersects():
                game.figure.y -= 1
        elif event.key == pygame.K_UP:
            game.figure.rotate()
            if game.intersects():
                game.figure.rotate()

The control system responds to key presses. If a collision is detected, the movement is canceled. The left and right arrows move the shape horizontally, the down arrow accelerates the fall, and the up arrow rotates the shape.

Automatic shape falling

if pygame.time.get_ticks() - last_move_down > fall_speed:
    game.figure.y += 1
    if game.intersects():
        game.figure.y -= 1
        game.freeze()
    last_move_down = pygame.time.get_ticks()

The timer controls the automatic downward movement of shapes. If further falling is not possible, the shape is fixed on the field. The falling speed can be changed to adjust the difficulty.

Graphics Rendering

Rendering the playing field

for y in range(game.height):
    for x in range(game.width):
        if game.field[y][x]:
            pygame.draw.rect(
                screen,
                game.field[y][x],
                [x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE]
            )

A double loop bypasses all cells of the playing field. Filled cells are drawn as colored rectangles. The size and position of each block are calculated based on the coordinates in the grid.

Displaying the active shape

shape = game.figure.shape()
for y, row in enumerate(shape):
    for x, cell in enumerate(row):
        if cell:
            pygame.draw.rect(
                screen,
                game.figure.color,
                [(game.figure.x + x) * BLOCK_SIZE, (game.figure.y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE]
            )

The falling shape is drawn on top of the playing field. The coordinates of the blocks of the shape are added to its position on the field. This ensures the correct display of the shape in motion.

Additional Features

Scoring System

To display the current score, you can use the pygame.font module:

font = pygame.font.Font(None, 36)
score_text = font.render(f"Score: {game.score}", True, WHITE)
screen.blit(score_text, (10, 10))

The text with points is displayed in the upper part of the screen. The score is updated when lines are removed. Different fonts and sizes allow you to customize the appearance of the interface.

Game Over Screen

When the top of the field is filled, you can display a game over message:

if game_over:
    game_over_text = font.render("GAME OVER", True, WHITE)
    screen.blit(game_over_text, (SCREEN_WIDTH//2 - 80, SCREEN_HEIGHT//2))

Functionality Expansion

Adding Sound Effects

Pygame.mixer allows you to play audio files:

pygame.mixer.init()
line_clear_sound = pygame.mixer.Sound("line_clear.wav")

Sounds can be played when lines are removed, shapes are rotated, or the game ends. This greatly improves the gaming experience.

Setting Difficulty Levels

Changing the falling speed of shapes creates different difficulty levels:

level = game.score // 10 + 1
fall_speed = max(50, 500 - level * 50)

The speed increases with increasing score. The minimum value prevents shapes from falling too quickly.

Next Shape Preview

Displaying the next shape increases the strategic component of the game:

next_figure = Figure(0, 0)
# Drawing in a separate area of the screen

Frequently Asked Questions

Changing the size of the playing field

The size of the field is configured through the constants SCREEN_WIDTH, SCREEN_HEIGHT, and BLOCK_SIZE. Increasing these values creates a more spacious playing field.

Adding Music

The pygame.mixer library supports playing music files of various formats. Background music is created via pygame.mixer.music.load() and pygame.mixer.music.play().

Different Difficulty Levels

Difficulty is adjusted by changing the fall_speed variable. Smaller values speed up the falling of shapes. You can also change the frequency of appearance of complex shapes.

Adaptation for Mobile Devices

For mobile platforms, it is recommended to use Kivy or BeeWare frameworks. They provide tools for creating cross-platform applications with touch control.

Windowed mode with control buttons

Graphical buttons are created as rectangular areas on the screen. Pygame.MOUSEBUTTONDOWN events handle clicks on buttons. This allows you to create an alternative way to control the game.

Multiplayer implementation

Network mode requires the use of the socket module to transfer data between players. Each player receives a copy of the game state and synchronizes their actions with other participants.

Conclusion

This guide contains a complete working project of the Tetris game in Python using the Pygame library. The code can be modified, adding new functions, changing the appearance, or improving the game mechanics.

Creating games develops logical thinking and practical programming skills. This project demonstrates the basic principles of game development: event handling, state management, graphics rendering, and game logic implementation.

News