西洋棋有一張每位棋手都要學的基本殘局表。王加車能勝。王加雙馬無法強勝。王加主教是死局。象棋也有同一張表,如果你讀中文,你會以同樣方式學到它,從各家殘局手冊裡,而它們的結論全都一致。而在英文世界裡,你只能拿到碎片。

最好的碎片是英文維基百科象棋條目的殘局一節。三十幾條結論,以散文敘述,一條引註也沒有。中文的對應內容是一篇叫做象棋勝和定式(象棋勝和定式)的條目。它涵蓋同樣的範圍,每條結論配一張圖,並引用兩本書:金啓昌與楊典的《象棋殘局勝和定式》(北京體育大學出版社,2008),以及薛榮賢的《象棋入門實用指南》(2009)。這兩本書我都沒讀過。下文我說「手冊」時,指的就是那兩篇維基條目:一篇是手冊傳統的壓縮報告,另一篇讓你看見局面。

其中三十二條結論列在下面,每一條都做成一個局面並經過兩次核對,而且每一條都是一塊你可以對著核對它的引擎實際走下去的棋盤。這份研究放在Mistboard上,一條結論一章。

基礎:什麼能贏士象全

車是象棋最強的子力,也就是換了名字的 rook。標準的士象全保有四個防守子力:將在原位、雙士、雙象,且兩個士正排在將的前方,讓炮無法拿它們當炮架。

單車對上這樣的防守是和棋,而且現在是有證明的和棋:資料庫說和棋,Pikafish 給出的評估值正好是 0。

三卒能勝。15 步內強制將死。

雙馬也能勝,25 步。

和棋

15 步將死

同樣的防守,同樣的一方行棋。兩個結論都出自資料庫,而不是搜尋。任選一局執黑,試試能不能守住。

依你能找到的任何子力價值表,一個車都比三個卒值錢,也比兩個馬值錢。但在這個殘局裡,它比兩者都不值。卒不能後退,聽起來像弱點,結果卻成了它們取勝的原因:三個卒壓向九宮,無法被趕回去,而防方也沒有辦法把它們兌掉。

同樣的邏輯反過來也成立。五個兵全都停在底線,對上光將,是和棋。五子對零子。它們不能後退,而且已經貼著邊,所以既無法強制將死,連困斃也做不到。

和棋

五子對一子,而局面中不存在殺法。執紅方,試試能不能贏。

決定這些殘局的是子力位置,而英文那一節剛好有一對圖直接把這點呈現出來。

單車對馬雙象。防方擺在它的堡壘裡(馬在 e8,象在 i8 與 g6)就能守住,資料庫也確認守得住。把 g6 的象移到 g10,紅方就勝,從 1.Rb7 開始。

和棋

紅勝

全部差別只在盤面右側那個象往上挪了四格。守住第一個,攻破第二個。

雙方子力相同、攻方相同、行棋方相同,結果卻翻轉。這一對之所以能在翻譯中存活,是因為有人把它畫出來了。下面三節,則是沒有人畫的那些。

取勝的條件

三十二條結論中有三條,取決於英文並未傳達的一項指示。每一條裡都有一個子力位置決定結果,而點明它的那句話正是被漏掉的那句。

車對馬炮,和棋只在一格之間

英文那一節寫道:「車對雙小子、無防守子力:和棋,但需要良好的防守位置。」

我造了八個這樣的局面。Pikafish 在全部八個裡都找到強制將死,從我能構造出最頑強的那一個算起是 15 步。八個全部如此不可能是搜尋的假象,所以我把這一條寫成有爭議後就往下走了,那是錯誤的判斷。

中文條目畫出了局面,並多加了一句英文沒有傳達的指示:炮要正好擺在自己將的後面,而馬要盡量離將遠。

和棋

12 步將死

只差一格。左邊炮正好在自己將的後面,局面是和棋;右邊它移了一格,紅方 12 步將死。任選一局執黑。

那個局面在雙方任一方行棋時都是資料庫判定的和棋,而 Pikafish 搜到深度 40 讀出 +0.10。炮在將後既護住底線,又不會被貼著將抽吃,這就是整個堡壘。把它橫移一格,同一個局面就是 12 步將死,而我試過的其他每一格也都是負,從隔一格到最遠的角落都一樣。

結果車本身的位置完全不重要。我從 a10、e5、i1 和 a1 都測過,每一個依然是和棋,這正是它之所以是堡壘、而不是碰巧擺對的原因。馬的規則比手冊那句「盡量離將遠」寬鬆:它只要避開單一車著能攻擊的格子就好。在 b7、c7、d7、e7、d6、d4 或 d2,局面守得住。在 d5,也就是車自己那一線上,車直接吃掉它,5 步將死。

我那八個局面的炮全都擺在別處。英文裡的結論本身是對的。「需要良好的防守位置」承擔了整個結果,卻什麼也沒告訴你。

車炮對車,「中路」不是一個格子

英文那一節寫道:「只要防方的車佔住中路,車加炮無法勝單車。」

那讀起來像是一條可以核對的規則。我的局面符合它:黑方的車在中路、在原位、正好在自己將的前面。Pikafish 28 步將死。

它之所以能將死,是因為那句話沒提到的那個子。炮吃子要隔一個子跳,所以它需要炮架,而且不管炮架是誰的。紅方的炮滑到中路、落在黑車下方,立刻將軍,拿那個黑車當炮架,拿它後面的黑將當目標。防方賴以求和的子,正是那個炮車架。

把防方的車沿著那一路往下走,資料庫會在上頭畫出一條界線:

和棋

紅勝

兩局的防方車都佔住中路。只有較低的那一局是和棋。最後的殺法是海底撈月,在那一路的底部完成。

紅方如圖佈置時,防方的車在第 5 線及以下是和棋,深度 40 時 0.00,在第 6 線及以上則是負。只差一線。

紅方要取勝必須有兩件事成立,而兩件都跟紅方有關。炮必須能落到中路並在那裡存活,在圖中就是落在第 5 線,那裡有紅方自己的車保護它。把那個炮移離車所在的橫線,挪到第 3 或第 7 線,原本每一個勝局都變回和棋。而紅方需要中路的底部,因為這個殘局賴以取勝的殺法海底撈月(海底捞月)就在那裡執行:車走到那一路的底端,自己的帥在它後面,防方的車最後被夾在兩將之間,離開那一路就會造成白臉將,留在那裡就會被吃。

防方站在自己半邊的中路上,兩件事哪一件都擋不住。佔住一路,說的是一個子所控制的那些格子。我把它讀成關於它所站那一格的主張,造了一個符合字面的局面,結果被 28 步將死。

馬炮對馬,再深的搜尋也定不下來

英文那一節寫道:「馬+炮+四個防守子力對一個小子[加]四個防守子力:小子是馬則勝……小子是炮則和。」

十三個子。chessdb 只建過一張這麼大的表,而且是另一組十三子,對這一組做即時查詢回傳的是未知。但這並不代表只剩搜尋。減到十子的 KANCKAABBN,也就是強方留一個仕的那個簡化,這張表存在:24,251,768,055 個局面,紅方勝其中 85.48%。支撐這個結論的是這道階梯,而不是搜尋深度。

搜尋解決不了它。深度 26 給 +2.24。深度 42 給 +2.47,依然沒有殺,而防方馬的六種不同擺法落在 +2.32 到 +3.03 之間,沒有一個變成決定性的。一個漫長的技術性勝局在固定深度下看起來就是這樣,而一個紅方單純較優卻是和棋的局面也是這樣。

有兩項證據仍然指向書上是對的。引擎自己就把這條結論的兩半分開了:把防方的馬換成炮,也就是同一句話裡和棋的那一半,同樣的深度讀出 +1.26。那個落差就是書上的區分,出現在一個從未被告知這件事的數字裡。

接著我讓 Pikafish 在深度 32 自己把這個局面走完。紅方花了 33 步調度,雙方都沒有吃子,第 34 步吃掉一個士,四步後吃掉第二個,然後持續轉化到第 56 步,局面降到十子,回到資料庫的範圍內。黑方行棋,每一個合法應手都是負。

第一次吃子就是整個問題的關鍵,因為有一條在西洋棋裡沒有對應的規則:自然限着(自然限着)規定雙方各走 60 步而完全沒有吃子即判和。一個需要 70 步技術的例勝,不是你能走出來的勝局。這一個在第 34 步就突破了,還剩 26 步餘裕。

象棋也把另一扇門關上,而那一扇對西洋棋棋手咬得更深。長將在這裡算:將軍的一方必須變著,否則判負。在西洋棋裡這是把敗勢救回來的標準手段,所以這個本能不只是幫不上忙,而是直接把分數送出去。這兩條規則一起框住了本文裡每一個殘局。防方無法靠將軍脫身,攻方也不能無限磨下去。兩者在西洋棋裡都沒有可以對譯的東西,這正是英文摘要會把它們漏掉的原因。

這仍然不是這一條的證明。黑方也是 Pikafish,早一點更好的防守也許能守住;這裡沒有任何東西排除這一點。它確定下來的,是過去要靠我信任的那一部分。最後的勝利不是引擎在深度 32 宣告的殺、而我選擇相信,而是一個可以被證明為負的局面,所以引擎的著法線轉化成精確的東西,而不是它自己的意見。引擎依然拒絕從根局面確認書上的結論。但它現在走進一個確定的局面。如果你想體會 34 步的擠壓有多薄,就執防方走這一條,而和棋的那一半只差一個子。

未解

十三個子,沒有任何殘局庫涵蓋的一條。黑方有 60 步的時間存活而不丟子。

把子力減下去,它就不再是搜尋問題。chessdb.cn 公布了這個殘局各種簡化的精確表,而它們改變了書上那句話的意思。把紅方自己的仕相拿掉,只留帥、炮、馬對士象全:5,492,247,706 個局面,紅方勝其中 22.31%。還給紅方一個仕,就升到 85.48%,共 24,251,768,055 個局面。第二個仕再加一個百分點。用一個相代替那個仕則加七個百分點。

所以那句話裡攻方那邊的「四個防守子力」是有實際作用的,而真正關鍵的數字是一。一個仕就撐起了勝局,相幾乎是白送的。我是先用 Pikafish 得到這個結論的,在知道那些表存在之前就在深度 30 跑了同一道階梯,而排序結果一樣。

把同一個局面從另一邊切,答案就翻轉。雙方都留著士象全,改成把防方的馬拿掉:十二個子,共 10,453,951,719 個局面,而 chessdb 建了這一張。紅方勝 92.71%,最長的殺是 69 個半著,而表中完全沒有記錄任何負。一個簡化說勝局靠的是紅方自己的仕。另一個說,一旦黑方沒有東西可以反抗,勝率就近乎全面。書上真正談的那組子力就落在兩者之間,不在那 8,705 張表的任何一張裡,而對它做即時查詢回傳的是未知。

六個詞,兩個桶

上面三節有兩節有一個共同點,而那就是詞彙。中文殘局手冊不會只把結果評為勝或和。它們用一把有六級的尺來評:

結論 它主張什麼 英文歸類
必勝 這組子力從任何局面都能勝
例勝 以標準技術取勝
巧勝 只有從特定局面才能勝
難勝 難以取勝,但這和和棋不是一回事 和棋
例和 以標準技術求和 和棋
必和 從任何局面都能求和 和棋

六個之中有五個能在翻譯中存活。第六個必須選一邊站,而上面三節裡有兩節就落在它身上。

中文條目把車對馬炮歸在第四級,難勝。英文那一節把它歸為「和棋」。殘局庫站在中文這邊:車在盤面上幾乎任何地方都能取勝,只被單一種防守陣形擋住。讀到「和棋」的棋手會隨意防守,然後 15 步被將死。讀到「難勝」的棋手知道,要找的東西就只有一個。

第二節從另一個方向落在同一級上。中文表把車炮對單車列為勝和(胜/和),然後用一整段講哪種是哪種。英文保留了其中俐落的那一半,把那一段丟掉了,而那一段才是關鍵。

局面才是事實。結論只是有人從一整架標籤裡挑一個貼上去,而把標籤翻出來卻不帶那整架,就失去了它大部分的意義。

這些結論的可靠程度如何

這張表裡的結論屬於三個等級之一,而它們遠遠不等價。有些是已證明的:資料庫算完了整個殘局,答案是一次查表。有些是搜尋出來的:引擎在我跑的深度上同意。那是證據,不是證明。剩下的是斷言,出自一本沒有引註的手冊。

把它們分類花了兩輪。

我把英文那一節的 32 條結論取出來,為每一條做了一個代表性局面,然後在深度 26 問 Pikafish。

深度 26,32 條結論  
引擎同意 29
書上說和棋,引擎找到強制勝 2
書上說勝,引擎走不完 1

接著我用第二種方式再核對同一批局面。象棋有雲端殘局資料庫,是查答案而不是搜尋答案:chessdb.cn 對子力足夠少的局面回傳勝、和或負,外加一個距離數。我的 32 個裡有二十七個符合。這個門檻比聽起來寬鬆,因為一個盤上有八個士象的局面,受限程度幾乎和只有三個子的局面一樣:士只有五個合法格,象只有七個,而且都不能離開自己半邊。它也確實是查表。沿著它自己的最佳著法一步步走,結論從不翻轉,距離也從不上升,沒有任何搜尋會這樣。

上面三節裡有兩節現在帶著資料庫確認過的防守佈置,而兩次更正都是我的,不是手冊的。整輪掃過的結果是 32 條裡有 31 條與 Pikafish 一致、1 條偏向一邊,其中 32 個局面裡有 27 個小到能進資料庫,且全部 27 個都是精確答案。

核對在我造的局面裡抓到什麼

核對抓到的其餘問題都是我的。三個不合法的局面,我讓兩個將帥在一條開放的直線上互相照面,這在解析上沒問題、看起來也沒問題,但不是合法的象棋。兩個我標成例和的局面,Pikafish 宣告 2 步將死,結論是對的,是我的快照是垃圾。還有一個是局面沒問題,但我對它的解釋是錯的。

書上說三個兵能勝士象全,也就是兵在第 7 線。把同樣三個兵退後一線,勝局就蒸發了。我把這寫成先手節奏的問題,差一步而差了全部。並不是。黑方的象在整個盤面上只能站在七個點上,而從 e8 出發,它正好能到 c6、g6、c10 和 g10。第 6 線上的三個兵裡有兩個正停在象位上。黑方在那裡不需要技術,只需要吃一子。

15 步將死

黑方吃一子

「高兵」所保護的,就是象的觸及範圍。第 7 線上的兵在棋局的任何時刻、從任何格子,都不可能被象吃掉。退後一線,就有兩個站在象能到的格子上。

我的六個局面有問題,而核對把六個全抓出來了。手冊的結論一條也沒有。

邊界

資料庫能把一個殘局定下來,是因為有人把它整個算完了。chessdb.cn 為象棋建了 8,705 張這樣的表。沒有任何英文頁面說明它們的規模,所以列在這裡。

子數 表數 局面數
3 4 18,858
4 36 690 萬
5 169 13 億
6 615 1880 億
7 1,582 7.0 兆
8 2,494 26.1 兆
9 2,169 33.9 兆
10 1,227 23.6 兆
11 368 10.4 兆
12 40 1.0 兆
13 1 304 億

102,230,279,715,348 個局面。磁碟上 25.56 TiB,把距離轉化表與 DTM(將死距離)表一起計入。數量在九個子時達到高峰,過了十一個子之後迅速下滑,這就是從內部看到的邊界模樣。整個語料庫裡最長的強制勝有 759 個半著:雙馬加一仕對雙象、一馬、一卒。

每方十三個子、雙方防守完整,已經超出那張表的最後一列。那就是馬炮這一條。手冊的例勝,它那句「常規取勝」,是對一個沒有人算過的局面下的結論。

chessdb 建過的那唯一一張十三子表是另一個殘局,也正是本文開頭那一個:三個兵在士象全之後,對上士象全。30,445,843,365 個局面,象棋裡有人解出的最深子力,而紅方勝其中 48.00%。那個數字計入子力的每一種擺法,而不只是好的那些,這也是為什麼書上的結論和勝率不是同一個主張。

這兩個數字都值得放在本頁頂端那張表旁邊一起看。基本殘局是小的那些,而其中大多數已被完整解決。一旦一條結論描述的是雙方全力,它描述的就是從來沒有人算過的子力,而在那裡手冊仍然是唯一的來源。


這 32 條,每一行都附上來源與出處,都在 xiangqi-endgame-corpus.ts 裡。核對工具是 verify-xiangqi-endgames.ts;把它指向一個 Pikafish 執行檔,它就會把書上的結論和引擎的讀數並排印出來,附上主要變著。加上 --tablebase,它會為每一個小到有精確答案的局面補上那個答案。

基本殘局是一個你可以測試的主張,這也是把它們放到棋盤上而不是放進表格裡的全部理由:執書上說是負的那一方,看看你能撐多久。32 個全都是 Mistboard 上的一份研究,一條一章,以該局面為起點,引擎的著法線作為主線,結論寫在下面。當事情走得不順時,Pikafish 就在對戰機器人階梯上以全力待著,下面還有八個 Fairy-Stockfish 等級,供全力不再有教學意義的時候使用。