Here is a complete console-based Tetris game in Python with increasing difficulty as the score increases. The controls are:
W
= RotateA
= Move LeftD
= Move RightS
= DropThe game increases in speed as your score rises. You can run this in a Windows console (due to the use of msvcrt
for keyboard input).
import os
import random
import time
import threading
import msvcrt
# Board dimensions
WIDTH = 10
HEIGHT = 20
# Tetromino shapes
SHAPES = {
'I': [[1, 1, 1, 1]],
'O': [[1, 1], [1, 1]],
'T': [[0, 1, 0], [1, 1, 1]],
'S': [[0, 1, 1], [1, 1, 0]],
'Z': [[1, 1, 0], [0, 1, 1]],
'J': [[1, 0, 0], [1, 1, 1]],
'L': [[0, 0, 1], [1, 1, 1]]
}
class Tetromino:
def __init__(self, shape):
self.shape = SHAPES[shape]
self.x = WIDTH // 2 - len(self.shape[0]) // 2
self.y = 0
def rotate(self):
self.shape = [list(row) for row in zip(*self.shape[::-1])]
class Tetris:
def __init__(self):
self.board = [[0] * WIDTH for _ in range(HEIGHT)]
self.score = 0
self.level = 1
self.tetromino = self.new_tetromino()
self.running = True
def new_tetromino(self):
return Tetromino(random.choice(list(SHAPES.keys())))
def valid_position(self, shape, offset_x, offset_y):
for y, row in enumerate(shape):
for x, cell in enumerate(row):
if cell:
new_x = offset_x + x
new_y = offset_y + y
if new_x < 0 or new_x >= WIDTH or new_y >= HEIGHT:
return False
if new_y >= 0 and self.board[new_y][new_x]:
return False
return True
def merge(self):
for y, row in enumerate(self.tetromino.shape):
for x, cell in enumerate(row):
if cell:
self.board[self.tetromino.y + y][self.tetromino.x + x] = 1
def clear_lines(self):
new_board = [row for row in self.board if any(cell == 0 for cell in row)]
lines_cleared = HEIGHT - len(new_board)
self.score += lines_cleared * 100
self.level = self.score // 500 + 1
while len(new_board) < HEIGHT:
new_board.insert(0, [0] * WIDTH)
self.board = new_board
def drop(self):
if self.valid_position(self.tetromino.shape, self.tetromino.x, self.tetromino.y + 1):
self.tetromino.y += 1
else:
self.merge()
self.clear_lines()
self.tetromino = self.new_tetromino()
if not self.valid_position(self.tetromino.shape, self.tetromino.x, self.tetromino.y):
self.running = False
def move(self, dx):
if self.valid_position(self.tetromino.shape, self.tetromino.x + dx, self.tetromino.y):
self.tetromino.x += dx
def rotate(self):
old_shape = self.tetromino.shape
self.tetromino.rotate()
if not self.valid_position(self.tetromino.shape, self.tetromino.x, self.tetromino.y):
self.tetromino.shape = old_shape
def render(self):
os.system('cls' if os.name == 'nt' else 'clear')
temp_board = [row[:] for row in self.board]
for y, row in enumerate(self.tetromino.shape):
for x, cell in enumerate(row):
if cell and 0 <= y + self.tetromino.y < HEIGHT and 0 <= x + self.tetromino.x < WIDTH:
temp_board[y + self.tetromino.y][x + self.tetromino.x] = 1
print("Score:", self.score, "Level:", self.level)
for row in temp_board:
print(''.join(['#' if cell else '.' for cell in row]))
def input_loop(self):
while self.running:
if msvcrt.kbhit():
key = msvcrt.getch()
if key == b'a':
self.move(-1)
elif key == b'd':
self.move(1)
elif key == b's':
self.drop()
elif key == b'w':
self.rotate()
def run(self):
threading.Thread(target=self.input_loop, daemon=True).start()
while self.running:
self.render()
self.drop()
time.sleep(max(0.1, 1.0 - (self.level - 1) * 0.1))
self.render()
print("Game Over! Final Score:", self.score)
if __name__ == '__main__':
Tetris().run()