项目计划:基于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)
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:
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 策略:
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更新逻辑
...
奖励设计:
reward = (
accumulated_score_delta * 0.3 + # 累积分数增量(权重低)
situational_score * 0.7 + # 局面分数(权重高)
game_over_penalty * (-100) # 游戏结束惩罚
)
4. 多进程训练 (parallel.py)
利用6核CPU,同时运行多个游戏实例:
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 构建界面:
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: 核心游戏逻辑
- 实现
game.py- 2048游戏规则 - 实现局面分数计算
- 编写游戏逻辑单元测试
阶段2: 模型与训练
- 实现
model.py- Transformer模型 - 实现
trainer.py- PPO训练器 - 实现
parallel.py- 多进程环境 - 验证训练流程可以运行
阶段3: GUI界面
- 实现
gui.py- 主窗口和游戏面板 - 实现训练状态可视化(分数曲线)
- 实现模式切换(训练/演示)
阶段4: 整合与优化
- 整合所有模块
- 性能优化
- 模型保存/加载功能
依赖
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局游戏平均分数无提升
- 用户手动停止