簡而言之

  • 我把英文與中文維基百科條目裡的 32 條象棋基本殘局結論做成局面,並用 Pikafish 與 chessdb.cn 的殘局庫各查核一次。全部 32 個都能在一份研究裡實際對下。
  • 棋書站得住腳:32 條中有 31 條與引擎一致,剩下那條的傾向也相同。有六個局面是錯的,而且全是我自己弄錯的。
  • 有三條的關鍵在於英文漏掉的一個擺法。車對馬炮只有在炮直接位於自己將的後方時才和棋;往旁邊移一個點,就是 12 步將死。
  • 資料庫涵蓋 8,705 種殘局、102 兆個局面。再往上、雙方士象全都完整時,棋書仍是唯一的依據。

西洋棋有一套每個棋手都要學的基本殘局表。王加車必勝。王加雙馬無法強制取勝。王加單象必和。象棋也有同樣一張表,如果你讀中文,你學的方式也一樣,來自各家殘局棋書,而它們的結論都相同。用英文,你只能拿到零碎的片段。

最好的那塊片段是英文維基百科象棋條目的殘局一節。三十幾條結論,以散文陳述,沒有任何一條附上出處。中文的對應條目叫做象棋勝和定式(象棋勝和定式)。它涵蓋同樣的範圍,每條結論配一張圖,並引用兩本書:金啓昌與楊典的《象棋殘局勝和定式》(北京體育大學出版社,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 個裡有 27 個符合。那個上限比聽起來寬鬆,因為一個盤上有八個士象的局面,受限程度幾乎和只有三個子的局面一樣:士只有五個合法格,象有七個,兩者都不能離開自己半場。它確實也是查表。順著它自己的最佳著法一著一著走下去,結論從不翻轉,距離也從不上升,這是任何搜尋都做不到的。

上面三條裡現在有兩條帶著資料庫確認的防守擺法,而兩處更正都是我的,不是棋書的。整輪掃過的結果是 32 條中有 31 條與 Pikafish 一致、一條傾向相同,其中 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 1,880 億
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,把轉換距離表與將死距離表一起算進去。數量在九個子時達到頂峰,過了十一個子就迅速下滑,這就是從內部看到的邊界樣貌。整個語料庫裡最長的強制勝局長 759 步:雙馬加一士對雙象、一馬與一兵。

雙方十三個子、士象全都完整,已經超出那張表的最後一行。那就是馬炮那一條。棋書的例勝,它的「常規取勝」,是對一個沒有人算過的局面所下的結論。

chessdb 唯一建過的十三子表是另一個殘局,也正是本文開頭那一個:三個兵加士象全,對士象全。30,445,843,365 個局面,是象棋裡有人解出的最深子力,紅方勝其中 48.00%。那個數字算的是所有擺法,而不是好的擺法,這也是棋書結論與勝率不是同一種主張的原因。

這兩個數字都值得拿來對照本頁頂端那張表。基本殘局是小的那些,而其中大部分已被徹底解決。一旦一條結論描述的是雙方都全力的情況,它描述的就是從來沒有人算過的子力,而棋書在那裡仍是唯一的依據。


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

基本殘局是一項你可以驗證的主張,這也正是把它們放到棋盤上而不是放進表格裡的全部理由:執棋書說會輸的那一方,看看你能撐多久。全部 32 條在 Mistboard 上是一份研究,一條一章,以該局面為根、引擎的著法線為主線,結論寫在下面。當下得不順時,Pikafish 就在機器人階梯的最高強度上,下面還有八個 Fairy-Stockfish 等級,供全力強度不再有教學意義時使用。