翻子是一次决策加一次掷骰

暗棋里一半的着法是翻开一枚背面朝下的棋子。一步棋,两件事:选哪枚翻,以及翻出来是什么。揭棋的揭子和翻翻棋的翻子是同一个问题换了棋子;全文用暗棋做例子,因为它的随机性最纯粹。

照搬国际象棋的复盘会给整步棋的胜率波动打分。翻开角落那枚棋,翻出对方的将,复盘就记你一个漏着。可那是一枚坏棋子,不是一个坏决策。

这就是 Mistboard 的暗棋复盘不显示厘兵损失的原因:它没法和翻出的棋子分开,放在那些可以分开的数字旁边,就只是噪音。

西洋双陆棋几十年前就解决了这件事

西洋双陆棋的软件会把每次掷骰和平均掷骰作比较,报告一个剔除运气后的结果,所以一份战报可以告诉你:你下得更好,但你输了。GnuBG 和 eXtreme Gammon 都这么做。扑克里的 all-in EV 是同一个思路。

国际象棋从没造过这些东西,因为它没有骰子。暗棋是象棋家族里带骰子的一员,却继承了国际象棋的工具,而那些工具里没有运气这一栏。

剩下棋子里的平均一枚

暗棋的随机性是数得清的。双方都看得到哪些棋子还背面朝下,整副棋的构成也是公开的,所以任何一次翻子,你都能列出那个格子上可能翻出的每一枚棋。这让诚实的基准变得可以计算:

对这个格子可能是的每一枚棋:
    把那枚棋放到格子下
    走这步翻子
    评估局面
按数量加权,取平均

这个平均值,就是你的决策在掷骰之前的价值。每次翻子三个数字:

每次翻子含义
played你所选翻子的平均价值
best当前最佳着法的同一种平均
realized实际翻出的棋子带来的结果
决策损失 decision loss = best - played      (实力,恒 >= 0)
运气   luck          = realized - played  (骰子,带符号)

运气为零,恰好就是翻出剩下棋子里的平均一枚。这是由构造保证的事实,不是引擎的看法。

开头那次翻子,逐一枚举

本文开头那盘棋的第 6 个半回合:我翻开 g3 上的棋子。此时还有二十七枚背面朝下,共十二种身份,每一种都通向一盘不同的棋。

g3 这枚棋可能是什么数量黑方胜率
黑卒×582%
红卒×446%
红车×245%
红象×244%
红士×243%
红炮×241%
黑马×234%
黑车×231%
黑象×129%
黑士×225%
黑炮×223%
黑将×113%

同一次翻子的价值从 13%(翻出我自己的将,深陷争夺之地)到 82%(我自己的卒,在那里既安全又有用)不等。加权平均是 45%。这个数字才是决策本身,准确率打分打的就是它。

高亮的那一行就是实际翻出的结果。实际 82,决策 45,运气 +37,而这份运气没有一点是我挣来的。

引擎对自己的骰子有偏心

最省事的捷径,是直接问引擎一步翻子值多少,再把差值叫作运气。我们的揭棋引擎演示了为什么不行:它高估自己的揭子,下出贪婪、赌博式的着法。而对一组身份已经确定的固定局面取平均,让概率节点完全不进搜索,偏心就无处安身。

决定你执哪色的那次翻子

开局第一次翻子决定你执哪种颜色:翻出什么色,你就执什么色。这次翻子的反事实会改变你自己的整支军队,所以这套拆分把"我分到哪一边"也计成运气。乍听之下不太对劲,想上十来秒就完全对了。

复盘画出什么

+37 的那次翻子 实际对局 每次翻子按平均 累积运气 红优 黑优
开头那盘棋。实线:实际进行的对局,从红方视角。虚线:同一盘棋,每次翻子都按平均值计。两线之间的差距就是运气。

优势图多了一条线。实线是实际发生的对局。虚线是同一盘棋,每次翻子都按平均的那一枚计分。两次翻子之间,两条线同步移动,因为普通着法对两个版本的影响完全一样。每到一次翻子,差距就恰好按那次翻子的运气变化,所以到终局时,两线的差距就是整盘棋的运气总和。

这也是为什么图里的虚线一路朝一个方向走,而实线上下翻腾。我的运气一次又一次落在同一侧,差距只增不减。只看决策的话,引擎几乎全程占优;一旦虚线认定这盘棋照理已经完胜,它就顶在最上沿不动了。

着法列表把这份拆分标到每一步上。每次翻子都带一个骰子标记,写着它的运气,就在评估值旁边;决策则单独打分:下图第 3 步就是那次 +37% 的翻子,虽然它帮我赢了棋,作为选择却被标为可疑。

复盘着法列表:每次翻子的评估值旁都有骰子标记标出运气百分比;第 3 步即 +37% 的那次翻子,作为决策被标为 ?!。
示例对局的着法列表。第 3 步是那次 +37% 的翻子,作为决策被标为可疑。

而准确率只按决策数字打分,翻得走运不会加分,翻得倒霉也不会扣分。这盘棋的总结和故事本身完全一致:引擎下得干净,我则不然,而结果却说了反话。

示例对局去除运气后的准确率总结:MistyBanqi 准确率 95%,零失误;dev-testing 89%,九次失准、两次错着、两次漏着。
同一盘棋去除运气后的总结。dev-testing 是我,用的是我的测试账号。

证据

开头那场胜利:对引擎的 156 个半回合,图把我供了出来。第 6 个半回合,那次值 45% 的翻子翻出了 82%。整盘棋里没有任何一个决策能把指针拨得这么远。

总账:我的翻子+76,引擎的 −12;翻子决策上我送掉 74 个百分点,引擎送掉 0。虚线里我大半盘都在输。实线里我赢了。棋子推翻了棋艺。整盘棋在 Mistboard 上公开,下方可以回放。

走到第 6 个半回合:平均价值 45% 的那次翻子,翻出了 82%。

反过来也一样。在我输掉的另一盘棋里,光是第 19 个半回合的一次翻子,就烧掉了 34 个百分点的胜率。那盘棋我的决策平平。旧式复盘会把那次翻子记为致败漏着。新的复盘把它记为:胜负在此刻被一件不由你选择的事定了下来。

五十二盘棋的证据

我们把这套拆分跑遍了站上最近 52 盘人类对 Misty 的暗棋。

52 盘人类对 Misty 的对局数值
人类战绩14 胜 33 负 5 和
胜局中偏向人类的净运气平均 +28 个百分点
负局中偏向人类的净运气平均 −9 个百分点

引擎更强,赢它通常都借了棋子的力。如果你赢过它,现在复盘会告诉你借了多少。

揭棋掷的是另一种骰子

揭棋用同一套办法,只是抽取的范围不同。揭棋的揭子从你自己剩余的暗子里抽,所以你知道颜色,不知道身份。暗棋的棋子对双方都是暗的,连颜色也是。范围不同,算法相同,斗兽棋的翻棋版本(翻翻棋)是第三家。它们每一个都有那条虚线。

写给实现者

三个选择让这些数字保持诚实。第一,反事实不能改变剩下棋子的构成。把翻开的格子从卒改标成炮,你就悄悄往棋局里加了一枚炮、拿走了一枚卒,局面因此偏移两枚棋子的分量,平均值也被抬高。所以实现里用的是交换:让反事实身份和一个确实藏着这枚棋的背面格子对调,隐藏棋子的集合始终恰好等于这盘棋的集合。

第二,一把有界的尺子。一切都用翻子一方视角的胜率来计:它可以在整盘棋上累加;直接终局的翻子恰好计 100、50 或 0;而一步翻进杀局的棋本来就没有厘兵值可言。搜索预算按节点数而非时间计,同一盘棋在任何机器上打出的分都一样。

第三,故意往少里算。决策上限只考虑引擎的第一选择,被引擎排在第二的更好着法会被漏掉,所以决策损失只会被低估。复盘可能漏报一个失误,但绝不会凭空捏造一个。

数字到哪里为止

这些胜率出自我们的暗棋引擎在固定搜索预算下的评估,而这个引擎同时也是这些对局里的对手,所以它自己的翻子被打成近乎完美,部分原因不过是它在认同自己的选择。看到"引擎零损失"时,请记住这一点。人类一侧的数字没有这个问题。

还有一条诚实的边界:逐点扣除运气,等于把胜率当成线性的,而它并不是线性的。方向可信。小数点后第二位不可信。

拿你自己的棋试试

在 Mistboard 打开任何一盘下完的暗棋、揭棋或翻翻棋,你自己的,或观战页上任何人的,然后请求电脑分析。运气数字会逐翻子出现在着法列表里,虚线则画出棋子们本该给你的那盘棋。

有时它承认你是被抢了。有时它把你的胜利收走。在对我旧棋的一轮扫描里,这两件事它都做了。

下暗棋暗棋规则