| # 2048 AI Trainer |
|
|
| 基于 Transformer 的 2048 游戏人工智能训练器,使用 PPO(Proximal Policy Optimization)强化学习算法,让 AI 学会玩 2048 游戏。 |
|
|
| ## 项目简介 |
|
|
| 本项目实现了一个完整的 2048 游戏 AI 训练系统,包括: |
|
|
| - **游戏引擎**: 完整的 2048 游戏逻辑实现 |
| - **深度学习模型**: 基于 Transformer 架构的策略网络 |
| - **强化学习训练**: PPO 算法实现 |
| - **可视化界面**: PyQt5 图形界面,支持训练监控和演示 |
| - **命令行工具**: 支持无 GUI 的训练和演示模式 |
|
|
| ## 功能特点 |
|
|
| ### 1. Transformer 模型 |
|
|
| 采用小型 Transformer 架构,专为 CPU 训练优化: |
|
|
| - **参数量**: 约 77,000 个参数(~300KB) |
| - **输入处理**: |
| - 棋盘状态编码为 16 个 token(每个格子对应一个 token) |
| - 分数特征(累积分数、局面分数)作为额外输入 |
| - 位置编码:行/列位置嵌入 |
| - **网络结构**: |
| - 2 层 Transformer Encoder |
| - 4 个注意力头 |
| - 隐藏维度 64 |
| - 前馈网络维度 128 |
| - **输出**: |
| - 策略头:4 个动作(上/下/左/右)的概率分布 |
| - 价值头:当前状态的价值评估 |
|
|
| ### 2. 双评分机制 |
|
|
| #### 累积分数(Accumulated Score) |
| 传统 2048 计分方式,每次合成砖块获得合成后砖块的数值作为分数。 |
|
|
| #### 局面分数(Situational Score) |
| 综合评估当前局面的质量,鼓励 AI 保持良好局面: |
|
|
| ``` |
| 局面分数 = 空格数 × 10 + 最大连续相邻数 × 15 + log₂(最大砖块) × 5 + 单调性奖励 |
| ``` |
|
|
| - **空格数**: 空格越多,操作空间越大 |
| - **连续相邻数**: 如 512-1024-2048 连续排列,便于后续合并 |
| - **单调性**: 鼓励数字按方向有序排列 |
|
|
| ### 3. PPO 训练算法 |
|
|
| 使用 Proximal Policy Optimization 算法进行训练: |
|
|
| - **优势估计**: GAE(Generalized Advantage Estimation) |
| - **策略裁剪**: 防止策略更新过大 |
| - **价值函数**: 辅助训练,提供状态价值估计 |
| - **熵正则化**: 鼓励探索 |
|
|
| ### 4. GUI 界面 |
|
|
| 基于 PyQt5 的图形界面: |
|
|
| - **训练模式**: |
| - 设置训练局数 |
| - 实时显示训练进度 |
| - 分数曲线可视化 |
| - 训练完成后自动保存模型 |
| |
| - **演示模式**: |
| - 键盘手动操作 |
| - AI 托管模式 |
| - 单步执行 |
| - 自动连续执行 |
| - 实时局面分数曲线 |
|
|
| ## 安装 |
|
|
| ### 环境要求 |
|
|
| - Python 3.8+ |
| - Windows / Linux / macOS |
|
|
| ### 安装依赖 |
|
|
| ```bash |
| cd game2048 |
| pip install -r requirements.txt |
| ``` |
|
|
| ### 依赖列表 |
|
|
| ``` |
| torch>=2.0.0 # 深度学习框架 |
| numpy<2 # 数值计算 |
| PyQt5>=5.15.0 # GUI 框架 |
| matplotlib>=3.7.0 # 绘图库 |
| ``` |
|
|
| ## 使用方法 |
|
|
| ### 1. GUI 模式 |
|
|
| ```bash |
| python main.py |
| ``` |
|
|
| 启动图形界面后: |
|
|
| **训练模式**: |
| 1. 选择 "Training Mode" |
| 2. 设置训练局数(默认 500) |
| 3. 点击 "Start Training" 开始训练 |
| 4. 训练完成后自动保存到 `checkpoints/model.pt` |
| 5. 可随时点击 "Stop Training" 停止 |
|
|
| **演示模式**: |
| 1. 选择 "Demo Mode" |
| 2. 点击 "Load Model" 加载已训练模型 |
| 3. 使用方式: |
| - 键盘方向键:手动操作 |
| - "AI Mode":切换 AI 托管 |
| - "Step":AI 单步执行 |
| - "Auto":AI 自动连续执行 |
| - "Reset":重新开始游戏 |
|
|
| ### 2. 命令行训练 |
|
|
| ```bash |
| # 训练 1000 局 |
| python main.py --train --games 1000 |
| |
| # 使用 4 个并行环境 |
| python main.py --train --games 1000 --envs 4 |
| |
| # 设置随机种子 |
| python main.py --train --games 1000 --seed 42 |
| ``` |
|
|
| ### 3. 演示模式 |
|
|
| ```bash |
| # 加载模型并演示 5 局 |
| python main.py --demo --model checkpoints/model.pt --games 5 |
| |
| # 不加载模型(随机权重) |
| python main.py --demo --games 3 |
| ``` |
|
|
| ### 4. 简单训练脚本 |
|
|
| ```bash |
| python train_simple.py |
| ``` |
|
|
| 修改脚本末尾可调整训练参数: |
|
|
| ```python |
| train_simple(num_games=500, save_path="checkpoints/model.pt") |
| ``` |
|
|
| ## 项目结构 |
|
|
| ``` |
| game2048/ |
| ├── TASK.md # 任务需求文档 |
| ├── PLAN.md # 项目计划文档 |
| ├── README.md # 本文件 |
| ├── main.py # 程序入口 |
| ├── game.py # 2048 游戏核心逻辑 |
| │ ├── Game2048 # 游戏类 |
| │ ├── move() # 移动操作 |
| │ ├── get_state() # 获取状态 |
| │ └── calculate_situational_score() # 计算局面分数 |
| │ |
| ├── model.py # Transformer 模型 |
| │ ├── Game2048Transformer # Transformer 模型 |
| │ ├── Game2048CNN # CNN 备选模型 |
| │ └── get_action() # 动作选择 |
| │ |
| ├── trainer.py # PPO 训练器 |
| │ ├── PPOTrainer # PPO 训练类 |
| │ ├── RolloutBuffer # 经验缓冲区 |
| │ ├── Transition # 状态转移数据结构 |
| │ └── TrainingStats # 训练统计 |
| │ |
| ├── parallel.py # 并行训练环境 |
| │ ├── ParallelGameEnv # 并行游戏环境 |
| │ ├── TrainingWorker # 训练工作器 |
| │ └── TrainingLoop # 训练循环 |
| │ |
| ├── gui.py # GUI 界面 |
| │ ├── MainWindow # 主窗口 |
| │ ├── GameBoardWidget # 游戏面板 |
| │ ├── ScoreWidget # 分数显示 |
| │ ├── PlotCanvas # 曲线绑图 |
| │ └── SimpleTrainingThread # 训练线程 |
| │ |
| ├── train_simple.py # 简化训练脚本 |
| ├── utils.py # 工具函数 |
| ├── requirements.txt # 依赖列表 |
| └── checkpoints/ # 模型保存目录 |
| └── model.pt # 训练好的模型 |
| ``` |
|
|
| ## 模型架构详解 |
|
|
| ### 输入表示 |
|
|
| ```python |
| # 棋盘状态 (4, 4) |
| # 每个格子值转换为 log₂(value),空格为 0 |
| state = [[0, 1, 2, 0], # 对应 [空, 2, 4, 空] |
| [1, 2, 3, 1], # 对应 [2, 4, 8, 2] |
| ...] |
| |
| # 分数特征 (2,) |
| # [归一化累积分数, 归一化局面分数] |
| scores = [0.05, 0.85] |
| ``` |
|
|
| ### 网络结构 |
|
|
| ``` |
| Input: (batch, 4, 4) board + (batch, 2) scores |
| ↓ |
| Position Embedding: (batch, 16, 64) |
| + Spatial Embedding: (batch, 16, 64) |
| + Score Embedding: (batch, 1, 64) |
| ↓ |
| Transformer Encoder (2 layers) |
| - Multi-Head Attention (4 heads) |
| - Feed-Forward Network (dim=128) |
| ↓ |
| Global Mean Pooling: (batch, 64) |
| ↓ |
| ├── Policy Head → (batch, 4) # 动作概率 |
| └── Value Head → (batch, 1) # 状态价值 |
| ``` |
|
|
| ## 训练策略 |
|
|
| ### 奖励设计 |
|
|
| ```python |
| reward = 局面分数变化 × 0.7 + 累积分数增量 × 0.003 |
| |
| # 游戏结束惩罚 |
| if game_over: |
| reward -= 10.0 |
| ``` |
|
|
| ### 超参数 |
|
|
| | 参数 | 值 | |
| |------|-----| |
| | Learning Rate | 3e-4 | |
| | Batch Size | 64 | |
| | PPO Clip Ratio | 0.2 | |
| | GAE Lambda | 0.95 | |
| | Discount Factor (γ) | 0.99 | |
| | Entropy Coefficient | 0.01 | |
|
|
| ## 训练结果 |
|
|
| ### 500 局训练后 |
|
|
| | 指标 | 数值 | |
| |------|------| |
| | 平均分数 | ~2500 | |
| | 最高分数 | 6812 | |
| | 最大砖块 | 512 | |
| | 训练时间 | ~9 分钟 | |
|
|
| ### 分数分布 |
|
|
| ``` |
| 随机权重: 平均 ~800, 最高 ~2000 |
| 训练 500 局: 平均 ~2500, 最高 ~6800 |
| ``` |
|
|
| ## 开发说明 |
|
|
| ### 添加新功能 |
|
|
| 1. **修改局面评分**: 编辑 `game.py` 中的 `calculate_situational_score()` |
| 2. **调整模型**: 修改 `model.py` 中的网络结构 |
| 3. **优化训练**: 调整 `trainer.py` 中的超参数 |
|
|
| ### 调试模式 |
|
|
| ```python |
| # 在 game.py 中测试游戏逻辑 |
| if __name__ == "__main__": |
| game = Game2048() |
| print(game) |
| game.move(Game2048.LEFT) |
| print(game) |
| ``` |
|
|
| ## 已知问题 |
|
|
| - Windows 下 PyTorch 可能需要特定版本以避免 DLL 加载问题 |
| - NumPy 2.x 与 PyTorch 存在兼容性问题,建议使用 NumPy < 2 |
|
|
| ## 参考资料 |
|
|
| - [PPO 论文](https://arxiv.org/abs/1707.06347) |
| - [Transformer 论文](https://arxiv.org/abs/1706.03762) |
| - [2048 游戏](https://play2048.co/) |
|
|
| ## 许可证 |
|
|
| MIT License |
|
|
| --- |
|
|
| *本项目由 GLM-5 开发实现* |