Tetris Game Using Python

Tetris Game Using Python

Tetris Game Using Python

Creating games is an excellent way to understand Python programming because it combines logic, user input, graphics, loops, and object-oriented programming in one practical project. In this tutorial, we will build a complete Tetris Game Using Python with the Pygame library.

Tetris is a block-arranging puzzle game in which different shapes fall from the top of the game board. The player moves and rotates these shapes to create complete horizontal rows. Whenever a row is completely filled, it is removed and the player receives points.

This project is suitable for beginners who want to practice Python and learn how a simple desktop game works. The complete game is contained in a single Python file, so you can copy the code, install Pygame, and run it directly from Visual Studio Code or a terminal.

Complete Advance AI Topics: Click Here
Real Time Projects on YouTube:- DecodeIT

What You Will Learn

After completing this project, you will understand how to:

  • Create a graphical game window with Pygame.
  • Represent a game board using a two-dimensional list.
  • Create different falling block shapes.
  • Move blocks horizontally and vertically.
  • Rotate Tetris pieces.
  • Detect collisions with the board and existing blocks.
  • Remove completed rows.
  • Calculate and display the player’s score.
  • Handle keyboard controls.
  • Detect the game-over condition.

Requirements

Before starting, make sure Python is installed on your computer. You also need the Pygame package because it provides the graphics, keyboard events, timing, and display functionality required by the game.

Open the VS Code terminal and install Pygame with the following command:

pip install pygame

After installation, create a file named tetris_game.py. The complete source code is provided below.

How the Tetris Game Works

The game board is divided into rows and columns. Each falling piece is represented by four blocks. When the player moves a piece, the program checks whether its new position is valid.

If a piece reaches the bottom of the board or touches another fixed block, it becomes part of the board. The program then checks every row. A completely filled row is removed, and the blocks above it move downward.

The game continues generating new pieces until a new piece cannot be placed inside the board. At that point, the game enters the game-over state.

Keyboard Controls

KeyAction
Left ArrowMove the current piece left
Right ArrowMove the current piece right
Down ArrowMove the piece downward faster
Up ArrowRotate the current piece
SpaceDrop the piece immediately
EscapeRestart the game

Screenshot

Tetris Game Using Python
Tetris Game Using Python

Complete Tetris Game Code

The following is the complete project code. You only need to create one Python file and paste this code into it. No additional Python source files are required.

import pygame
import random

pygame.init()

# -----------------------------
# Game Configuration
# -----------------------------
CELL_SIZE = 30
BOARD_WIDTH = 10
BOARD_HEIGHT = 20

SIDE_PANEL = 180
WINDOW_WIDTH = BOARD_WIDTH * CELL_SIZE + SIDE_PANEL
WINDOW_HEIGHT = BOARD_HEIGHT * CELL_SIZE

FPS = 60

# -----------------------------
# Colors
# -----------------------------
BACKGROUND = (18, 18, 24)
GRID_COLOR = (55, 55, 65)
WHITE = (245, 245, 245)
GRAY = (140, 140, 150)
RED = (230, 70, 70)
GREEN = (70, 200, 120)
BLUE = (70, 130, 230)
YELLOW = (235, 210, 70)
PURPLE = (170, 90, 220)
CYAN = (70, 210, 220)
ORANGE = (240, 150, 60)

BLOCK_COLORS = [
    CYAN,
    BLUE,
    ORANGE,
    YELLOW,
    GREEN,
    PURPLE,
    RED
]

# -----------------------------
# Tetris Shapes
# Each shape contains rotation states.
# -----------------------------
SHAPES = [
    [
        [
            "....",
            "....",
            "IIII",
            "...."
        ],
        [
            "..I.",
            "..I.",
            "..I.",
            "..I."
        ]
    ],

    [
        [
            ".OO.",
            ".OO.",
            "....",
            "...."
        ]
    ],

    [
        [
            ".T..",
            "TTT.",
            "....",
            "...."
        ],
        [
            ".T..",
            ".TT.",
            ".T..",
            "...."
        ],
        [
            "....",
            "TTT.",
            ".T..",
            "...."
        ],
        [
            ".T..",
            "TT..",
            ".T..",
            "...."
        ]
    ],

    [
        [
            "SS..",
            ".SS.",
            "....",
            "...."
        ],
        [
            ".S..",
            "SS..",
            "S...",
            "...."
        ]
    ],

    [
        [
            ".ZZ.",
            "ZZ..",
            "....",
            "...."
        ],
        [
            "Z...",
            "ZZ..",
            ".Z..",
            "...."
        ]
    ],

    [
        [
            "J...",
            "JJJ.",
            "....",
            "...."
        ],
        [
            ".JJ.",
            ".J..",
            ".J..",
            "...."
        ],
        [
            "....",
            "JJJ.",
            "..J.",
            "...."
        ],
        [
            ".J..",
            ".J..",
            "JJ..",
            "...."
        ]
    ],

    [
        [
            "..L.",
            "LLL.",
            "....",
            "...."
        ],
        [
            ".L..",
            ".L..",
            ".LL.",
            "...."
        ],
        [
            "....",
            "LLL.",
            "L...",
            "...."
        ],
        [
            "LL..",
            ".L..",
            ".L..",
            "...."
        ]
    ]
]


class Piece:
    def __init__(self):
        self.shape_index = random.randrange(len(SHAPES))
        self.rotation = 0
        self.color = BLOCK_COLORS[self.shape_index]
        self.x = BOARD_WIDTH // 2 - 2
        self.y = -1

    def cells(self):
        shape = SHAPES[self.shape_index][self.rotation]

        blocks = []

        for row in range(4):
            for col in range(4):
                if shape[row][col] != ".":
                    blocks.append((self.x + col, self.y + row))

        return blocks

    def rotate(self):
        self.rotation = (self.rotation + 1) % len(
            SHAPES[self.shape_index]
        )


class TetrisGame:
    def __init__(self):
        self.board = [
            [None for _ in range(BOARD_WIDTH)]
            for _ in range(BOARD_HEIGHT)
        ]

        self.current_piece = Piece()
        self.next_piece = Piece()

        self.score = 0
        self.lines = 0
        self.level = 1

        self.game_over = False

        self.fall_timer = 0
        self.fall_delay = 500

    def valid_position(self, piece):
        for x, y in piece.cells():

            if x < 0 or x >= BOARD_WIDTH:
                return False

            if y >= BOARD_HEIGHT:
                return False

            if y >= 0 and self.board[y][x] is not None:
                return False

        return True

    def move(self, dx, dy):
        self.current_piece.x += dx
        self.current_piece.y += dy

        if not self.valid_position(self.current_piece):
            self.current_piece.x -= dx
            self.current_piece.y -= dy
            return False

        return True

    def rotate(self):
        old_rotation = self.current_piece.rotation

        self.current_piece.rotate()

        if not self.valid_position(self.current_piece):
            self.current_piece.rotation = old_rotation

    def lock_piece(self):
        for x, y in self.current_piece.cells():

            if 0 <= y < BOARD_HEIGHT:
                self.board[y][x] = self.current_piece.color

        self.clear_lines()

        self.current_piece = self.next_piece
        self.next_piece = Piece()

        if not self.valid_position(self.current_piece):
            self.game_over = True

    def clear_lines(self):
        completed_lines = 0

        new_board = []

        for row in self.board:
            if all(cell is not None for cell in row):
                completed_lines += 1
            else:
                new_board.append(row)

        while len(new_board) < BOARD_HEIGHT:
            new_board.insert(
                0,
                [None for _ in range(BOARD_WIDTH)]
            )

        self.board = new_board

        if completed_lines > 0:
            self.lines += completed_lines

            points = {
                1: 100,
                2: 300,
                3: 500,
                4: 800
            }

            self.score += points.get(
                completed_lines,
                completed_lines * 250
            )

            self.level = self.lines // 10 + 1

            self.fall_delay = max(
                100,
                500 - (self.level - 1) * 40
            )

    def hard_drop(self):
        distance = 0

        while self.move(0, 1):
            distance += 1

        self.score += distance * 2
        self.lock_piece()

    def update(self, delta_time):
        if self.game_over:
            return

        self.fall_timer += delta_time

        if self.fall_timer >= self.fall_delay:
            self.fall_timer = 0

            if not self.move(0, 1):
                self.lock_piece()


def draw_block(screen, x, y, color, size=CELL_SIZE):
    pygame.draw.rect(
        screen,
        color,
        (
            x * size + 1,
            y * size + 1,
            size - 2,
            size - 2
        )
    )

    pygame.draw.rect(
        screen,
        WHITE,
        (
            x * size + 1,
            y * size + 1,
            size - 2,
            size - 2
        ),
        1
    )


def draw_board(screen, game):
    for row in range(BOARD_HEIGHT):
        for col in range(BOARD_WIDTH):

            pygame.draw.rect(
                screen,
                GRID_COLOR,
                (
                    col * CELL_SIZE,
                    row * CELL_SIZE,
                    CELL_SIZE,
                    CELL_SIZE
                ),
                1
            )

            if game.board[row][col] is not None:
                draw_block(
                    screen,
                    col,
                    row,
                    game.board[row][col]
                )


def draw_piece(screen, piece):
    for x, y in piece.cells():

        if y >= 0:
            draw_block(
                screen,
                x,
                y,
                piece.color
            )


def draw_next_piece(screen, piece, font):
    title = font.render("Next Piece", True, WHITE)
    screen.blit(
        title,
        (
            BOARD_WIDTH * CELL_SIZE + 25,
            55
        )
    )

    shape = SHAPES[piece.shape_index][0]

    for row in range(4):
        for col in range(4):

            if shape[row][col] != ".":
                pygame.draw.rect(
                    screen,
                    piece.color,
                    (
                        BOARD_WIDTH * CELL_SIZE
                        + 45
                        + col * 25,
                        95 + row * 25,
                        23,
                        23
                    )
                )


def draw_panel(screen, game, font, large_font):
    panel_x = BOARD_WIDTH * CELL_SIZE

    pygame.draw.rect(
        screen,
        (25, 25, 33),
        (
            panel_x,
            0,
            SIDE_PANEL,
            WINDOW_HEIGHT
        )
    )

    title = large_font.render(
        "TETRIS",
        True,
        WHITE
    )

    screen.blit(
        title,
        (
            panel_x + 25,
            15
        )
    )

    draw_next_piece(
        screen,
        game.next_piece,
        font
    )

    score_text = font.render(
        f"Score: {game.score}",
        True,
        WHITE
    )

    lines_text = font.render(
        f"Lines: {game.lines}",
        True,
        WHITE
    )

    level_text = font.render(
        f"Level: {game.level}",
        True,
        WHITE
    )

    screen.blit(
        score_text,
        (panel_x + 25, 220)
    )

    screen.blit(
        lines_text,
        (panel_x + 25, 255)
    )

    screen.blit(
        level_text,
        (panel_x + 25, 290)
    )

    controls = [
        "Controls",
        "Left / Right",
        "Move",
        "Down",
        "Soft Drop",
        "Up",
        "Rotate",
        "Space",
        "Hard Drop",
        "ESC",
        "Restart"
    ]

    y = 350

    for index, text in enumerate(controls):

        if index == 0:
            color = YELLOW
        else:
            color = GRAY

        control_text = font.render(
            text,
            True,
            color
        )

        screen.blit(
            control_text,
            (panel_x + 25, y)

        y += 22


def draw_game_over(screen, font, large_font):
    overlay = pygame.Surface(
        (BOARD_WIDTH * CELL_SIZE, WINDOW_HEIGHT)
    )

    overlay.set_alpha(190)
    overlay.fill((0, 0, 0))

    screen.blit(
        overlay,
        (0, 0)
    )

    game_over_text = large_font.render(
        "GAME OVER",
        True,
        RED
    )

    restart_text = font.render(
        "Press ESC to restart",
        True,
        WHITE
    )

    screen.blit(
        game_over_text,
        (
            45,
            WINDOW_HEIGHT // 2 - 50
        )
    )

    screen.blit(
        restart_text,
        (
            75,
            WINDOW_HEIGHT // 2 + 15
        )
    )


def main():
    screen = pygame.display.set_mode(
        (WINDOW_WIDTH, WINDOW_HEIGHT)
    )

    pygame.display.set_caption(
        "Tetris Game Using Python"
    )

    clock = pygame.time.Clock()

    font = pygame.font.SysFont(
        "Arial",
        18,
        True
    )

    large_font = pygame.font.SysFont(
        "Arial",
        30,
        True
    )

    game = TetrisGame()

    running = True

    while running:

        delta_time = clock.tick(FPS)

        for event in pygame.event.get():

            if event.type == pygame.QUIT:
                running = False

            if event.type == pygame.KEYDOWN:

                if event.key == pygame.K_ESCAPE:
                    game = TetrisGame()

                elif not game.game_over:

                    if event.key == pygame.K_LEFT:
                        game.move(-1, 0)

                    elif event.key == pygame.K_RIGHT:
                        game.move(1, 0)

                    elif event.key == pygame.K_DOWN:
                        if game.move(0, 1):
                            game.score += 1

                    elif event.key == pygame.K_UP:
                        game.rotate()

                    elif event.key == pygame.K_SPACE:
                        game.hard_drop()

        if not game.game_over:
            game.update(delta_time)

        screen.fill(BACKGROUND)

        draw_board(
            screen,
            game
        )

        if not game.game_over:
            draw_piece(
                screen,
                game.current_piece
            )

        draw_panel(
            screen,
            game,
            font,
            large_font
        )

        if game.game_over:
            draw_game_over(
                screen,
                font,
                large_font
            )

        pygame.display.flip()

    pygame.quit()


if __name__ == "__main__":
    main()

How to Run the Project

After saving the code as tetris_game.py, open the VS Code terminal in the same folder and run:

python tetris_game.py

If your computer has multiple Python versions installed, you can also use:

py tetris_game.py

The Tetris window should open. Use the arrow keys to control the falling pieces and use the Space key to drop a piece immediately.

Understanding the Main Components

Piece Class

The Piece class represents the currently falling Tetris block. It stores the selected shape, color, rotation, and position on the board. The cells() method converts the selected shape into actual board coordinates.

TetrisGame Class

The TetrisGame class contains the main game logic. It manages the board, score, levels, falling speed, current piece, next piece, movement, collision detection, row removal, and game-over condition.

Collision Detection

Before moving a piece, the program checks whether its new position is outside the board or overlaps with an existing block. If the position is invalid, the movement is cancelled.

Line Clearing

Whenever a piece becomes fixed on the board, the program checks each row. A row containing no empty cells is considered complete and is removed. Empty rows are then added at the top of the board.

Scoring System

The game awards points according to the number of rows removed at once. Clearing multiple rows provides more points. The game also increases its level after every ten cleared lines, causing pieces to fall faster.

Project Structure

Because this version uses a single Python source file, the project structure is very simple:

Tetris-Game/
│
└── tetris_game.py

Conclusion

This Tetris project demonstrates how Python can be used to create an interactive desktop game with Pygame. Instead of relying on multiple files or complicated dependencies, the complete game logic is contained in one Python program.

Keywords: Tetris Game Using Python, Tetris Python Code, Tetris Game Using Pygame, Python Tetris Game, Create Tetris Game Using Python, Pygame Game Project, Python Game Development, Tetris Code, Python Projects for Beginners,Tetris Game Using Pygame, Tetris Python Code, Python Tetris Game, Tetris Game Python Source Code, Create Tetris Game Using Python, Tetris Game Using Python Pygame, Python Game Development, Pygame Projects, Pygame Game Project, Python Game Projects, Tetris Code, Tetris Python Project, Python Projects for Beginners, Tetris Game Source Code, How to Create Tetris Game in Python, Tetris Game Code Copy and Paste, Tetris Game for Beginners, Python Desktop Game, Pygame Python Tutorial, Tetris Game Project in Python

Leave a Reply

Your email address will not be published. Required fields are marked *