这是一个使用 Python 3 和 Pygame 做的俄罗斯方块小项目。除了普通单机模式,还加了一个局域网双人模式:两边各玩各的棋盘,60 秒后比较分数。界面只用了 Pygame 的基本绘制和文字,没有额外的图片素材。
- 标准 10×20 棋盘和 I、O、T、S、Z、J、L 七种方块
- 7-bag 随机算法,保证每轮七种方块各出现一次
- 左右移动、自动下落、软降和硬降
- 顺时针、逆时针旋转和简化墙踢
- 幽灵方块提示硬降位置
- 方块暂存及下一个方块预览
- 0.5 秒落地锁定时间
- 单行、多行消除
- 分数、累计消行、等级和连击显示
- 等级提高后自动加快下落
- 暂停、恢复和游戏结束界面
- 一名玩家创建房间,另一名玩家加入
- 使用 UDP 广播自动搜索同一局域网内的房间
- 自动搜索不可用时可手动输入主机 IPv4 地址
- 使用 TCP 可靠传输双方棋盘和分数
- 主机统一发起比赛
- 比赛开始前显示准备倒计时
- 每局固定持续 60 秒
- 双方屏幕实时显示自己和对手的棋盘、操作结果及分数
- 时间结束后按分数判断胜利、失败或平局
- 对手断开时显示提示,不会直接导致程序崩溃
联机玩法是独立棋盘计分赛,不会向对手发送垃圾行。一方提前堆满后停止操作,但比赛仍等待 60 秒结束,再比较双方分数。
| 功能 | 按键 |
|---|---|
| 向左移动 | ← |
| 向右移动 | → |
| 软降 | ↓ |
| 顺时针旋转 | X 或 ↑ |
| 逆时针旋转 | Z |
| 硬降 | Space |
| 暂存/交换方块 | C |
| 单机暂停/继续 | P |
| 返回菜单/退出房间 | Esc |
计分由独立的 ScoreSystem 管理,棋盘规则只负责报告发生了什么事件。
- 软降:每主动下降一格获得 1 分
- 硬降:每下降一格获得 2 分
- 自动下落不计分
| 一次消除 | 基础分 |
|---|---|
| 1 行 | 100 |
| 2 行 | 300 |
| 3 行 | 500 |
| 4 行 | 800 |
实际消行分为:
基础分 × 当前等级 + 连击奖励
连续两个或更多方块均产生消行时,奖励为 连击索引 × 50。某个方块锁定后没有消行,连击立即中断。每累计消除 10 行提升一级。
- Python 3.10 或更高版本
- Pygame 2.6.x
- Windows 10/11 或较新的 macOS
- 联机设备位于允许相互访问的同一 IPv4 局域网
界面使用系统自带中文字体:
- Windows:微软雅黑
C:/Windows/Fonts/msyh.ttc - macOS:华文黑体
/System/Library/Fonts/STHeiti Medium.ttc
进入项目目录,安装依赖:
python -m pip install -r requirements.txt启动游戏:
python main.py部分系统需要使用:
python3 -m pip install -r requirements.txt
python3 main.py- 启动程序。
- 点击“单机模式”。
- 使用方向键、旋转键、硬降和暂存键操作。
- 查看右侧的分数、消行、等级和连击。
- 堆叠到出生区域后游戏结束,按
Esc返回菜单。
- 启动游戏并进入“联机模式”。
- 点击“创建房间”。
- 将界面显示的局域网 IP 告知另一名玩家。
- 等待对手连接。
- 对手连接成功后点击“开始 60 秒对战”。
- 连接与主机相同的局域网。
- 进入“联机模式”。
- 点击“搜索局域网”,程序会连接发现的第一个房间。
- 如果搜索不到,在输入框键入主机 IPv4 地址,再点击“连接 IP”。
- 等待主机开始比赛。
| 协议 | 端口 | 用途 |
|---|---|---|
| UDP | 45678 | 房间发现与响应 |
| TCP | 45679 | 昵称、开始消息和游戏快照 |
Windows 防火墙或 macOS 防火墙询问时,需要允许 Python 接收局域网连接。访客 Wi-Fi、部分校园网和开启客户端隔离的路由器可能禁止设备互访。
.
├── main.py 程序入口
├── requirements.txt Python 依赖
├── README.md 项目使用说明
├── tetris/
│ ├── __init__.py
│ ├── config.py 窗口、棋盘、颜色和端口配置
│ ├── core.py 方块、棋盘、玩法和独立计分系统
│ ├── ui.py 字体、按钮、面板、方格和棋盘绘制
│ ├── network.py UDP 发现、TCP 会话和消息队列
│ └── app.py 主循环、场景、输入、绘制和联机控制
├── tests/
│ └── test_core.py 玩法与计分自动测试
└── docs/ 需求、设计、协议、测试和小组分工文档
项目按规则、界面、网络和应用控制分层:
main.py
└── TetrisApp
├── TetrisGame
│ ├── Piece / Bag
│ └── ScoreSystem
├── ui.py
└── LanSession
负责棋盘和全部俄罗斯方块玩法,包括碰撞、移动、旋转、下落、暂存、锁定、消行和游戏结束。它不读取键盘,也不进行绘制,因此可以独立测试。
只管理分数、累计消行、等级和连击。TetrisGame 在软降、硬降和消行发生时调用计分接口,不在棋盘代码中重复分数公式。
提供通用 Pygame 绘制函数。棋盘绘制函数既可以画单机大棋盘,也可以通过不同单元格尺寸画联机对手的小棋盘。
UDP 用于房间发现,TCP 使用 UTF-8 JSON Lines 传输消息。网络操作运行在守护线程中,Pygame 主线程通过线程安全队列收发字典,避免连接或接收操作冻结画面。
管理四种场景:
menu 主菜单
single 单机游戏
network 联机大厅
battle 联机对战
主循环每帧读取事件、更新当前场景、绘制画面,最后调用 pygame.display.flip() 显示结果。
TCP 每条消息为一行 JSON:
JSON对象 + 换行符
主要消息:
{"type":"hello","name":"玩家名称"}
{"type":"start","delay":1.5}
{"type":"state","data":{"board":[],"score":0,"lines":0,"level":1,"game_over":false}}
{"type":"final","score":3200}start.delay 表示收到消息后等待多少秒开始,因此不要求两台电脑的系统时钟完全一致。state 大约每 80ms 发送一次。对战双方本地模拟自己的棋盘,网络快照用于向对手展示实时状态。倒计时结束后双方交换 final,使用最终分数判胜,避免用到尚未抵达的旧快照。
执行全部自动测试:
python -m unittest discover -s tests -v测试覆盖:
- 7-bag 是否包含全部七种方块
- 棋盘边界碰撞
- 硬降锁定
- 单行消除
- 每个活动块只能暂存一次
- 软降和硬降分数
- 消行、等级和连击
- 无消行时中断连击
- 方块落地后保持完整的 0.5 秒锁定时间
三人按模块分工,合并时以公开接口为准:
- 成员一:除计分外的完整俄罗斯方块玩法
- 成员二:UI、输入和场景绘制
- 组长:独立计分系统、应用控制、局域网和最终集成
具体到类和方法的所有权见:
README 放常用的运行和使用信息,具体的分工、接口和 Git 流程分开记在下面三份文档里:
| 文档 | 内容 |
|---|---|
| 小组分工与集成方案 | 三人代码所有权和集成流程 |
| 完整接口文档 | 类、方法、数据格式和网络协议契约 |
| Git 与 GitHub 协作指南 | 从空仓库初始化到PR合并和发布 |
确认系统字体文件存在。项目在 Windows 使用微软雅黑,在 macOS 使用华文黑体。如果系统经过精简并删除字体,需要恢复对应系统字体或修改 tetris/ui.py 中的字体路径。
确认两台设备连接同一路由器,关闭访客隔离,并允许 UDP 45678。也可以直接输入主机显示的 IPv4 地址。
确认防火墙允许 TCP 45679,并检查两端是否运行相同版本的网络消息格式。
确认没有旧游戏进程仍在运行。正常退出联机房间会关闭监听和连接套接字。
当前版本专注于经典单机和局域网计分对战,不包含垃圾行攻击、互联网服务器、账号系统、排行榜、观战、断线重连、音效或外部图片素材。