课程定位: 围棋打谱辅助系统系列课程的 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
  1. 弹出启动对话框
  2. 选择模式 → 开始对局 / 选择 SGF
  3. 对弈中随时 💾 保存
  4. 终局自动弹出保存对话框

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 双模型选型说明