前言
本来想再发几个小游戏后在整理一下流程的,但是今天试了一下这个俄罗斯方块的游戏结果发现本来修改的好好的的,结果后面越改越乱,前面的版本也没保存,根据AI修改他是在几个版本改来改去,想着要求还是不能这么高。我是采用DeepSeek+Pycharm+Lingma来操作的。
DeepSeek部分
对于DeepSeek我是直接询问"Python可以设计哪些简单的小游戏 ",然后根据推荐的游戏挑选直接感兴趣的,比如可以询问"用Python代码实现俄罗斯方块小游戏",于是就生成了以下的代码,因为我是选择了联网搜索,所以肯定会借鉴其他博主代码:
python
import pygame
import random
# 初始化 Pygame
pygame.init()
# 游戏窗口尺寸和颜色
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
COLORS = [
(0, 255, 255), # I 型(青色)
(255, 165, 0), # L 型(橙色)
(0, 0, 255), # J 型(蓝色)
(255, 255, 0), # O 型(黄色)
(0, 255, 0), # S 型(绿色)
(255, 0, 255), # T 型(紫色)
(255, 0, 0) # Z 型(红色)
]
# 方块形状定义(4x4 矩阵)
SHAPES = [
[[1, 1, 1, 1]], # I 型
[[1, 0, 0], [1, 1, 1]], # L 型
[[0, 0, 1], [1, 1, 1]], # J 型
[[1, 1], [1, 1]], # O 型
[[0, 1, 1], [1, 1, 0]], # S 型
[[0, 1, 0], [1, 1, 1]], # T 型
[[1, 1, 0], [0, 1, 1]] # Z 型
]
# 初始化窗口
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
# 游戏区域矩阵(0表示空)
game_area = [[0] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
# 当前方块和位置
current_shape = None
current_pos = [0, 0]
current_color = None
score = 0
def new_shape():
"""生成新方块"""
global current_shape, current_pos, current_color
current_shape = random.choice(SHAPES)
current_color = random.choice(COLORS)
current_pos = [GRID_WIDTH // 2 - len(current_shape[0]) // 2, 0]
def check_collision(shape, pos):
"""检查方块是否与边界或已有方块碰撞"""
for y, row in enumerate(shape):
for x, cell in enumerate(row):
if cell:
px = pos[0] + x
py = pos[1] + y
if px < 0 or px >= GRID_WIDTH or py >= GRID_HEIGHT:
return True
if py >= 0 and game_area[py][px]:
return True
return False
def merge_shape():
"""将当前方块合并到游戏区域"""
for y, row in enumerate(current_shape):
for x, cell in enumerate(row):
if cell:
game_area[current_pos[1] + y][current_pos[0] + x] = current_color
def clear_lines():
"""消除满行并更新分数"""
global score
lines_to_clear = [i for i, row in enumerate(game_area) if all(cell != 0 for cell in row)]
for i in lines_to_clear:
del game_area[i]
game_area.insert(0, [0] * GRID_WIDTH)
score += len(lines_to_clear) * 100
def rotate_shape():
"""旋转当前方块(顺时针)"""
global current_shape
rotated = list(zip(*current_shape[::-1]))
if not check_collision(rotated, current_pos):
current_shape = rotated
def draw_grid():
"""绘制游戏区域"""
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
color = game_area[y][x]
if color:
pygame.draw.rect(screen, color, (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE-1, GRID_SIZE-1))
def draw_current_shape():
"""绘制当前下落的方块"""
for y, row in enumerate(current_shape):
for x, cell in enumerate(row):
if cell:
px = (current_pos[0] + x) * GRID_SIZE
py = (current_pos[1] + y) * GRID_SIZE
pygame.draw.rect(screen, current_color, (px, py, GRID_SIZE-1, GRID_SIZE-1))
def game_loop():
clock = pygame.time.Clock()
fall_time = 0
fall_speed = 500 # 下落间隔(毫秒)
new_shape()
running = True
while running:
screen.fill(BLACK)
current_time = pygame.time.get_ticks()
# 处理输入事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
current_pos[0] -= 1
if check_collision(current_shape, current_pos):
current_pos[0] += 1
if event.key == pygame.K_RIGHT:
current_pos[0] += 1
if check_collision(current_shape, current_pos):
current_pos[0] -= 1
if event.key == pygame.K_DOWN:
current_pos[1] += 1
if check_collision(current_shape, current_pos):
current_pos[1] -= 1
if event.key == pygame.K_UP:
rotate_shape()
if event.key == pygame.K_SPACE: # 快速下落
while not check_collision(current_shape, current_pos):
current_pos[1] += 1
current_pos[1] -= 1
# 自动下落
if current_time - fall_time > fall_speed:
current_pos[1] += 1
if check_collision(current_shape, current_pos):
current_pos[1] -= 1
merge_shape()
clear_lines()
new_shape()
if check_collision(current_shape, current_pos):
running = False # 游戏结束
fall_time = current_time
# 绘制界面
draw_grid()
draw_current_shape()
pygame.display.update()
clock.tick(60)
# 游戏结束提示
font = pygame.font.Font(None, 74)
text = font.render(f'Game Over! Score: {score}', True, WHITE)
screen.blit(text, (WINDOW_WIDTH//2 - 200, WINDOW_HEIGHT//2 - 50))
pygame.display.update()
pygame.time.wait(3000)
if __name__ == "__main__":
game_loop()
pygame.quit()
如果直接运行当然会存在一些的问题,比如这个右侧是空白的,但是方块无法移动过去。

Pycharm+Lingma部分

我主要是用的这个插件进行修改代码,一方面可以让他对代码进行优化,另一方面还能询问可以增加什么特色的功能。
比如我想增加历史最高分的模块以及右侧可以用来显示下一个方块的相关信息,本来一切都向着好方向修改,结果却越改越乱,下面的代码用DeepSeek应该是修改不了,所以建议新增功能还是一个个功能的增加测试,不然不清楚哪里有问题,还需要慢慢调试,对于要修改的地方可以通过Ctrl+F来搜索进行快速定位,对于代码报错或者警告的信息也可以询问Lingma进行修改,甚至可以进行直接替换(但是感觉不保险,怕乱改)。
以下的代码存在着很多问题,但是又不想删除(毕竟改了一下午),所以也放出来的,有感兴趣的大佬可以看一下,目前存在的问题是游戏无法结束,他触碰到上底后会在下底增加方块,还有这个生存模式根据DeepSeek的操作应该是要增加障碍,还有这个不同颜色方块的功能,应该就是突然增加太多了导致代码出问题了。
python
# -*- coding: utf-8 -*-
import pygame
import random
import os
from typing import List, Tuple, Optional
# 初始化 Pygame
pygame.init()
os.environ['PYGAME_FREETYPE'] = '1' # 启用更好字体渲染
# 新增最高分文件路径
HIGHSCORE_FILE = "tetris_highscore.txt"
# 在文件开头添加字体常量
FONT_CONFIG = {
"simhei": {
"path": "simhei.ttf",
"sizes": {"title": 36, "subtitle": 28, "normal": 24}
},
"backup": {
"name": "arial",
"sizes": {"title": 36, "subtitle": 28, "normal": 24}
}
}
# 修改字体加载函数
def load_font(font_type: str, size_type: str):
"""统一字体加载函数(优化版)"""
# 直接使用系统字体名称
try:
return pygame.font.SysFont("simhei", FONT_CONFIG["simhei"]["sizes"][size_type])
except Exception as e:
print(f"系统字体加载失败: {e}")
try:
return pygame.font.Font(None, FONT_CONFIG["backup"]["sizes"][size_type])
except:
return pygame.font.SysFont(FONT_CONFIG["backup"]["name"],
FONT_CONFIG["backup"]["sizes"][size_type])
def load_highscore():
"""加载历史最高分"""
try:
if os.path.exists(HIGHSCORE_FILE):
with open(HIGHSCORE_FILE, 'r') as f:
return int(f.read().strip() or 0)
return 0
except Exception as e:
print(f"读取最高分失败: {e}")
return 0
def save_highscore(score):
"""保存最高分"""
with open(HIGHSCORE_FILE, 'w') as f:
f.write(str(score))
# 游戏窗口尺寸和颜色
WINDOW_HEIGHT = 600
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
DARK_GRAY = (40, 40, 40) # 游戏区域背景色
COLORS = [
(0, 255, 255), # I 型(青色)
(255, 165, 0), # L 型(橙色)
(0, 0, 255), # J 型(蓝色)
(255, 255, 0), # O 型(黄色)
(0, 255, 0), # S 型(绿色)
(255, 0, 255), # T 型(紫色)
(255, 0, 0) # Z 型(红色)
]
# 特殊方块颜色
SPECIAL_COLORS = [
(255, 100, 100), # 爆炸红:消除整行
(100, 255, 100), # 加速绿:下落速度x2
(100, 100, 255) # 护盾蓝:免疫下一次碰撞
]
COLORS += SPECIAL_COLORS
# 方块形状定义(4x4 矩阵)
SHAPES = [
[[1, 1, 1, 1]], # I 型
[[1, 0, 0], [1, 1, 1]], # L 型
[[0, 0, 1], [1, 1, 1]], # J 型
[[1, 1], [1, 1]], # O 型
[[0, 1, 1], [1, 1, 0]], # S 型
[[0, 1, 0], [1, 1, 1]], # T 型
[[1, 1, 0], [0, 1, 1]] # Z 型
]
# 新增游戏状态常量
GAME_STATES = {
'START': 0,
'MODE_SELECT': 1, # 新增模式选择状态
'PLAYING': 2,
'GAME_OVER': 3
}
# 修改游戏界面布局参数(新增右侧信息面板)
INFO_PANEL_WIDTH = 200 # 右侧信息面板宽度
GAME_PANEL_WIDTH = GRID_WIDTH * GRID_SIZE # 300
# 游戏界面宽度
WINDOW_WIDTH = GAME_PANEL_WIDTH + INFO_PANEL_WIDTH
# 初始化窗口
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
# 游戏区域矩阵(0表示空)
game_area = [[None] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
# 当前方块和位置
current_shape: Optional[List[List[int]]] = None
current_pos = [0, 0]
current_color: Optional[Tuple[int, int, int]] = None
# 在全局变量声明区域添加:
next_shape: Optional[List[List[int]]] = None
next_color: Optional[Tuple[int, int, int]] = None
score = 0
# 新增全局变量
game_mode = 0 # 0-经典模式 1-生存模式
obstacle_timer = 0
special_effect = None
effect_duration = 0
# 修改new_shape函数(添加下一个方块生成)
def new_shape():
global current_shape, current_pos, current_color, next_shape, next_color
if not hasattr(new_shape, "_initialized"):
current_shape = random.choice(SHAPES)
current_color = random.choice(COLORS)
next_shape = random.choice(SHAPES)
next_color = random.choice(COLORS)
new_shape._initialized = True
else:
current_shape = next_shape
current_color = next_color
next_shape = random.choice(SHAPES)
next_color = random.choice(COLORS)
if random.random() < 0.2 and current_color not in SPECIAL_COLORS:
current_color = random.choice(SPECIAL_COLORS)
# 修复点1:计算方块总高度
shape_height = len(current_shape)
# 修复点2:调整生成位置计算逻辑 ▼
base_offset = -shape_height # 确保整个方块在屏幕外
# 生存模式额外上移(原逻辑错误导致坐标计算为负值不够)
if game_mode == 1:
base_offset = -shape_height - 2 # 调整为-2格缓冲
current_pos = [
GRID_WIDTH // 2 - len(current_shape[0]) // 2,
base_offset # Y坐标修正
]
def check_collision(shape, pos):
"""统一碰撞检测(移除模式判断)"""
# 修改点1:block → shape
for y in range(len(shape)): # ← 修改这里
# 修改点2:block.shape → shape
for x in range(len(shape[y])): # ← 修改这里
# 修改点3:cell → shape[y][x]
if shape[y][x]: # ← 修改这里(原cell未定义)
px = pos[0] + x
py = pos[1] + y
# 边界检测
if px < 0 or px >= GRID_WIDTH:
return True
if py >= GRID_HEIGHT: # 底部边界
return True
# 有效区域检测(包含顶部边界)
if py >= 0 and game_area[py][px] is not None:
return True
return False
def merge_shape():
"""将当前方块合并到游戏区域"""
for y, row in enumerate(current_shape):
for x, cell in enumerate(row):
if cell:
game_area[current_pos[1] + y][current_pos[0] + x] = current_color
def clear_lines():
"""消除满行并更新分数"""
global score, fall_speed, game_area
lines_to_clear = [i for i, row in enumerate(game_area) if all(cell is not None for cell in row)]
# 删除并补充新行(保持总行数不变)
for _ in lines_to_clear:
del game_area[0] # 删除顶部行(原逻辑错误:删除指定索引行)
game_area.append([None] * GRID_WIDTH) # 在底部补充新行
score += len(lines_to_clear) * 100
fall_speed = max(50, 500 - (score // 100) * 50) # 每得100分加快50ms
def rotate_shape():
"""旋转当前方块(顺时针)"""
global current_shape
rotated = [list(row) for row in zip(*current_shape[::-1])] # 转换为列表的列表
if not check_collision(rotated, current_pos):
current_shape = rotated
def draw_grid():
"""绘制游戏区域(增加维度保护)"""
assert len(game_area) == GRID_HEIGHT, \
f"游戏区域行数异常:{len(game_area)}(应保持{GRID_HEIGHT}行)"
assert all(len(row) == GRID_WIDTH for row in game_area), \
"游戏区域列数异常"
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
color = game_area[y][x]
# 加颜色有效性验证(必须为三元组且数值合法)
if color is not None and isinstance(color, tuple) and len(color) == 3:
pygame.draw.rect(screen, color, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
def draw_current_shape():
"""绘制当前下落的方块"""
for y, row in enumerate(current_shape):
for x, cell in enumerate(row):
if cell:
px = (current_pos[0] + x) * GRID_SIZE
py = (current_pos[1] + y) * GRID_SIZE
pygame.draw.rect(screen, current_color, (px, py, GRID_SIZE - 1, GRID_SIZE - 1))
# 新增右侧信息面板绘制函数
def draw_info_panel(score, highscore, next_shape, next_color, mode, effect):
# 绘制右侧面板背景
pygame.draw.rect(screen, BLACK, (GAME_PANEL_WIDTH, 0, INFO_PANEL_WIDTH, WINDOW_HEIGHT))
# 统一字体加载(修改关键部分)
main_font = load_font("simhei", "normal") # 使用预设的24号字
small_font = load_font("simhei", "subtitle") # 使用28号字
# 修改显示文本为中文
# 显示下一个方块标题(修正坐标计算)
title_text = main_font.render("下一个方块", True, WHITE)
title_x = GAME_PANEL_WIDTH + (INFO_PANEL_WIDTH - title_text.get_width()) // 2
screen.blit(title_text, (title_x, 30)) # 原100改为30避免重叠
# 分数显示修正(使用统一字体)
score_text = main_font.render(f"当前分数:{score}", True, WHITE)
screen.blit(score_text, (GAME_PANEL_WIDTH + 10, 300)) # 原使用Font(None)处
# 显示最高分
highscore_text = main_font.render(f"最高纪录:{highscore}", True, WHITE)
screen.blit(highscore_text, (GAME_PANEL_WIDTH + 10, 350))
# 计算预览方块位置(居中显示)
preview_size = len(next_shape[0]) * GRID_SIZE
start_x = GAME_PANEL_WIDTH + (INFO_PANEL_WIDTH - preview_size) // 2
start_y = 100
# 绘制预览方块
for y, row in enumerate(next_shape):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, next_color, (
start_x + x * GRID_SIZE,
start_y + y * GRID_SIZE,
GRID_SIZE - 1, GRID_SIZE - 1
))
# 添加模式显示
mode_text = main_font.render( # 使用已定义的main_font
f"模式:{'生存' if mode else '经典'}",
True,
(255, 0, 0) if mode else (0, 255, 0)
)
screen.blit(mode_text, (GAME_PANEL_WIDTH + 10, 400))
# 显示当前特效
if effect:
effect_mapping = {'speed': '加速', 'shield': '护盾'}
effect_text = main_font.render( # 使用已定义的main_font
f"特效:{effect_mapping.get(effect, '未知')}",
True,
(255, 255, 0)
)
screen.blit(effect_text, (GAME_PANEL_WIDTH + 10, 450))
# 新增开始界面
def show_start_screen(highscore):
# 添加中文字体支持
try:
title_font = pygame.font.SysFont("SimHei", 36)
text_font = pygame.font.SysFont("SimHei", 24)
except:
try:
title_font = pygame.font.Font("msyh.ttc", 36)
text_font = pygame.font.Font("msyh.ttc", 24)
except:
title_font = pygame.font.Font(None, 36)
text_font = pygame.font.Font(None, 24)
screen.fill(BLACK)
title = title_font.render("俄罗斯方块", True, WHITE)
title_rect = title.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 70))
screen.blit(title, title_rect)
font = pygame.font.Font(None, 32)
prompt = title_font.render("按任意键开始游戏", True, WHITE)
prompt_rect = prompt.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 30))
screen.blit(prompt, prompt_rect)
highscore_text = title_font.render(f"当前最高分: {highscore}", True, (200, 200, 200))
highscore_rect = highscore_text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 90))
screen.blit(highscore_text, highscore_rect)
pygame.display.update()
waiting = True
while waiting:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
return False
if event.type in (pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN):
waiting = False
return True
# 新增模式选择界面(在show_start_screen函数后添加)
def show_mode_menu():
screen.fill(BLACK)
# 加载中文字体的正确方式
font = load_font("simhei", "subtitle") # 28号字
desc_font = load_font("simhei", "normal") # 24号字
# 打印调试信息
print(f"当前使用字体: {font}")
print("可用系统字体:", pygame.font.get_fonts())
# 绘制标题
title = font.render("选择游戏模式", True, (255, 255, 0))
title_rect = title.get_rect(center=(WINDOW_WIDTH // 2, 60))
screen.blit(title, title_rect)
# 模式按钮参数
modes = [
{
"text": "经典模式",
"desc": "标准俄罗斯方块规则",
"key": "1",
"color": (0, 255, 255),
"rect": pygame.Rect(WINDOW_WIDTH // 2 - 180, 120, 360, 60)
},
{
"text": "生存模式",
"desc": "每30秒生成障碍行",
"key": "2",
"color": (255, 0, 0),
"rect": pygame.Rect(WINDOW_WIDTH // 2 - 180, 210, 360, 60)
}
]
# 绘制模式选项
for mode in modes:
# 按钮背景
pygame.draw.rect(screen, (30, 30, 30), mode["rect"], border_radius=10)
# 模式名称
text_surf = font.render(f"{mode['key']}. {mode['text']}", True, mode["color"])
text_rect = text_surf.get_rect(left=mode["rect"].left + 20, centery=mode["rect"].centery - 15)
screen.blit(text_surf, text_rect)
# 模式描述
desc_surf = desc_font.render(mode["desc"], True, (200, 200, 200))
desc_rect = desc_surf.get_rect(left=mode["rect"].left + 20, centery=mode["rect"].centery + 15)
screen.blit(desc_surf, desc_rect)
# 绘制提示文字
prompt = desc_font.render("使用数字键选择 或 鼠标点击选择", True, (150, 150, 150))
prompt_rect = prompt.get_rect(center=(WINDOW_WIDTH // 2, 320))
screen.blit(prompt, prompt_rect)
pygame.display.update()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_1: return 0
if event.key == pygame.K_2: return 1
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
for idx, mode in enumerate(modes):
if mode["rect"].collidepoint(mouse_pos):
return idx # 0或1对应模式
def game_loop():
'''游戏主循环'''
global score, game_area, current_shape, current_pos, current_color, next_shape, next_color
global game_mode, obstacle_timer, special_effect, effect_duration
# 初始化游戏状态
current_state = GAME_STATES['START']
highscore = load_highscore()
running = True
clock = pygame.time.Clock()
fall_time = pygame.time.get_ticks()
fall_speed = 10 # 下落间隔(毫秒)
while running:
current_time = pygame.time.get_ticks()
# 状态处理
if current_state == GAME_STATES['PLAYING']:
assert len(game_area) == GRID_HEIGHT, "游戏区域行数异常"
assert all(len(row) == GRID_WIDTH for row in game_area), "游戏区域列数异常"
if current_state == GAME_STATES['START']:
if not show_start_screen(highscore):
return
current_state = GAME_STATES['MODE_SELECT']
if current_state == GAME_STATES['MODE_SELECT']:
game_mode = show_mode_menu()
game_area = [[None] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
score = 0
if game_mode == 1:
game_area = [[None] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
obstacle_timer = pygame.time.get_ticks()
for _ in range(3):
obstacle_row = [DARK_GRAY if random.random() < 0.3 else None for _ in range(GRID_WIDTH)]
game_area.insert(0, obstacle_row)
del game_area[-1]
if hasattr(new_shape, "_initialized"):
del new_shape._initialized
new_shape()
new_shape()
current_state = GAME_STATES['PLAYING']
# 事件处理
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
current_pos[0] -= 1
if check_collision(current_shape, current_pos):
current_pos[0] += 1
if event.key == pygame.K_RIGHT:
current_pos[0] += 1
if check_collision(current_shape, current_pos):
current_pos[0] -= 1
if event.key == pygame.K_DOWN:
current_pos[1] += 1
if check_collision(current_shape, current_pos):
current_pos[1] -= 1
if event.key == pygame.K_UP:
rotate_shape()
if event.key == pygame.K_SPACE: # 快速下落
while not check_collision(current_shape, current_pos):
current_pos[1] += 1
current_pos[1] -= 1
draw_current_shape()
pygame.display.update()
# 自动下落逻辑
if current_time - fall_time > (fall_speed if special_effect != 'speed' else fall_speed // 2):
current_pos[1] += 1
fall_time = current_time
if check_collision(current_shape, current_pos):
current_pos[1] -= 1
# 经典模式立即合并
if game_mode == 0:
merge_shape()
clear_lines()
new_shape()
# 生存模式特殊处理
else:
if (current_pos[1] + len(current_shape)) > 0: # 底部进入可见区域
merge_shape()
clear_lines()
new_shape()
# 生存模式障碍生成
if game_mode == 1:
if current_time - obstacle_timer > 30000:
new_row = [DARK_GRAY if random.random() < 0.7 else None for _ in range(GRID_WIDTH)]
game_area.insert(0, new_row)
del game_area[-1]
obstacle_timer = current_time
# 统一结束判断
if check_collision(current_shape, current_pos) and special_effect != 'shield':
# 经典模式:触顶即结束
if game_mode == 0 and current_pos[1] < 0:
running = False
# 生存模式:完全进入后碰撞才结束
elif game_mode == 1 and (current_pos[1] + len(current_shape)) >= 0:
running = False
# 特效处理
if special_effect and effect_duration > 0:
effect_duration -= clock.get_time()
if effect_duration <= 0:
special_effect = None
# 绘制界面
screen.fill(DARK_GRAY)
draw_grid()
draw_current_shape()
draw_info_panel(score, highscore, next_shape, next_color, game_mode, special_effect)
pygame.display.update()
clock.tick(60)
# 游戏结束处理
if not running:
if score > highscore:
save_highscore(score)
highscore = score
screen.fill(BLACK)
try:
font = pygame.font.SysFont("simhei", 48)
except:
font = pygame.font.Font(None, 48)
game_over_text = font.render("游戏结束", True, (255, 0, 0))
text_rect = game_over_text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 30))
screen.blit(game_over_text, text_rect)
score_text = font.render(f"最终得分: {score}", True, WHITE)
score_rect = score_text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 30))
screen.blit(score_text, score_rect)
pygame.display.update()
pygame.time.wait(3000)
current_state = GAME_STATES['GAME_OVER']
if current_state == GAME_STATES['GAME_OVER']:
if not show_start_screen(highscore):
running = False
else:
current_state = GAME_STATES['START']
if __name__ == "__main__":
game_loop()
pygame.quit()

修改后的代码
我测试了几次,主要之前出现的问题就是
- 游戏无法正常结束,当方块触碰到上方越界后,方块从最下方更新。
- 方块没有正常清除,当本行方块填满后他整体上移,而不是清除的效果。
- 游戏无法正常开始,方块下移的速度太快,开始游戏后直接结束了。
- 方块预览和实际出现的方块之间没有关系。
修改后游戏具备一下的特色功能:
下一个方块预览
- 在右侧信息面板实时显示即将出现的方块形状与颜色
- 实现方法:通过 draw_info_panel 方法中的预览方块绘制逻辑
速度递增机制
- 得分每增加50分下落速度提升25ms(最大速度50ms)
- 代码体现:self.fall_speed = max(50, 500 - (self.score // 50) * 25)
智能位置修正
- 旋转时若超出边界自动尝试左右平移(rotate_shape 方法)
- 特征:最多平移至边界,若仍碰撞则取消旋转
- 优势:减少无效旋转操作挫败感
本地高分记录,使用 tetris_highscore.txt 文件存储历史最高分 功能点:
- 启动时自动读取 (load_highscore)
- 破纪录时自动保存 (save_highscore)
三维检测体系,check_collision 方法实现:
- 底部越界检测 (py >= GRID_HEIGHT)
- 侧边越界检测 (px < 0 or px >= GRID_WIDTH)
- 方块重叠检测 (game_area[py][px] is not None)
python
# -*- coding: utf-8 -*-
import pygame
import random
import os
# 初始化 Pygame
pygame.init()
os.environ['PYGAME_FREETYPE'] = '1'
# 常量定义
HIGHSCORE_FILE = "tetris_highscore.txt"
WINDOW_HEIGHT = 600
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
INFO_PANEL_WIDTH = 200
GAME_PANEL_WIDTH = GRID_WIDTH * GRID_SIZE
WINDOW_WIDTH = GAME_PANEL_WIDTH + INFO_PANEL_WIDTH
# 颜色定义
COLORS = [
(0, 255, 255), # I型
(255, 165, 0), # L型
(0, 0, 255), # J型
(255, 255, 0), # O型
(0, 255, 0), # S型
(255, 0, 255), # T型
(255, 0, 0) # Z型
]
BLACK = (0, 0, 0)
DARK_GRAY = (40, 40, 40)
WHITE = (255, 255, 255)
# 方块形状定义
SHAPES = [
[[1, 1, 1, 1]],
[[1, 0, 0], [1, 1, 1]],
[[0, 0, 1], [1, 1, 1]],
[[1, 1], [1, 1]],
[[0, 1, 1], [1, 1, 0]],
[[0, 1, 0], [1, 1, 1]],
[[1, 1, 0], [0, 1, 1]]
]
# 初始化窗口
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
def load_font(size):
try:
return pygame.font.SysFont("simhei", size)
except:
return pygame.font.Font(None, size)
def load_highscore():
try:
if os.path.exists(HIGHSCORE_FILE):
with open(HIGHSCORE_FILE, 'r') as f:
return int(f.read().strip() or 0)
return 0
except:
return 0
def save_highscore(score):
with open(HIGHSCORE_FILE, 'w') as f:
f.write(str(score))
class GameState:
def __init__(self):
self.reset()
def reset(self):
self.game_area = [[None] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
self.score = 0
self.highscore = load_highscore()
# 仅初始化next_shape
self.next_shape = random.choice(SHAPES)
self.next_color = random.choice(COLORS)
self.fall_speed = 500
self.running = True
self.initialize_shapes() # 在此方法中生成current_shape
def initialize_shapes(self):
self.current_shape = self.next_shape # 继承next_shape
self.current_color = self.next_color
# 生成新next_shape
self.next_shape = random.choice(SHAPES)
self.next_color = random.choice(COLORS)
self.reset_position()
def reset_position(self):
shape_height = len(self.current_shape)
self.current_pos = [
GRID_WIDTH // 2 - len(self.current_shape[0]) // 2,
-shape_height + 1 # 完全在游戏区域外生成
]
# 如果初始位置就碰撞,说明游戏结束
if self.check_collision(self.current_shape, self.current_pos):
self.game_over()
def check_collision(self, shape, pos):
for y in range(len(shape)):
for x in range(len(shape[y])):
if shape[y][x]:
px = pos[0] + x
py = pos[1] + y
if py >= GRID_HEIGHT:
return True
if px < 0 or px >= GRID_WIDTH:
return True
if py >= 0 and self.game_area[py][px] is not None:
return True
return False
def rotate_shape(self):
original_shape = self.current_shape
original_pos = self.current_pos.copy()
# 尝试旋转
rotated = [list(row) for row in zip(*original_shape[::-1])]
self.current_shape = rotated
# 旋转后位置修正
while self.check_collision(self.current_shape, self.current_pos):
if self.current_pos[0] < 0:
self.current_pos[0] += 1 # 右移
else:
self.current_pos[0] -= 1 # 左移
if self.current_pos[0] < 0 or self.current_pos[0] >= GRID_WIDTH:
self.current_shape = original_shape # 还原形状
self.current_pos = original_pos
break
def merge_shape(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
px = self.current_pos[0] + x # 计算实际网格坐标
py = self.current_pos[1] + y
# 添加双重边界检查
if 0 <= px < GRID_WIDTH and 0 <= py < GRID_HEIGHT:
self.game_area[py][px] = self.current_color
def clear_lines(self):
# 获取待消除行索引(从下往上检测)
lines_to_clear = [i for i, row in enumerate(self.game_area) if all(cell is not None for cell in row)]
if lines_to_clear:
# 从下往上删除已满行(避免索引错位)
for idx in reversed(lines_to_clear):
del self.game_area[idx]
# 在顶部补充新空行(数量=消除行数)
new_rows = [[None] * GRID_WIDTH for _ in lines_to_clear]
self.game_area = new_rows + self.game_area
# 更新分数和速度
self.score += len(lines_to_clear) * 100
self.fall_speed = max(50, 500 - (self.score // 50) * 25) # 更平缓的速度变化
def draw_grid(self):
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
color = self.game_area[y][x]
# 添加颜色类型安全检查
if isinstance(color, tuple) and len(color) == 3:
pygame.draw.rect(screen, color,
(x * GRID_SIZE, y * GRID_SIZE,
GRID_SIZE - 1, GRID_SIZE - 1))
def draw_current_shape(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
px = (self.current_pos[0] + x) * GRID_SIZE
py = (self.current_pos[1] + y) * GRID_SIZE
pygame.draw.rect(screen, self.current_color,
(px, py, GRID_SIZE - 1, GRID_SIZE - 1))
def draw_info_panel(self):
font = load_font(24)
pygame.draw.rect(screen, BLACK, (GAME_PANEL_WIDTH, 0, INFO_PANEL_WIDTH, WINDOW_HEIGHT))
# 下一个方块
title = font.render("下一个方块", True, WHITE)
screen.blit(title, (GAME_PANEL_WIDTH + 10, 30))
# 预览方块
start_x = GAME_PANEL_WIDTH + (INFO_PANEL_WIDTH - len(self.next_shape[0]) * GRID_SIZE) // 2
start_y = 100
for y, row in enumerate(self.next_shape):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, self.next_color,
(start_x + x * GRID_SIZE, start_y + y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
# 分数显示
score_text = font.render(f"分数: {self.score}", True, WHITE)
screen.blit(score_text, (GAME_PANEL_WIDTH + 10, 300))
highscore_text = font.render(f"最高分: {self.highscore}", True, WHITE)
screen.blit(highscore_text, (GAME_PANEL_WIDTH + 10, 350))
def handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.current_pos[0] -= 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[0] += 1
elif event.key == pygame.K_RIGHT:
self.current_pos[0] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[0] -= 1
elif event.key == pygame.K_DOWN:
self.current_pos[1] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] -= 1
elif event.key == pygame.K_UP:
self.rotate_shape()
elif event.key == pygame.K_SPACE:
while not self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] += 1
self.current_pos[1] -= 1
def game_over(self):
if self.score > self.highscore:
save_highscore(self.score)
screen.fill(BLACK)
font = load_font(48)
text = font.render("游戏结束", True, (255, 0, 0))
text_rect = text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 30))
screen.blit(text, text_rect)
score_text = font.render(f"得分: {self.score}", True, WHITE)
score_rect = score_text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 30))
screen.blit(score_text, score_rect)
pygame.display.update()
pygame.time.wait(3000)
self.running = False
def run(self):
clock = pygame.time.Clock()
fall_time = pygame.time.get_ticks()
while self.running:
current_time = pygame.time.get_ticks()
self.handle_input()
# 自动下落
if current_time - fall_time > self.fall_speed:
self.current_pos[1] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] -= 1
self.merge_shape()
self.clear_lines()
self.initialize_shapes() # 生成新方块
# 游戏结束检测
if self.check_collision(self.current_shape, self.current_pos):
self.game_over()
fall_time = current_time
# 绘制界面
screen.fill(DARK_GRAY)
self.draw_grid()
self.draw_current_shape()
self.draw_info_panel()
pygame.display.update()
clock.tick(60)
if __name__ == "__main__":
game = GameState()
game.run()
pygame.quit()

特殊方块
新增特殊方块的功能还是出现了几个问题:
- 想一次性消除多行,消除后空白的地方方块没有整体下移。
- 方块结构被破坏,出现单列下移的情况,缝隙都被填满了。
- 普通方块无法消除本行。
- 特殊方块的功能和普通方块冲突(比如黑色方块清除的时候只会清除周围的方块就不会清除当前行的方块了)。
结果发现还是不会改,留给有缘人了,目前是触发特殊效果后消除的地方不会下移,若修改成可以下移的话就会破坏原始的结构,不过这样玩还别有一番滋味。(虽然是存在bug,但是感觉玩起来也还不错,目前玩起来有两个显著问题,一个是触发粉色方块后不会整体下移,触发黑色方块和紫色方块后只会按照特殊方块的逻辑生效,并不会清除当前行,大家可以理解成博主"故意"设置的)
处理的顺序是黑色->紫色->粉色->普通,特殊方块全部设置成1*1的形状方便消除,功能如下:
- 黑色 方块 :清除以它为中心的3*3区域
- 紫色 方块 : 清除它所在列的所有方块
- 粉色方块:清除它所在行及其上下一行的所有方块
python
# -*- coding: utf-8 -*-
import pygame
import random
import os
# 初始化 Pygame
pygame.init()
os.environ['PYGAME_FREETYPE'] = '1'
# 常量定义
HIGHSCORE_FILE = "tetris_highscore.txt"
WINDOW_HEIGHT = 600
GRID_SIZE = 30
GRID_WIDTH = 10
GRID_HEIGHT = 20
INFO_PANEL_WIDTH = 200
GAME_PANEL_WIDTH = GRID_WIDTH * GRID_SIZE
WINDOW_WIDTH = GAME_PANEL_WIDTH + INFO_PANEL_WIDTH
# 颜色定义
COLORS = [
(0, 255, 255), # (青色)
(255, 165, 0), # (橙色)
(0, 0, 255), # (蓝色)
(255, 255, 0), # (黄色)
(0, 255, 0), # (绿色)
(255, 0, 255), # (品红色)
(255, 0, 0) # (红色)
]
# 颜色定义(原COLORS列表后添加特殊颜色)
SPECIAL_COLORS = [
(0, 0, 0), # 黑色(中心爆破)
(128, 0, 128), # 紫色(整列消除)
(255, 192, 203) # 粉色(三行消除)
]
BLACK = (0, 0, 0)
DARK_GRAY = (40, 40, 40)
WHITE = (255, 255, 255)
# 方块形状定义
SHAPES = [
[[1, 1, 1, 1]], # I行
[[1, 0, 0], [1, 1, 1]], # L行
[[0, 0, 1], [1, 1, 1]], # J行
[[1, 1], [1, 1]], # S行
[[0, 1, 1], [1, 1, 0]],
[[0, 1, 0], [1, 1, 1]],
[[1, 1, 0], [0, 1, 1]],
[[1]] # 新增特殊方块形状(放在列表最后)
]
# 初始化窗口
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("俄罗斯方块")
def load_font(size):
try:
return pygame.font.SysFont("simhei", size)
except:
return pygame.font.Font(None, size)
def load_highscore():
try:
if os.path.exists(HIGHSCORE_FILE):
with open(HIGHSCORE_FILE, 'r') as f:
return int(f.read().strip() or 0)
return 0
except:
return 0
def save_highscore(score):
with open(HIGHSCORE_FILE, 'w') as f:
f.write(str(score))
class GameState:
def __init__(self):
self.reset()
def clear_around(self, x, y):
"""以(x,y)为中心消除3x3区域"""
for dy in [-1, 0, 1]:
for dx in [-1, 0, 1]:
nx, ny = x + dx, y + dy
if 0 <= nx < GRID_WIDTH and 0 <= ny < GRID_HEIGHT:
self.game_area[ny][nx] = None
def clear_column(self, x):
"""清除整列"""
for y in range(GRID_HEIGHT):
self.game_area[y][x] = None
def clear_rows(self, y):
"""清除指定行及其相邻行"""
for dy in [-1, 0, 1]:
if 0 <= (y + dy) < GRID_HEIGHT:
self.game_area[y + dy] = [None] * GRID_WIDTH
def reset(self):
self.game_area = [[None] * GRID_WIDTH for _ in range(GRID_HEIGHT)]
self.score = 0
self.highscore = load_highscore()
# 仅初始化next_shape
self.next_shape = random.choice(SHAPES)
self.next_color = random.choice(COLORS)
self.fall_speed = 500
self.running = True
self.initialize_shapes() # 在此方法中生成current_shape
def initialize_shapes(self):
self.current_shape = self.next_shape # 继承next_shape
self.current_color = self.next_color
# 生成新next_shape
# 30%概率生成特殊方块
if random.random() < 0.3:
self.next_shape = [[1]]
self.next_color = random.choice(SPECIAL_COLORS)
else:
self.next_shape = random.choice(SHAPES[:-1]) # 排除特殊形状
self.next_color = random.choice(COLORS)
self.reset_position()
def reset_position(self):
shape_height = len(self.current_shape)
self.current_pos = [
GRID_WIDTH // 2 - len(self.current_shape[0]) // 2,
-shape_height + 1 # 完全在游戏区域外生成
]
# 如果初始位置就碰撞,说明游戏结束
if self.check_collision(self.current_shape, self.current_pos):
self.game_over()
def check_collision(self, shape, pos):
for y in range(len(shape)):
for x in range(len(shape[y])):
if shape[y][x]:
px = pos[0] + x
py = pos[1] + y
if py >= GRID_HEIGHT:
return True
if px < 0 or px >= GRID_WIDTH:
return True
if py >= 0 and self.game_area[py][px] is not None:
return True
return False
def rotate_shape(self):
original_shape = self.current_shape
original_pos = self.current_pos.copy()
# 尝试旋转
rotated = [list(row) for row in zip(*original_shape[::-1])]
self.current_shape = rotated
# 旋转后位置修正
while self.check_collision(self.current_shape, self.current_pos):
if self.current_pos[0] < 0:
self.current_pos[0] += 1 # 右移
else:
self.current_pos[0] -= 1 # 左移
if self.current_pos[0] < 0 or self.current_pos[0] >= GRID_WIDTH:
self.current_shape = original_shape # 还原形状
self.current_pos = original_pos
break
def merge_shape(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
px = self.current_pos[0] + x # 计算实际网格坐标
py = self.current_pos[1] + y
# 添加双重边界检查
if 0 <= px < GRID_WIDTH and 0 <= py < GRID_HEIGHT:
self.game_area[py][px] = self.current_color
def clear_lines(self):
lines_cleared = []
while True:
# 标准行消除
full_lines = [i for i, row in enumerate(self.game_area)
if all(cell is not None for cell in row)]
if not full_lines: break
# 处理特殊方块
for y in full_lines:
for x in range(GRID_WIDTH):
color = self.game_area[y][x]
if color == (0, 0, 0): # 黑色方块
self.clear_around(x, y)
elif color == (128, 0, 128): # 紫色方块
self.clear_column(x)
elif color == (255, 192, 203): # 粉色方块
self.clear_rows(y)
# 更新游戏区域
new_grid = [row for row in self.game_area
if not all(cell is not None for cell in row)]
new_rows = GRID_HEIGHT - len(new_grid)
self.game_area = [[None] * GRID_WIDTH for _ in range(new_rows)] + new_grid
lines_cleared.extend(full_lines)
# 分数计算(每个特殊方块额外加分)
self.score += len(lines_cleared) * 100
self.score += sum(20 for y in lines_cleared
for cell in self.game_area[y]
if cell in SPECIAL_COLORS)
def draw_grid(self):
for y in range(GRID_HEIGHT):
for x in range(GRID_WIDTH):
color = self.game_area[y][x]
# 添加颜色类型安全检查
if isinstance(color, tuple) and len(color) == 3:
pygame.draw.rect(screen, color,
(x * GRID_SIZE, y * GRID_SIZE,
GRID_SIZE - 1, GRID_SIZE - 1))
def draw_current_shape(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
px = (self.current_pos[0] + x) * GRID_SIZE
py = (self.current_pos[1] + y) * GRID_SIZE
pygame.draw.rect(screen, self.current_color,
(px, py, GRID_SIZE - 1, GRID_SIZE - 1))
def draw_info_panel(self):
font = load_font(24)
pygame.draw.rect(screen, BLACK, (GAME_PANEL_WIDTH, 0, INFO_PANEL_WIDTH, WINDOW_HEIGHT))
# 下一个方块
title = font.render("下一个方块", True, WHITE)
screen.blit(title, (GAME_PANEL_WIDTH + 10, 30))
# 预览方块
start_x = GAME_PANEL_WIDTH + (INFO_PANEL_WIDTH - len(self.next_shape[0]) * GRID_SIZE) // 2
start_y = 100
for y, row in enumerate(self.next_shape):
for x, cell in enumerate(row):
if cell:
pygame.draw.rect(screen, self.next_color,
(start_x + x * GRID_SIZE, start_y + y * GRID_SIZE, GRID_SIZE - 1, GRID_SIZE - 1))
# 分数显示
score_text = font.render(f"分数: {self.score}", True, WHITE)
screen.blit(score_text, (GAME_PANEL_WIDTH + 10, 300))
highscore_text = font.render(f"最高分: {self.highscore}", True, WHITE)
screen.blit(highscore_text, (GAME_PANEL_WIDTH + 10, 350))
def handle_input(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.current_pos[0] -= 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[0] += 1
elif event.key == pygame.K_RIGHT:
self.current_pos[0] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[0] -= 1
elif event.key == pygame.K_DOWN:
self.current_pos[1] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] -= 1
elif event.key == pygame.K_UP:
self.rotate_shape()
elif event.key == pygame.K_SPACE:
while not self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] += 1
self.current_pos[1] -= 1
def game_over(self):
if self.score > self.highscore:
save_highscore(self.score)
screen.fill(BLACK)
font = load_font(48)
text = font.render("游戏结束", True, (255, 0, 0))
text_rect = text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 - 30))
screen.blit(text, text_rect)
score_text = font.render(f"得分: {self.score}", True, WHITE)
score_rect = score_text.get_rect(center=(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2 + 30))
screen.blit(score_text, score_rect)
pygame.display.update()
pygame.time.wait(3000)
self.running = False
def run(self):
clock = pygame.time.Clock()
fall_time = pygame.time.get_ticks()
while self.running:
current_time = pygame.time.get_ticks()
self.handle_input()
# 自动下落
if current_time - fall_time > self.fall_speed:
self.current_pos[1] += 1
if self.check_collision(self.current_shape, self.current_pos):
self.current_pos[1] -= 1
self.merge_shape()
self.clear_lines()
self.initialize_shapes() # 生成新方块
# 游戏结束检测
if self.check_collision(self.current_shape, self.current_pos):
self.game_over()
fall_time = current_time
# 绘制界面
screen.fill(DARK_GRAY)
self.draw_grid()
self.draw_current_shape()
self.draw_info_panel()
pygame.display.update()
clock.tick(60)
if __name__ == "__main__":
game = GameState()
game.run()
pygame.quit()

总结
- 先使用DeepSeek生成可以执行的代码
- 使用Lingma优化代码
- 使用Lingma增加特色功能(建议不要一次性增加多个模块,可以一个个模块进行增加调试,对相对满意的模块可以先进行保存)
- 可以通过Ctrl+F调出搜索界面实现快速定位,可以询问Lingma解决警告和报错信息。
当然有Python基础的当然可以理解一下代码的逻辑然后进行修改,但是像我这种什么都不懂的同学就只能以AI为主,通过人工辅助询问一些问题来开发自己的小游戏,个人感觉非常消耗时间,最后如果有什么问题可以在评论区留言,欢迎各位大佬来批评指正!!!