象棋基本殘局:已知的結論,以及可靠程度
簡而言之
- 我把英文與中文維基百科條目裡的 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 等級,供全力強度不再有教學意義時使用。