如何借助Stockfish perft函数排查走法生成器的非法走法
问题背景
在Unity环境开发国际象棋程序时,已实现perft(走法遍历计数)函数用于校验走法生成逻辑的正确性,同时使用Stockfish引擎作为基准结果做对照。在初始棋盘位置、搜索深度为3的测试场景下,绝大多数走法的节点计数与Stockfish结果一致,仅马跳步相关分支存在计数偏差。
Stockfish基准perft结果(depth=3,初始位置)
a2a3: 380 b2b3: 420 c2c3: 420 d2d3: 539 e2e3: 599 f2f3: 380 g2g3: 420 h2h3: 380 a2a4: 420 b2b4: 421 c2c4: 441 d2d4: 560 e2e4: 600 f2f4: 401 g2g4: 421 h2h4: 420 b1a3: 400 b1c3: 440 g1f3: 440 g1h3: 400 Nodes searched: 8902
自定义程序运行结果
a2a3: 380 b2b3: 420 c2c3: 420 d2d3: 539 e2e3: 599 f2f3: 380 g2g3: 420 h2h3: 380 a2a4: 420 b2b4: 421 c2c4: 441 d2d4: 560 e2e4: 600 f2f4: 401 g2g4: 421 h2h4: 420 b1a3: 420 b1c3: 460 g1f3: 460 g1h3: 420 Nodes searched: 8982
偏差规律:四个白方开局马跳步分支各多统计20个节点,总节点数多80个;进一步排查确认,偏差全部出现在黑方执行跳马操作后的子节点遍历环节,每次黑方跳马后会比合法值多生成20个节点。
定位误判非法走法的具体操作
- 缩小测试范围:直接将perft起始位置设置为白马跳马后的局面(比如先走
b1c3),将测试深度设为2,和Stockfish同局面同深度的perft divide结果做比对。此时不需要遍历其他无偏差的兵、象、后、王走法分支,排查范围直接缩小到该固定局面下的黑方走法生成逻辑。 - 走法列表差分:在上述
b1c3局面下跑depth=1的perft,导出你程序生成的所有黑方合法走法列表,和Stockfish输出的该局面合法走法逐行对比,先找出你额外生成的走法。结合「每次黑方跳马后多20个节点」的规律,优先检查黑方双马的走法生成逻辑,重点排查三类高频错误:- 马的位移计算未做边界校验,把棋盘坐标外的格子错误计入可走位置
- 马的走法未过滤己方棋子占位,把己方棋子所在格子判定为可移动/可吃子位置
- 走法合法性校验漏判「走子后己方王被将军」的情况:比如黑方跳马时,马原本遮挡了己方王被白方车/象/后的攻击路线,跳马后王暴露在攻击下,这类走法本应判非法但未被过滤,后续衍生的子节点计数刚好匹配20左右的固定差值。
- 逐层下钻定位:如果depth=1的走法列表和Stockfish完全一致,就升深度到2,对每个黑方走法单独统计子节点数,找到计数差为20的具体黑方走法,再将走完该步的局面设为新的测试起点,重复上述差分流程,直到定位到具体的逻辑bug。
高效perft调试通用方案
- 优先实现perft divide功能:给现有perft函数增加分支计数能力,遍历当前节点的所有生成走法时,单独统计执行每个走法后的子节点总数,输出「走法串: 节点数」的对照表,直接和Stockfish的divide输出逐行匹配,哪行走法计数不符就直接定位到对应分支,无偏差的分支直接跳过不用排查。
- 梯度递进测试:不要一开始就跑深度5以上的长序列perft,从depth=1开始逐深度提升测试深度,每提升一个深度就和基准结果做一次校验,哪个深度出现计数偏差,问题就出在对应深度的走法生成、合法性校验环节,避免高深度下节点量爆炸导致排查范围失控。
- 增加内置校验断言:在走法生成逻辑中加入强制校验规则:每生成一个走法,执行走法后立刻检查当前走子方的王是否处于被攻击状态,如果是则直接判定为非法走法丢弃;所有走法的目标坐标先过边界校验,不在棋盘合法坐标范围内的走法直接过滤,从源头减少非法走法漏判。
- 固化复现用例:一旦定位到存在计数偏差的走法分支,立刻记录从初始局面到该分支的完整走法序列,写成独立单元测试,调试时直接加载走法序列终局的棋盘状态,不需要每次从初始位置跑全量perft,大幅缩短调试迭代周期。
内容的提问来源于stack exchange,提问作者Elia Giaccardi Old
相关产品推荐
相关产品推荐

