课程定位: 围棋打谱辅助系统系列课程的 Part 2.1。在 Part 2 介绍了 GTP 协议与 KataGo 原理之后,本章展示具体实现:GtpEngine 子进程通信、SgfParser 节点树解析、GoGame AI 自动落子循环、启动对话框与棋谱导出。
前置知识: Part 2 GTP 协议、Part 1 规则引擎
预计阅读: 15 分钟
Go Board 游戏功能 —— 新增功能说明
日期:2026-05-14
项目:go-board-cpp
新增模块:GtpEngine / SgfParser / GoGame AI / Kifu / SGF Export
1. 概述
本轮为围棋系统新增了完整的对弈和打谱能力,从"只能检测棋子"升级为"可以和人下棋 + 看棋谱”。
涉及 4 个新增模块、2 个核心类改造、1 个启动对话框。
2. 功能清单
2.1 🤖 AI 对弈 (GtpEngine + GoGame AI)
GtpEngine:GTP 协议围棋 AI 客户端
src/engine/GtpEngine.h/.cpp
| 功能 | 说明 |
|---|---|
| 启动引擎 | QProcess 启动 KataGo / GNU Go 子进程 |
| GTP 握手 | name / version / boardsize / komi |
| 局面同步 | clear_board → 逐子 play B/W <coord> |
| AI 落子 | genmove B/W → 解析坐标 |
| 坐标转换 | rowColToGtp("D16") ↔ gtpToRowCol("D4") |
| 容错 | 10s 超时 → pass;崩溃 → 自动重连 |
GoGame AI 自动落子:
game.setAiColor(GoBoard::WHITE); // AI 执白
game.requestAiMove(); // 手动触发 (或自动)
// 信号: aiMoveReady(row, col)
- 用户落子后,回合自动切换到 AI
- 200ms 延迟后异步执行 AI 计算(不阻塞 UI)
- AI 落子后恢复棋盘输入
- 悔棋在人机模式自动连悔两步
2.2 📜 打谱模式 (SgfParser + Kifu UI)
SgfParser:SGF 棋谱解析器
src/core/SgfParser.h/.cpp
| 功能 | 说明 |
|---|---|
| 加载 | loadFile(path) / loadString(sgf) |
| 解析 | 构建节点树 → root/children/parent |
| 遍历 | mainLineMoves() 收集主线全部移动 |
| 导航 | 沿 children[0] 走主线,children[1..] 为分支 |
| 导出 | toString() / saveFile(path) |
| 属性 | getProp("B") / getProp("PB") / multi-props |
坐标工具 (SgfUtil):
SGF 坐标: a=0列, a=0行(顶)
dd = (row=3, col=3) = 左上星位
pp = (row=15, col=15)= 右下星位
jj = (row=9, col=9) = 天元
打谱 UI:
◀◀ 首手 ◀ 上步 ▶ 下步 末手 ▶▶ 跳至: [N] [跳]
- 启动对话框选 “📜 打谱” → 弹出文件选择器加载
.sgf - 显示棋手名、结果、总手数
- 导航时从第0手完整重建局面到目标手数
- 棋盘在打谱模式下禁用点击
2.3 💾 棋谱保存 (SGF Export)
GoGame 落子历史:
game.totalMoves() // 总手数
game.moveHistory() // vector<{row, col, color}>
game.saveSgf(path, "Logic", "KataGo") // 导出 SGF
保存对话框支持:
| 功能 | 说明 |
|---|---|
| 棋手名称 | 可编辑,默认 “Black” / “White” |
| 文件名 | 自动生成 黑方_vs_白方_时间戳.sgf |
| 浏览路径 | 点 ... 选择保存位置 |
| 重命名 | 任意修改文件名 |
| 终局自动弹出 | Pass×2 / 超时 / 计目 → 自动弹出保存对话框 |
生成的 SGF 格式:
(;GM[1]FF[4]SZ[19]KM[6.5]
PB[Logic]PW[KataGo]
DT[2026-05-14]PC[14:30:00]RU[Chinese]
;B[dd];W[pp];B[pd];W[dp]...)
2.4 🎮 启动对话框
┌─ 围棋 — 新局设置 ────────────┐
│ 对局模式 │
│ [👤 人人对弈 ▼] │
│ 🤖 人机对弈 — 我执黑 │
│ 🤖 人机对弈 — 我执白 │
│ 📜 打谱 (加载 SGF) │
│ │
│ 棋盘: [19×19 ▼] │
│ │
│ ⏱ 计时 │
│ 主时间: [30] 分钟 │
│ 读秒: [3] 次 × [30] 秒 │
│ │
│ 贴目: [6.5] │
│ │
│ [开始对局] [退出] │
└───────────────────────────────┘
- 人人对弈:双方在棋盘上交替点击落子
- 人机对弈(执黑):用户先下,AI 执白
- 人机对弈(执白):AI 先下,用户执白
- 打谱:加载 SGF 文件,手动浏览棋谱
3. 架构变化
3.1 新增文件
| 文件 | 模块 | 说明 |
|---|---|---|
src/engine/GtpEngine.h/.cpp |
AI | GTP 协议客户端 |
src/core/SgfParser.h/.cpp |
Core | SGF 解析/导出 |
go_game.cpp (重写) |
App | 游戏主窗口 + 启动对话框 |
3.2 改造文件
| 文件 | 改动 |
|---|---|
src/core/GoGame.h |
+aiColor, +moveHistory, +saveSgf(), +requestAiMove(), +aiMoveReady 信号 |
src/core/GoGame.cpp |
AI 自动落子循环, 落子历史记录, SGF 导出 |
CMakeLists.txt |
+SgfParser.cpp 到 go-core, +GtpEngine.cpp 到 go-engine |
3.3 数据流 (人机对弈)
用户点击棋盘 → GoBoardWidget::clicked
→ GoGameWindow::onBoardClick
→ GoGame::play(row, col) ← 规则校验
→ 成功: 切换回合 + emit turnChanged
→ m_aiColor == currentPlayer?
→ YES: requestAiMove()
→ QTimer → GtpEngine::generateMove()
→ GoGame::play(row, col) ← AI 落子
→ emit aiMoveReady
4. 运行
go-game.exe
- 弹出启动对话框
- 选择模式 → 开始对局 / 选择 SGF
- 对弈中随时
💾 保存 - 终局自动弹出保存对话框
5. 依赖
| 组件 | 路径 | 说明 |
|---|---|---|
| KataGo (可选) | engines/katago-opencl/katago.exe |
OpenCL 后端 (Intel Arc) |
| 模型权重 | engines/model.bin.gz |
b18c384nbt-uec (93MB) |
| GTP 配置 | engines/gtp.cfg |
4线程, 300 visits |
| Mock 引擎 | MockEngine (内置) |
随机落子 — KataGo 不可用时自动回退 |
6. 相关文档
| 文档 | 内容 |
|---|---|
doc/gtp-katago-explained.md |
GTP 协议与 KataGo 通俗讲解 |
doc/game-plan.md |
对战 & 打谱开发路线图 |
doc/calibration-bugfix-postmortem.md |
标定 Bug 修复记录 |
doc/dual-model-selection.md |
双模型选型说明 |