docs: AI 升级专业版设计文档

This commit is contained in:
2026-05-31 15:33:51 +08:00
parent 2ad05cab4b
commit 0d9b21d4c5
@@ -0,0 +1,288 @@
# Gobang AI 升级设计文档
> 状态: 待审核 | 日期: 2026-05-31
## 目标
将当前基础 Alpha-Beta AI 升级为专业级五子棋 AI,具备迭代加深、置换表、组合棋形评估、VCF/VCT 杀棋搜索和开局库。
## 架构变更
```
当前: 升级后:
AlphaBetaAi AlphaBetaAi
└─ negamax() ├─ iterative_deepening() ← 新增:逐层加深 + 时间控制
├─ negamax() ← 改进:加 TT + killer
│ ├─ evaluate_board() ← 改进:组合棋形 + 位置权重
│ └─ tt: TransTable ← 新增:置换表缓存
├─ vcf_search() ← 新增:连续冲四取胜搜索
├─ vct_search() ← 新增:连续活三取胜搜索
└─ opening_book ← 新增:开局定式库
```
---
## 一、迭代加深 + 时间控制
### 原理
不再固定搜索 depth=N,而是从 depth=1 开始,每轮加深 1 层,直到时间用尽。每轮完成后保存 best_move,超时时返回上一轮的结果。
### 实现
```
best_move(board, color):
start_time = now()
time_limit = 根据难度映射: [1,2,3,5,8] 秒 → level 1~5
best = center_position
for depth in 1..=MAX_DEPTH:
result = negamax_root(board, depth, color)
if 搜索完成没超时:
best = result.best_move
else:
break // 超时,返回上轮 best
return best
```
### 时间分配策略
- 每层完成后检查是否剩余时间 < 本层耗时 × 1.5,如果是则不再加深
- 防止"刚开搜就超时"的无效搜索
### 难度→时间映射
| Level | 时间上限 | depth 上限 |
|-------|---------|-----------|
| 1 | 1s | 4 |
| 2 | 2s | 6 |
| 3 | 3s | 8 |
| 4 | 5s | 12 |
| 5 | 8s | 20 |
---
## 二、置换表 (Transposition Table)
### 原理
五子棋中不同走子顺序可能到达同一局面。用 Zobrist 哈希为每个局面生成唯一 key,缓存搜索结果。下次遇到相同哈希直接查表,避免重复搜索。
### 数据结构
```rust
struct TransTable {
entries: Vec<Option<TTEntry>>, // 2^N 大小,N=20 → 约 100 万条目
size_mask: u64,
}
struct TTEntry {
hash: u64, // 完整哈希(防冲突)
depth: u8, // 搜索深度
score: i32, // 局面评分 (转为整数)
bound: BoundType, // Exact / LowerBound / UpperBound
best_move: Option<Position>,
}
```
### Zobrist 哈希
```rust
// 初始化:全局二维随机数表
// zobrist[color][x][y] = random_u64()
// 局面哈希 = XOR 所有棋子的 zobrist[color][x][y]
// 落子时增量更新:hash ^= zobrist[color][x][y]
// 不提子所以不需要 undo 操作,直接用 hash ^=
```
### 查表/存表
```
negamax(board, depth, alpha, beta, color):
hash = board.zobrist_hash
// 查表
if let Some(entry) = tt.probe(hash, depth):
if entry.depth >= depth:
match entry.bound:
Exact => return entry.score
LowerBound => alpha = max(alpha, entry.score)
UpperBound => beta = min(beta, entry.score)
if alpha >= beta: return entry.score
// ... 正常搜索 ...
// 存表
if score <= alpha_orig: bound = UpperBound
elif score >= beta: bound = LowerBound
else: bound = Exact
tt.store(hash, depth, score, bound, best_move)
```
### 配置
- 表大小:2^20 条目 ≈ 32MB(每条目 ~32 bytes
- 替换策略:深度优先(depth 深的覆盖 depth 浅的)
---
## 三、组合棋形评估
### 问题
当前评估单方向扫描,无法识别"一个方向活三 + 垂直方向活三 = 必胜威胁"的组合。
### 改进:多方向特征向量
```rust
struct PositionFeatures {
// 四个方向 (水平、垂直、对角线1、对角线2) 各自的最大棋形
max_pattern: [Pattern; 4], // Five/OpenFour/RushFour/OpenThree/...
combo_score: f64, // 组合加分
position_bonus: f64, // 位置权重
}
enum Pattern {
Five, OpenFour, RushFour, OpenThree, SleepThree,
OpenTwo, SleepTwo, OpenOne, Empty,
}
```
### 组合评分规则
| 组合 | 加分 | 说明 |
|------|------|------|
| 活三 + 活三 (交叉方向) | 5000 | 必胜形 |
| 活三 + 冲四 | 10000 | 近似必胜 |
| 冲四 + 冲四 | 8000 | 双重威胁 |
| 活三 + 活二 | 500 | 发展优势 |
### 位置权重
```
position_score(x, y, board_size) =
基础距离分: 离中心越近越高 (高斯分布)
+ 边缘惩罚: 边缘 2 行内下降 50%
+ 星位偏好: 标准星位额外 +5%
```
位置权重占总评分的 ~5%,主要影响开局和中盘。
---
## 四、杀棋启发 (Killer Move Heuristic)
### 原理
记录每层深度中触发 Beta 剪枝的走法。同一层深的相似局面,上次有效的走法这次也优先搜索。
### 数据结构
```rust
// 每层存 2 个 killer move
killer_moves: [[Option<Position>; 2]; MAX_DEPTH]
```
### 候选排序优先级
1. 置换表中的 best_move
2. killer_moves[depth]
3. 能立即五连的走法
4. evaluate_board 预评分排序的其余走法
---
## 五、VCF/VCT 杀棋搜索
### VCF (Victory by Continuous Fours)
连续冲四取胜。当一方有冲四时,对手必须堵,我方继续冲四,直到五连。
```
vcf_search(board, color, depth_limit=10):
if check_win: return Some(win_path)
for each rush_four position for `color`:
board.place(pos)
opponent_blocks = forced_block_positions(board) // 对手只有一种堵法
if len(opponent_blocks) == 1:
board.place(opponent_block) // 对手被迫堵
result = vcf_search(board, color, depth-2)
if result: return Some(pos + result)
board.undo(2)
return None // 无必胜序列
```
### VCT (Victory by Continuous Threats)
类似 VCF 但目标棋形更宽(冲四 + 活三),搜索更深。
- VCF:仅搜索连续冲四序列,depth ≤ 10
- VCT:搜索冲四/活三混合序列,depth ≤ 15
### 触发时机
`best_move` 中:
1. 先跑 VCF/VCT 浅搜索(depth=6
2. 如果找到必胜序列 → 直接返回第一步
3. 否则 → 正常 Alpha-Beta 搜索
4. 如果 AB 搜索发现对手有威胁 → 防御模式,优先堵 VCF/VCT 路径
---
## 六、开局库
### 格式
```rust
struct OpeningBook {
// hash → [best_moves]
positions: HashMap<u64, Vec<Position>>,
}
// 初始化时从内置数据加载
fn load_opening_book() -> OpeningBook {
// 内置 50 个常见开局定式
// 花月、浦月、云月、雨月、溪月、金星、水月、新月 ...
}
```
### 数据来源
内置 50 个标准五子棋开局定式,覆盖前 3~7 手。直接从专业棋谱提取坐标序列,编译期嵌入。
### 使用逻辑
```
best_move():
if 总手数 < opening_book_threshold: // 前 7 手
if let Some(moves) = opening_book.lookup(board.hash):
return random_choice(moves) // 从定式中随机选一个变招
// 超过开局阶段,正常搜索
return iterative_deepening(...)
```
---
## 七、文件变更
| 文件 | 操作 | 内容 |
|------|------|------|
| `core/Cargo.toml` | 改 | 加 `rand` 依赖(开局随机选择),加 `fxhash`(快速哈希) |
| `core/src/ai/mod.rs` | 改 | AiEngine trait 不变 |
| `core/src/ai/search.rs` | 重写 | 迭代加深 + TT + killer + VCF/VCT 入口 |
| `core/src/ai/evaluate.rs` | 重写 | 组合棋形 + 位置权重 |
| `core/src/ai/trans_table.rs` | 新建 | 置换表实现 (Zobrist + HashMap) |
| `core/src/ai/killer.rs` | 新建 | Killer move 表 |
| `core/src/ai/vcf.rs` | 新建 | VCF/VCT 杀棋搜索 |
| `core/src/ai/opening.rs` | 新建 | 开局库 (50 定式) |
| `core/src/board.rs` | 改 | Board 加 zobrist_hash 字段,place/undo 增量更新 |
| `core/src/types.rs` | 改 | 加 Zobrist 相关类型 |
| `gui/src/commands.rs` | 改 | new_game 适配新的 AI 参数(时间上限替代 depth) |
---
## 八、测试策略
| 模块 | 测试 |
|------|------|
| Zobrist 哈希 | 落子后哈希变化、对称局面哈希不同、undo 后恢复 |
| 置换表 | 存/查/同局面命中、depth 优先级替换 |
| 组合棋形 | 单方向评分不变、交叉活三加分、冲四+活三加分 |
| 位置权重 | 中心>边缘、对称位置权重相同 |
| Killer | 插入、查询、满容量替换 |
| VCF | 已知必胜序列被找到、无解返回 None |
| 开局库 | lookup 已知局面、未知局面返回 None |
| 迭代加深 | 超时返回有效 move、时间限制内完成 |
| AI 回归 | 原有 3 个 AI 测试仍然通过 |
---
## 九、不做 (YAGNI)
- 多线程并行搜索(收益有限,复杂度高)
- 蒙特卡洛树搜索(五子棋不适合)
- 神经网络评估(太重)
- 在线开局库更新
- 残局库