Python开发俄罗斯方块_俄罗斯方块的编程

import pygame
import random
import time
import os

# 初始化pygame
pygame.init()

# 游戏常量
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
SIDEBAR_WIDTH = 200

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 120, 255)
YELLOW = (255, 255, 0)
PURPLE = (180, 0, 255)
CYAN = (0, 255, 255)
ORANGE = (255, 165, 0)
GRAY = (128, 128, 128)
DARK_GRAY = (50, 50, 50)
COLORS = [CYAN, BLUE, ORANGE, YELLOW, GREEN, PURPLE, RED]

# 方块形状
SHAPES = [
    [[1, 1, 1, 1]],  # I
    [[1, 1, 1], [0, 1, 0]],  # T
    [[1, 1, 1], [1, 0, 0]],  # L
    [[1, 1, 1], [0, 0, 1]],  # J
    [[1, 1], [1, 1]],  # O
    [[0, 1, 1], [1, 1, 0]],  # S
    [[1, 1, 0], [0, 1, 1]]  # Z
]

# 设置屏幕
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("俄罗斯方块")

# 游戏区域
game_area_left = (SCREEN_WIDTH - SIDEBAR_WIDTH - GRID_WIDTH * GRID_SIZE) // 2
game_area_top = (SCREEN_HEIGHT - GRID_HEIGHT * GRID_SIZE) // 2

# 加载字体
def get_font(size):
    # 尝试加载系统中文字体
    font_names = ['SimHei', 'Microsoft YaHei', 'SimSun', 'KaiTi', 'Arial Unicode MS']
    for font_name in font_names:
        try:
            return pygame.font.SysFont(font_name, size)
        except:
            continue
    # 如果系统中文字体都不可用,使用默认字体
    return pygame.font.Font(None, size)

class Tetromino:
    def __init__(self):
        self.shape_idx = random.randint(0, len(SHAPES) - 1)
        self.shape = SHAPES[self.shape_idx]
        self.color = COLORS[self.shape_idx]
        self.x = GRID_WIDTH // 2 - len(self.shape[0]) // 2
        self.y = 0
    
    def rotate(self):
        # 转置矩阵实现旋转
        rotated = [[self.shape[y][x] for y in range(len(self.shape)-1, -1, -1)] 
                  for x in range(len(self.shape[0]))]
        return rotated
    
    def draw(self, surface):
        for y, row in enumerate(self.shape):
            for x, cell in enumerate(row):
                if cell:
                    rect = pygame.Rect(
                        game_area_left + (self.x + x) * GRID_SIZE,
                        game_area_top + (self.y + y) * GRID_SIZE,
                        GRID_SIZE, GRID_SIZE
                    )
                    pygame.draw.rect(surface, self.color, rect)
                    pygame.draw.rect(surface, WHITE, rect, 1)
                    
                    # 添加内部高光效果
                    highlight = pygame.Rect(
                        game_area_left + (self.x + x) * GRID_SIZE + 2,
                        game_area_top + (self.y + y) * GRID_SIZE + 2,
                        GRID_SIZE - 4, GRID_SIZE - 4
                    )
                    pygame.draw.rect(surface, self.color, highlight)
                    pygame.draw.rect(surface, WHITE, highlight, 1)

class Game:
    def __init__(self):
        self.board = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
        self.current_piece = Tetromino()
        self.next_piece = Tetromino()
        self.game_over = False
        self.score = 0
        self.level = 1
        self.lines_cleared = 0
        self.fall_speed = 0.5  # 初始下落速度(秒)
        self.last_fall_time = time.time()
    
    def draw_board(self, surface):
        # 绘制游戏区域背景
        pygame.draw.rect(surface, DARK_GRAY, (
            game_area_left, 
            game_area_top, 
            GRID_WIDTH * GRID_SIZE, 
            GRID_HEIGHT * GRID_SIZE
        ))
        
        # 绘制网格
        for x in range(GRID_WIDTH + 1):
            pygame.draw.line(
                surface, GRAY, 
                (game_area_left + x * GRID_SIZE, game_area_top),
                (game_area_left + x * GRID_SIZE, game_area_top + GRID_HEIGHT * GRID_SIZE)
            )
        for y in range(GRID_HEIGHT + 1):
            pygame.draw.line(
                surface, GRAY, 
                (game_area_left, game_area_top + y * GRID_SIZE),
                (game_area_left + GRID_WIDTH * GRID_SIZE, game_area_top + y * GRID_SIZE)
            )
        
        # 绘制已落下的方块
        for y, row in enumerate(self.board):
            for x, cell in enumerate(row):
                if cell:
                    color_idx = cell - 1
                    rect = pygame.Rect(
                        game_area_left + x * GRID_SIZE,
                        game_area_top + y * GRID_SIZE,
                        GRID_SIZE, GRID_SIZE
                    )
                    pygame.draw.rect(surface, COLORS[color_idx], rect)
                    pygame.draw.rect(surface, WHITE, rect, 1)
                    
                    # 添加内部高光效果
                    highlight = pygame.Rect(
                        game_area_left + x * GRID_SIZE + 2,
                        game_area_top + y * GRID_SIZE + 2,
                        GRID_SIZE - 4, GRID_SIZE - 4
                    )
                    pygame.draw.rect(surface, COLORS[color_idx], highlight)
                    pygame.draw.rect(surface, WHITE, highlight, 1)
    
    def draw_sidebar(self, surface):
        sidebar_left = game_area_left + GRID_WIDTH * GRID_SIZE + 20
        
        # 绘制侧边栏背景
        pygame.draw.rect(surface, DARK_GRAY, (
            sidebar_left - 10, 
            game_area_top - 10, 
            SIDEBAR_WIDTH, 
            GRID_HEIGHT * GRID_SIZE + 20
        ))
        
        # 绘制下一个方块预览标题
        font = get_font(28)
        next_text = font.render("下一个方块:", True, YELLOW)
        surface.blit(next_text, (sidebar_left, game_area_top))
        
        # 绘制下一个方块预览区域背景
        preview_bg = pygame.Rect(
            sidebar_left - 5, 
            game_area_top + 40, 
            120, 
            120
        )
        pygame.draw.rect(surface, BLACK, preview_bg)
        pygame.draw.rect(surface, WHITE, preview_bg, 2)
        
        # 绘制下一个方块
        next_left = sidebar_left + 30
        next_top = game_area_top + 70
        for y, row in enumerate(self.next_piece.shape):
            for x, cell in enumerate(row):
                if cell:
                    rect = pygame.Rect(
                        next_left + x * GRID_SIZE,
                        next_top + y * GRID_SIZE,
                        GRID_SIZE, GRID_SIZE
                    )
                    pygame.draw.rect(surface, self.next_piece.color, rect)
                    pygame.draw.rect(surface, WHITE, rect, 1)
        
        # 绘制分数和等级
        score_text = font.render(f"分数: {self.score}", True, WHITE)
        level_text = font.render(f"等级: {self.level}", True, WHITE)
        lines_text = font.render(f"消除行数: {self.lines_cleared}", True, WHITE)
        
        surface.blit(score_text, (sidebar_left, next_top + 150))
        surface.blit(level_text, (sidebar_left, next_top + 190))
        surface.blit(lines_text, (sidebar_left, next_top + 230))
        
        # 绘制游戏说明
        controls_font = get_font(20)
        controls = [
            "游戏控制:",
            "← → : 左右移动",
            "↑ : 旋转方块",
            "↓ : 加速下落",
            "空格 : 直接落下",
            "P : 暂停游戏",
            "R : 重新开始"
        ]
        
        for i, text in enumerate(controls):
            color = YELLOW if i == 0 else WHITE
            control_text = controls_font.render(text, True, color)
            surface.blit(control_text, (sidebar_left, next_top + 300 + i * 30))
    
    def draw_game_over(self, surface):
        # 半透明覆盖层
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 180))
        surface.blit(overlay, (0, 0))
        
        font_large = get_font(72)
        font_small = get_font(36)
        
        game_over_text = font_large.render("游戏结束!", True, RED)
        text_rect = game_over_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 50))
        surface.blit(game_over_text, text_rect)
        
        score_text = font_small.render(f"最终分数: {self.score}", True, YELLOW)
        score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 20))
        surface.blit(score_text, score_rect)
        
        restart_text = font_small.render("按 R 键重新开始", True, WHITE)
        restart_rect = restart_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 80))
        surface.blit(restart_text, restart_rect)
    
    def check_collision(self, shape, x, y):
        for row_idx, row in enumerate(shape):
            for col_idx, cell in enumerate(row):
                if cell:
                    # 检查是否超出边界
                    if (x + col_idx < 0 or x + col_idx >= GRID_WIDTH or 
                        y + row_idx >= GRID_HEIGHT):
                        return True
                    # 检查是否与已有方块重叠
                    if y + row_idx >= 0 and self.board[y + row_idx][x + col_idx]:
                        return True
        return False
    
    def merge_piece(self):
        for y, row in enumerate(self.current_piece.shape):
            for x, cell in enumerate(row):
                if cell and self.current_piece.y + y >= 0:
                    self.board[self.current_piece.y + y][self.current_piece.x + x] = self.current_piece.shape_idx + 1
    
    def clear_lines(self):
        lines_to_clear = []
        for y, row in enumerate(self.board):
            if all(row):
                lines_to_clear.append(y)
        
        for line in lines_to_clear:
            del self.board[line]
            self.board.insert(0, [0 for _ in range(GRID_WIDTH)])
        
        # 更新分数
        if lines_to_clear:
            self.lines_cleared += len(lines_to_clear)
            self.score += (1, 2, 5, 10)[min(len(lines_to_clear)-1, 3)] * 100 * self.level
            self.level = self.lines_cleared // 10 + 1
            self.fall_speed = max(0.05, 0.5 - (self.level - 1) * 0.05)
    
    def move(self, dx, dy):
        if not self.check_collision(self.current_piece.shape, 
                                   self.current_piece.x + dx, 
                                   self.current_piece.y + dy):
            self.current_piece.x += dx
            self.current_piece.y += dy
            return True
        return False
    
    def rotate_piece(self):
        rotated = self.current_piece.rotate()
        if not self.check_collision(rotated, self.current_piece.x, self.current_piece.y):
            self.current_piece.shape = rotated
            return True
        return False
    
    def drop_piece(self):
        while self.move(0, 1):
            pass
        self.merge_piece()
        self.clear_lines()
        self.current_piece = self.next_piece
        self.next_piece = Tetromino()
        
        # 检查游戏是否结束
        if self.check_collision(self.current_piece.shape, self.current_piece.x, self.current_piece.y):
            self.game_over = True
    
    def update(self):
        current_time = time.time()
        if current_time - self.last_fall_time > self.fall_speed:
            if not self.move(0, 1):
                self.merge_piece()
                self.clear_lines()
                self.current_piece = self.next_piece
                self.next_piece = Tetromino()
                
                # 检查游戏是否结束
                if self.check_collision(self.current_piece.shape, self.current_piece.x, self.current_piece.y):
                    self.game_over = True
            
            self.last_fall_time = current_time
    
    def reset(self):
        self.__init__()

def main():
    game = Game()
    clock = pygame.time.Clock()
    paused = False
    
    # 游戏背景
    background = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
    background.fill((40, 40, 60))
    
    # 添加一些装饰性网格
    for x in range(0, SCREEN_WIDTH, 40):
        pygame.draw.line(background, (50, 50, 80), (x, 0), (x, SCREEN_HEIGHT), 1)
    for y in range(0, SCREEN_HEIGHT, 40):
        pygame.draw.line(background, (50, 50, 80), (0, y), (SCREEN_WIDTH, y), 1)
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    game.reset()
                    paused = False
                
                if game.game_over:
                    continue
                
                if event.key == pygame.K_p:
                    paused = not paused
                
                if not paused:
                    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:
                        game.move(0, 1)
                    elif event.key == pygame.K_UP:
                        game.rotate_piece()
                    elif event.key == pygame.K_SPACE:
                        game.drop_piece()
        
        # 绘制背景
        screen.blit(background, (0, 0))
        
        if not paused and not game.game_over:
            game.update()
        
        # 绘制游戏元素
        game.draw_board(screen)
        game.current_piece.draw(screen)
        game.draw_sidebar(screen)
        
        if game.game_over:
            game.draw_game_over(screen)
        
        if paused and not game.game_over:
            # 半透明覆盖层
            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 150))
            screen.blit(overlay, (0, 0))
            
            font = get_font(72)
            pause_text = font.render("游戏暂停", True, YELLOW)
            text_rect = pause_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2))
            screen.blit(pause_text, text_rect)
            
            font_small = get_font(28)
            continue_text = font_small.render("按 P 键继续游戏", True, WHITE)
            continue_rect = continue_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 80))
            screen.blit(continue_text, continue_rect)
        
        pygame.display.flip()
        clock.tick(60)
    
    pygame.quit()

if __name__ == "__main__":
    main()