| # 项目计划:基于Transformer的2048游戏AI |
|
|
| ## 技术方案概述 |
|
|
| ### 硬件约束 |
| - AMD Ryzen 5 PRO 4650U (6核12线程) CPU |
| - 8GB RAM |
| - 无NVIDIA GPU,纯CPU训练 |
| - 需要小型高效的模型架构 |
|
|
| ### 整体架构 |
|
|
| ``` |
| ┌─────────────────────────────────────────────────────────┐ |
| │ GUI主窗口 (PyQt5) │ |
| ├─────────────────────────────────────────────────────────┤ |
| │ ┌──────────────┐ ┌──────────────────────────────────┐ │ |
| │ │ 2048游戏面板 │ │ 训练状态面板 │ │ |
| │ │ (4x4网格) │ │ - 当前分数/局面分数 │ │ |
| │ │ │ │ - 训练速度 (games/sec) │ │ |
| │ │ │ │ - 累积分数变化曲线 │ │ |
| │ │ │ │ - 局面分数变化曲线 │ │ |
| │ └──────────────┘ └──────────────────────────────────┘ │ |
| ├─────────────────────────────────────────────────────────┤ |
| │ 控制面板: [训练模式] [演示模式] [开始/停止] [AI托管] │ |
| └─────────────────────────────────────────────────────────┘ |
| ``` |
|
|
| ## 模块设计 |
|
|
| ### 1. 游戏核心模块 (`game.py`) |
|
|
| ```python |
| class Game2048: |
| """2048游戏核心逻辑""" |
| |
| def __init__(self): |
| self.board: np.ndarray # 4x4棋盘 |
| self.accumulated_score: int # 累积分数 |
| self.situational_score: float # 局面分数 |
| |
| def reset(self) -> None: ... |
| def move(self, direction: int) -> tuple[bool, bool]: ... # (moved, game_over) |
| def get_state(self) -> np.ndarray: ... # 返回当前局面 |
| def calculate_situational_score(self) -> float: ... |
| ``` |
|
|
| **局面分数计算公式:** |
| ``` |
| situation_score = ( |
| empty_cells * 10 + # 空格越多越好 |
| max_consecutive_adjacent * 15 + # 连续相邻数字越多越好 |
| log2(max_tile) * 5 - # 最高数字的对数 |
| monotonicity_penalty # 单调性惩罚(避免混乱) |
| ) |
| ``` |
|
|
| ### 2. Transformer模型 (`model.py`) |
|
|
| 考虑到CPU训练的限制,采用小型Transformer: |
|
|
| ```python |
| class Game2048Transformer(nn.Module): |
| """小型Transformer用于2048决策""" |
| |
| def __init__(self): |
| # 输入: 4x4棋盘 + 2个分数特征 |
| # 将棋盘展平为16个token,每个token代表一个格子的状态 |
| |
| self.embedding = nn.Embedding(16, 64) # 0-15 表示 log2(value),16表示空 |
| self.score_embedding = nn.Linear(2, 64) # 两种分数的embedding |
| |
| encoder_layer = nn.TransformerEncoderLayer( |
| d_model=64, |
| nhead=4, |
| dim_feedforward=128, |
| dropout=0.1, |
| batch_first=True |
| ) |
| self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=2) |
| |
| self.policy_head = nn.Linear(64, 4) # 输出4个动作的概率 |
| self.value_head = nn.Linear(64, 1) # 输出状态价值 |
| ``` |
|
|
| **模型大小估算:** |
| - Embedding: 17 * 64 = 1,088 参数 |
| - Transformer (2层): ~50,000 参数 |
| - 输出头: ~300 参数 |
| - **总计: ~52,000 参数** - 非常小,适合CPU训练 |
|
|
| ### 3. 训练模块 (`trainer.py`) |
|
|
| 采用 **Actor-Critic + PPO** 策略: |
|
|
| ```python |
| class PPOTrainer: |
| """PPO训练器""" |
| |
| def __init__(self, model, lr=1e-4): |
| self.model = model |
| self.optimizer = torch.optim.Adam(model.parameters(), lr=lr) |
| |
| def compute_advantage(self, rewards, values, dones): |
| # 计算GAE (Generalized Advantage Estimation) |
| ... |
| |
| def update(self, trajectories): |
| # PPO更新逻辑 |
| ... |
| ``` |
|
|
| **奖励设计:** |
| ```python |
| reward = ( |
| accumulated_score_delta * 0.3 + # 累积分数增量(权重低) |
| situational_score * 0.7 + # 局面分数(权重高) |
| game_over_penalty * (-100) # 游戏结束惩罚 |
| ) |
| ``` |
|
|
| ### 4. 多进程训练 (`parallel.py`) |
|
|
| 利用6核CPU,同时运行多个游戏实例: |
|
|
| ```python |
| class ParallelGameEnv: |
| """并行游戏环境""" |
| |
| def __init__(self, num_envs=4): |
| self.num_envs = num_envs |
| self.envs = [Game2048() for _ in range(num_envs)] |
| |
| def step(self, actions: list[int]) -> list[Transition]: |
| # 并行执行动作,返回状态转移 |
| ... |
| ``` |
|
|
| ### 5. GUI模块 (`gui.py`) |
|
|
| 使用 PyQt5 构建界面: |
|
|
| ```python |
| class MainWindow(QMainWindow): |
| """主窗口""" |
| |
| def __init__(self): |
| self.game_widget = GameBoardWidget() |
| self.stats_widget = StatsWidget() |
| self.control_widget = ControlWidget() |
| |
| # 训练线程 |
| self.training_thread = TrainingThread() |
| |
| def switch_mode(self, mode: str): ... |
| def update_display(self): ... |
| ``` |
|
|
| ## 文件结构 |
|
|
| ``` |
| game2048/ |
| ├── TASK.md # 任务描述 |
| ├── PLAN.md # 本文件 |
| ├── main.py # 入口文件 |
| ├── game.py # 游戏核心逻辑 |
| ├── model.py # Transformer模型定义 |
| ├── trainer.py # PPO训练器 |
| ├── parallel.py # 多进程训练 |
| ├── gui.py # GUI界面 |
| ├── utils.py # 工具函数 |
| ├── requirements.txt # 依赖 |
| └── checkpoints/ # 模型保存目录 |
| ``` |
|
|
| ## 实现步骤 |
|
|
| ### 阶段1: 核心游戏逻辑 |
| 1. 实现 `game.py` - 2048游戏规则 |
| 2. 实现局面分数计算 |
| 3. 编写游戏逻辑单元测试 |
|
|
| ### 阶段2: 模型与训练 |
| 4. 实现 `model.py` - Transformer模型 |
| 5. 实现 `trainer.py` - PPO训练器 |
| 6. 实现 `parallel.py` - 多进程环境 |
| 7. 验证训练流程可以运行 |
|
|
| ### 阶段3: GUI界面 |
| 8. 实现 `gui.py` - 主窗口和游戏面板 |
| 9. 实现训练状态可视化(分数曲线) |
| 10. 实现模式切换(训练/演示) |
|
|
| ### 阶段4: 整合与优化 |
| 11. 整合所有模块 |
| 12. 性能优化 |
| 13. 模型保存/加载功能 |
|
|
| ## 依赖 |
|
|
| ``` |
| torch>=2.0.0 |
| numpy>=1.24.0 |
| PyQt5>=5.15.0 |
| matplotlib>=3.7.0 |
| ``` |
|
|
| ## 训练策略细节 |
|
|
| ### 状态表示 |
| - 棋盘状态:将每个格子的值转换为 log2(value),空格为0 |
| - 分数归一化:累积分数和局面分数归一化到 [0, 1] |
|
|
| ### 动作空间 |
| - 0: 上 |
| - 1: 下 |
| - 2: 左 |
| - 3: 右 |
|
|
| ### 训练超参数 |
| - Learning rate: 1e-4 |
| - Batch size: 64 |
| - PPO clip ratio: 0.2 |
| - GAE lambda: 0.95 |
| - Discount factor (gamma): 0.99 |
| - 并行环境数: 4 (根据CPU核心数调整) |
|
|
| ### 停止条件 |
| - 连续100局游戏平均分数无提升 |
| - 用户手动停止 |
|
|