import pygame import random # 初始化Pygame pygame.init() # 游戏常量设置 BLOCK_SIZE = 30 # 单个方块的大小(像素) GRID_WIDTH = 10 # 游戏网格宽度(方块数) GRID_HEIGHT = 20 # 游戏网格高度(方块数) WINDOW_WIDTH = BLOCK_SIZE * GRID_WIDTH + 200 # 窗口宽度(右侧留200像素显示信息) WINDOW_HEIGHT = BLOCK_SIZE * GRID_HEIGHT # 窗口高度 # 颜色定义(RGB) BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (128, 128, 128) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 255, 0) CYAN = (0, 255, 255) MAGENTA = (255, 0, 255) ORANGE = (255, 165, 0) # 俄罗斯方块7种形状(每种形状的坐标偏移,基于中心) SHAPES = [ [[1, 1, 1, 1]], # I型 [[1, 1], [1, 1]], # O型 [[1, 1, 1], [0, 1, 0]], # T型 [[1, 1, 1], [1, 0, 0]], # L型 [[1, 1, 1], [0, 0, 1]], # J型 [[0, 1, 1], [1, 1, 0]], # S型 [[1, 1, 0], [0, 1, 1]] # Z型 ] # 形状对应的颜色 SHAPE_COLORS = [CYAN, YELLOW, MAGENTA, ORANGE, BLUE, GREEN, RED] # 初始化游戏窗口 screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("俄罗斯方块") # 时钟(控制游戏帧率) clock = pygame.time.Clock() FPS = 60 # 游戏网格初始化(二维列表,0表示空,非0表示对应颜色的方块) grid = [[BLACK for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)] def create_new_piece(): """生成一个新的俄罗斯方块(随机形状和颜色)""" shape_idx = random.randint(0, len(SHAPES) - 1) piece_shape = SHAPES[shape_idx] piece_color = SHAPE_COLORS[shape_idx] # 初始位置:水平居中,垂直顶部 x = GRID_WIDTH // 2 - len(piece_shape[0]) // 2 y = 0 return { 'shape': piece_shape, 'color': piece_color, 'x': x, 'y': y } def check_collision(piece, grid, dx=0, dy=0, rotated_shape=None): """ 检查方块是否碰撞(边界或已有方块) :param piece: 当前方块 :param grid: 游戏网格 :param dx: 水平偏移量 :param dy: 垂直偏移量 :param rotated_shape: 旋转后的形状(若为None则使用原形状) :return: True表示碰撞,False表示无碰撞 """ shape = rotated_shape if rotated_shape is not None else piece['shape'] piece_x = piece['x'] + dx piece_y = piece['y'] + dy for row_idx, row in enumerate(shape): for col_idx, cell in enumerate(row): if cell: # 只检查非空的方块部分 # 计算当前方块在网格中的绝对坐标 abs_x = piece_x + col_idx abs_y = piece_y + row_idx # 检查是否超出左右边界 if abs_x < 0 or abs_x >= GRID_WIDTH: return True # 检查是否超出下边界 if abs_y >= GRID_HEIGHT: return True # 检查是否与已有方块重叠(忽略上方超出边界的情况,因为方块是从顶部下落) if abs_y >= 0 and grid[abs_y][abs_x] != BLACK: return True return False def rotate_piece(piece): """旋转方块(顺时针旋转90度)""" shape = piece['shape'] # 旋转逻辑:将原形状的列反向作为新形状的行 rotated_shape = list(zip(*shape[::-1])) # 转换为二维列表(zip返回的是元组) rotated_shape = [list(row) for row in rotated_shape] # 检查旋转后是否碰撞,若不碰撞则返回旋转后的形状,否则返回原形状 if not check_collision(piece, grid, rotated_shape=rotated_shape): return rotated_shape return shape def lock_piece(piece, grid): """将方块锁定到网格中(方块下落到底或碰撞后固定)""" shape = piece['shape'] piece_x = piece['x'] piece_y = piece['y'] for row_idx, row in enumerate(shape): for col_idx, cell in enumerate(row): if cell: abs_x = piece_x + col_idx abs_y = piece_y + row_idx if abs_y >= 0: # 忽略上方超出边界的部分(方块还未完全进入网格) grid[abs_y][abs_x] = piece['color'] def clear_full_lines(grid): """清除网格中填满的行,并返回消除的行数(用于计分)""" full_lines = [] # 先找出所有填满的行 for row_idx, row in enumerate(grid): if all(cell != BLACK for cell in row): full_lines.append(row_idx) # 清除填满的行,并在顶部补充空行 for line_idx in full_lines: # 删除填满的行 del grid[line_idx] # 在顶部插入一行空方块(黑色) grid.insert(0, [BLACK for _ in range(GRID_WIDTH)]) return len(full_lines) def draw_grid(screen, grid): """绘制游戏网格""" for row_idx, row in enumerate(grid): for col_idx, cell in enumerate(row): # 绘制方块 rect = pygame.Rect( col_idx * BLOCK_SIZE, row_idx * BLOCK_SIZE, BLOCK_SIZE - 1, # 减1是为了显示网格线(黑色间隙) BLOCK_SIZE - 1 ) pygame.draw.rect(screen, cell, rect) def draw_piece(screen, piece): """绘制当前下落的方块""" shape = piece['shape'] piece_x = piece['x'] piece_y = piece['y'] for row_idx, row in enumerate(shape): for col_idx, cell in enumerate(row): if cell: # 计算方块在窗口中的像素坐标 pixel_x = (piece_x + col_idx) * BLOCK_SIZE pixel_y = (piece_y + row_idx) * BLOCK_SIZE rect = pygame.Rect( pixel_x, pixel_y, BLOCK_SIZE - 1, BLOCK_SIZE - 1 ) pygame.draw.rect(screen, piece['color'], rect) def draw_info(screen, score, level): """绘制右侧信息(分数、等级)""" font = pygame.font.SysFont(None, 36) # 绘制分数文本 score_text = font.render(f"分数: {score}", True, WHITE) screen.blit(score_text, (GRID_WIDTH * BLOCK_SIZE + 10, 50)) # 绘制等级文本 level_text = font.render(f"等级: {level}", True, WHITE) screen.blit(level_text, (GRID_WIDTH * BLOCK_SIZE + 10, 100)) # 绘制操作提示 hint_font = pygame.font.SysFont(None, 24) hint1 = hint_font.render("← →: 左右移动", True, GRAY) hint2 = hint_font.render("↑: 旋转", True, GRAY) hint3 = hint_font.render("↓: 快速下落", True, GRAY) hint4 = hint_font.render("空格: 直接落地", True, GRAY) screen.blit(hint1, (GRID_WIDTH * BLOCK_SIZE + 10, 200)) screen.blit(hint2, (GRID_WIDTH * BLOCK_SIZE + 10, 250)) screen.blit(hint3, (GRID_WIDTH * BLOCK_SIZE + 10, 300)) screen.blit(hint4, (GRID_WIDTH * BLOCK_SIZE + 10, 350)) def game_over_check(grid): """检查游戏是否结束(方块堆积到顶部)""" # 检查第一行是否有非空方块 return any(cell != BLACK for cell in grid[0]) def main(): """游戏主循环""" current_piece = create_new_piece() # 当前下落的方块 fall_time = 0 # 记录下落计时 fall_speed = 0.8 # 方块下落速度(秒/格) score = 0 # 玩家分数 level = 1 # 游戏等级 lines_cleared = 0 # 累计消除的行数 running = True while running: screen.fill(BLACK) # 清空屏幕 delta_time = clock.tick(FPS) / 1000 # 转换为秒 fall_time += delta_time # 事件处理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: # 左右移动 if event.key == pygame.K_LEFT: if not check_collision(current_piece, grid, dx=-1): current_piece['x'] -= 1 if event.key == pygame.K_RIGHT: if not check_collision(current_piece, grid, dx=1): current_piece['x'] += 1 # 旋转 if event.key == pygame.K_UP: current_piece['shape'] = rotate_piece(current_piece) # 快速下落 if event.key == pygame.K_DOWN: if not check_collision(current_piece, grid, dy=1): current_piece['y'] += 1 score += 1 # 快速下落加分 # 直接落地 if event.key == pygame.K_SPACE: while not check_collision(current_piece, grid, dy=1): current_piece['y'] += 1 score += 2 # 直接落地加分 lock_piece(current_piece, grid) # 消除满行 lines = clear_full_lines(grid) lines_cleared += lines score += lines * 100 * level # 消除行数越多,加分越多 # 升级逻辑(每消除10行升一级) if lines_cleared // 10 >= level: level += 1 fall_speed = max(0.2, fall_speed - 0.1) # 升级后加快下落速度 # 生成新方块 current_piece = create_new_piece() # 检查游戏是否结束 if game_over_check(grid): running = False # 自动下落逻辑 if fall_time >= fall_speed: if not check_collision(current_piece, grid, dy=1): current_piece['y'] += 1 else: # 碰撞后锁定方块 lock_piece(current_piece, grid) # 消除满行 lines = clear_full_lines(grid) lines_cleared += lines score += lines * 100 * level # 升级 if lines_cleared // 10 >= level: level += 1 fall_speed = max(0.2, fall_speed - 0.1) # 生成新方块 current_piece = create_new_piece() # 检查游戏结束 if game_over_check(grid): running = False fall_time = 0 # 绘制游戏元素 draw_grid(screen, grid) draw_piece(screen, current_piece) draw_info(screen, score, level) # 更新屏幕 pygame.display.flip() # 游戏结束提示 font = pygame.font.SysFont(None, 48) game_over_text = font.render("游戏结束!", True, RED) final_score_text = font.render(f"最终分数: {score}", True, WHITE) screen.blit(game_over_text, (WINDOW_WIDTH // 2 - game_over_text.get_width() // 2, WINDOW_HEIGHT // 2 - 50)) screen.blit(final_score_text, (WINDOW_WIDTH // 2 - final_score_text.get_width() // 2, WINDOW_HEIGHT // 2 + 10)) pygame.display.flip() # 延迟3秒后退出 pygame.time.delay(3000) pygame.quit() if __name__ == "__main__": main()