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.
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.
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.
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.
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 (,) |
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()
pip install pygame
tetris_for_two.py
.tetris_for_two.py
file in your terminal or command prompt.python tetris_for_two.py
Once the game starts, two Tetris boards will appear side-by-side. Each player controls their own board using the designated keys:
A
D
W
S
Q
Left Arrow
Right Arrow
Up Arrow
Down Arrow
,
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.
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.