Ithy Logo

Two-Player Tetris Clone in Python

Engage in classic Tetris gameplay with an exhilarating two-player experience.

tetris game on computer

Key Takeaways

  • Dual Gameplay: Enjoy simultaneous Tetris games side-by-side on a single screen.
  • Customized Controls: Each player has dedicated keys for movement, rotation, and dropping tetrominos.
  • Real-Time Scoring: Track and display individual scores for both players in real-time.

Overview

This two-player Tetris clone is developed in Python using the Pygame library. It allows two players to play Tetris simultaneously on the same machine, each with their own game board. The game features separate controls for each player, individual scoring systems, and a responsive interface that ensures smooth gameplay.

Features

Dual Game Boards

The game displays two independent Tetris grids side-by-side, enabling both players to play without interference. Each board operates independently, managing its own tetromino generation, movement, and line clearing mechanics.

Individual Controls

Each player has a set of dedicated keys to control their respective game boards. This ensures that both players can play without input conflicts, allowing for an engaging and competitive experience.

Scoring System

The game tracks each player's score independently. Points are awarded based on the number of lines cleared, with higher rewards for multiple line clears at once. Scores are displayed in real-time below each game board.

Controls

Player Action Keys
Player 1 Move Left A
Player 1 Move Right D
Player 1 Rotate W
Player 1 Soft Drop S
Player 1 Hard Drop Q
Player 2 Move Left Left Arrow
Player 2 Move Right Right Arrow
Player 2 Rotate Up Arrow
Player 2 Soft Drop Down Arrow
Player 2 Hard Drop Comma (,)

Complete Python Code

Below is the complete Python code for the two-player Tetris clone. The code is structured to handle two separate game boards, manage independent controls for each player, and track individual scores. Ensure you have Python and Pygame installed on your system before running the game.


import pygame
import random

# Initialize Pygame
pygame.init()

# Screen dimensions
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 500
BLOCK_SIZE = 30

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)
CYAN = (0, 255, 255)
YELLOW = (255, 255, 0)
PURPLE = (128, 0, 128)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
ORANGE = (255, 165, 0)
BLUE = (0, 0, 255)

# Tetromino shapes
SHAPES = {
    'I': [['.....',
           '.....',
           '..X..',
           '..X..',
           '..X..',
           '..X..',
           '.....'],
          ['.....',
           '.....',
           '.....',
           'XXXX.',
           '.....',
           '.....',
           '.....']],
    'O': [['.....',
           '.....',
           '.XX..',
           '.XX..',
           '.....',
           '.....',
           '.....']],
    'T': [['.....',
           '.....',
           '..X..',
           '.XXX.',
           '.....',
           '.....',
           '.....'],
          ['.....',
           '..X..',
           '..XX.',
           '..X..',
           '.....',
           '.....',
           '.....']],
    'S': [['.....',
           '.....',
           '..XX.',
           '.XX..',
           '.....',
           '.....',
           '.....']],
    'Z': [['.....',
           '.....',
           '.XX..',
           '..XX.',
           '.....',
           '.....',
           '.....']],
    'J': [['.....',
           '.....',
           '.X...',
           '.XXX.',
           '.....',
           '.....',
           '.....'],
          ['.....',
           '..XX.',
           '..X..',
           '..X..',
           '.....',
           '.....',
           '.....']],
    'L': [['.....',
           '.....',
           '...X.',
           '.XXX.',
           '.....',
           '.....',
           '.....'],
          ['.....',
           '..X..',
           '..X..',
           '..XX.',
           '.....',
           '.....',
           '.....']]
}

# Map shapes to colors
SHAPE_COLORS = {
    'I': CYAN,
    'O': YELLOW,
    'T': PURPLE,
    'S': GREEN,
    'Z': RED,
    'J': BLUE,
    'L': ORANGE
}

class Tetromino:
    def __init__(self, x, y, shape):
        self.x = x  # Horizontal position
        self.y = y  # Vertical position
        self.shape = shape
        self.color = SHAPE_COLORS[shape]
        self.rotation = 0  # Number from 0- number of rotations

    def image(self):
        return SHAPES[self.shape][self.rotation % len(SHAPES[self.shape])]

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

class Board:
    def __init__(self, x_offset):
        self.board = [[BLACK for _ in range(10)] for _ in range(20)]
        self.x_offset = x_offset
        self.current_piece = self.get_new_piece()
        self.next_piece = self.get_new_piece()
        self.score = 0
        self.game_over = False

    def get_new_piece(self):
        shape = random.choice(list(SHAPES.keys()))
        return Tetromino(4, 0, shape)

    def valid_space(self, shape, adj_x=0, adj_y=0):
        accepted_pos = [[(j, i) for j in range(10) if self.board[i][j] == BLACK] for i in range(20)]
        accepted_pos = [j for sub in accepted_pos for j in sub]
        formatted = self.convert_shape_format(shape)

        for pos in formatted:
            x, y = pos
            x += adj_x
            y += adj_y
            if x < 0 or x >= 10 or y >= 20:
                return False
            if (x, y) not in accepted_pos:
                if y >= 0:
                    return False
        return True

    def convert_shape_format(self, shape):
        positions = []
        format = shape.image()

        for i, line in enumerate(format):
            row = list(line)
            for j, column in enumerate(row):
                if column == 'X':
                    positions.append((shape.x + j, shape.y + i))

        return positions

    def lock_piece(self):
        formatted = self.convert_shape_format(self.current_piece)
        for pos in formatted:
            x, y = pos
            if y >= 0:
                self.board[y][x] = self.current_piece.color
        self.clear_rows()
        self.current_piece = self.next_piece
        self.next_piece = self.get_new_piece()
        if not self.valid_space(self.current_piece):
            self.game_over = True

    def clear_rows(self):
        rows_to_clear = []
        for i in range(len(self.board)):
            row = self.board[i]
            if BLACK not in row:
                rows_to_clear.append(i)
        for i in rows_to_clear:
            del self.board[i]
            self.board.insert(0, [BLACK for _ in range(10)])
            self.score += 100 * (len(rows_to_clear))

    def draw_grid(self, screen):
        for i in range(len(self.board)):
            pygame.draw.line(screen, GRAY, (self.x_offset, i * BLOCK_SIZE), 
                             (self.x_offset + 10 * BLOCK_SIZE, i * BLOCK_SIZE))
            for j in range(10):
                pygame.draw.line(screen, GRAY, 
                                 (self.x_offset + j * BLOCK_SIZE, 0),
                                 (self.x_offset + j * BLOCK_SIZE, 20 * BLOCK_SIZE))

    def draw(self, screen):
        for i in range(len(self.board)):
            for j in range(len(self.board[i])):
                pygame.draw.rect(screen, self.board[i][j], 
                                 (self.x_offset + j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0)
        self.draw_grid(screen)
        pygame.draw.rect(screen, WHITE, 
                         (self.x_offset, 0, 10 * BLOCK_SIZE, 20 * BLOCK_SIZE), 5)

    def move_piece(self, dx, dy):
        if self.valid_space(self.current_piece, dx, dy):
            self.current_piece.x += dx
            self.current_piece.y += dy
            return True
        return False

    def rotate_piece(self):
        original_rotation = self.current_piece.rotation
        self.current_piece.rotate()
        if not self.valid_space(self.current_piece):
            self.current_piece.rotation = original_rotation

class TetrisGame:
    def __init__(self):
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("Two-Player Tetris")
        self.clock = pygame.time.Clock()
        self.board1 = Board(50)
        self.board2 = Board(500)
        self.fall_time = 0
        self.fall_speed = 0.5  # seconds per fall

    def run(self):
        run = True
        while run:
            self.fall_time += self.clock.get_rawtime()
            self.clock.tick()

            if self.fall_time / 1000 > self.fall_speed:
                self.fall_time = 0
                for board in [self.board1, self.board2]:
                    if board.move_piece(0, 1):
                        pass
                    else:
                        board.lock_piece()

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    run = False

                if event.type == pygame.KEYDOWN:
                    # Player 1 Controls
                    if event.key == pygame.K_a:
                        self.board1.move_piece(-1, 0)
                    if event.key == pygame.K_d:
                        self.board1.move_piece(1, 0)
                    if event.key == pygame.K_w:
                        self.board1.rotate_piece()
                    if event.key == pygame.K_s:
                        self.board1.move_piece(0, 1)
                    if event.key == pygame.K_q:
                        while self.board1.move_piece(0, 1):
                            pass

                    # Player 2 Controls
                    if event.key == pygame.K_LEFT:
                        self.board2.move_piece(-1, 0)
                    if event.key == pygame.K_RIGHT:
                        self.board2.move_piece(1, 0)
                    if event.key == pygame.K_UP:
                        self.board2.rotate_piece()
                    if event.key == pygame.K_DOWN:
                        self.board2.move_piece(0, 1)
                    if event.key == pygame.K_COMMA:
                        while self.board2.move_piece(0, 1):
                            pass

            self.screen.fill(BLACK)

            self.board1.draw(self.screen)
            self.board2.draw(self.screen)

            # Display Scores
            font = pygame.font.SysFont('comicsans', 30)
            score1 = font.render(f'Player 1 Score: {self.board1.score}', True, WHITE)
            score2 = font.render(f'Player 2 Score: {self.board2.score}', True, WHITE)
            self.screen.blit(score1, (50, 420))
            self.screen.blit(score2, (500, 420))

            pygame.display.update()

            if self.board1.game_over or self.board2.game_over:
                run = False

        pygame.quit()

if __name__ == "__main__":
    game = TetrisGame()
    game.run()
    

How to Run the Game

  1. Ensure you have Python installed on your system. You can download it from Python's official website.
  2. Install the Pygame library by running the following command in your terminal or command prompt:
    pip install pygame
  3. Copy the complete Python code provided above into a file named tetris_for_two.py.
  4. Navigate to the directory containing the tetris_for_two.py file in your terminal or command prompt.
  5. Run the game by executing:
    python tetris_for_two.py

Gameplay Instructions

Once the game starts, two Tetris boards will appear side-by-side. Each player controls their own board using the designated keys:

Player 1 Controls

  • Move Left: A
  • Move Right: D
  • Rotate: W
  • Soft Drop: S
  • Hard Drop: Q

Player 2 Controls

  • Move Left: Left Arrow
  • Move Right: Right Arrow
  • Rotate: Up Arrow
  • Soft Drop: Down Arrow
  • Hard Drop: ,

The objective is to clear as many lines as possible by filling horizontal rows with tetromino blocks without gaps. Clearing multiple lines at once yields higher scores. The game ends when a new tetromino cannot enter the board without colliding with existing blocks.

Conclusion

This two-player Tetris clone offers an engaging and competitive experience, allowing friends and family to enjoy classic Tetris gameplay together on a single machine. With its intuitive controls and real-time scoring, it brings the timeless fun of Tetris to the modern Python environment.

References


Last updated February 5, 2025
Ask me more