Benedict 象棋(中國象棋):先手勝率 78%,全因一步棋
簡而言之
- Benedict 象棋(中國象棋)把 Benedict Chess 的規則放到象棋棋盤上:沒有吃子,你所移動的子力攻擊到的每一枚敵方棋子都會加入你。
- 它沒有殘局、炮永遠不會變弱,而且沒有和棋:54 盤引擎對局與 6,000 盤隨機對局中都沒有出現過。大約 28% 的合法著法會當場輸掉。
- 先手勝率 78%,而象棋約為 55%。幾乎全部來自同一個開局:把炮提到第五或第六線。
- 派餅規則可以修正它:黑方可以在第一著之後接手紅方的局面,這會逼紅方走向四個接近均勢的開局。紅方是否有必勝著法仍是未解問題;13 步之內並不存在。
Benedict Chess 沒有吃子。當你移動一枚棋子時,它從新位置攻擊到的每一枚敵方棋子都會改變顏色並加入你。你要靠把敵方的王變色來取勝。W. D. Troyka 在 2002 年發表了它,並以換邊投敵的 Benedict Arnold 為名。
就我所能查到的,還沒有人把這條規則放到象棋棋盤上。Chess Variant Pages 收錄了 7,383 種遊戲,其中 43 筆使用象棋棋盤的條目與十幾筆變色機制的條目是互不相交的集合;Ludii 的 2,330 種遊戲與 Fairy-Stockfish 的變體清單也是一樣。那些差一點的例子值得一提,因為多數只差一個關鍵環節。Desertion Chess 的變色條件是走到棋子旁邊而不是攻擊它,而且保留吃子。Andernach 翻轉的是吃子的一方而不是被吃的一方。Cleopatra Chess 是 Troyka 自己的規則,但限制在單一種棋子上。而象棋自己的翻子遊戲,暗棋與揭棋,翻開棋子是為了揭示它是什麼,這跟改變它屬於誰完全是兩回事。
象棋有隔炮架吃子的炮、被鎖在九宮九個點裡的將帥,還有不能過河的象,所以這個移植不可能是機械式的。它逼出了四個沒有預設答案的設計決定。
接著我做了一個引擎,想知道結果是否公平,而它並不公平。先手能贏下 78% 的對局,而普通象棋約為 55%。這幾乎全都歸結到第一著就能走出的某一步炮,而這比一個單純就是失衡的遊戲要好修得多。
規則
移動一枚棋子。它從落點攻擊到的每一枚敵方棋子都會加入你。
有件事值得早點注意:既有的攻擊態勢什麼都不會發生。只有移動的那枚棋子才會造成變色,所以一輛已經瞄住某枚棋子的車必須再走一次才能拿下它。威脅只能靠走子製造,永遠無法靠等待。
試著玩
雙方都由你操作,背後沒有引擎。它執行的是本文每一張棋圖都據以檢驗的同一套規則核心,所以它不可能允許本文說是不合法的著法。
棋盤下方的計數就是本文後面一再回頭談的那個數字。它從零開始,而且不會一直待在零。
這對象棋造成了什麼
永遠沒有殘局。全部 32 枚棋子整局都留在棋盤上。沒有簡化,也沒有兌子,而殘局庫在原理上就不可能,因為狀態空間永遠不會縮小。象棋殘局理論,也就是象棋理論的絕大部分,完全無法沿用。
炮不再走下坡。象棋的炮需要炮架才能吃子,所以隨著棋盤變空它會變弱。這裡棋盤永遠不會變空,第 80 著的炮和第 3 著的炮一樣危險。擬合出的數值也同意:炮算出來是 657,而象棋的子力價值表大約放在 450,這已經接近車的 900,足以改變你使用它們的方式。
士與象不再防守任何東西。它們的存在是為了防吃子,而這裡沒有吃子。它們仍然能封線、能當炮架,所以並非全無用處,但它們原本被設計來做的一切都消失了。
對局很短,而且沒有人和棋。中位數 29 步,而象棋是 80 到 100 步。在最強設定下的 54 盤對局中沒有一盤和棋,而在 6,000 盤完全隨機的對局中也沒有一盤,這是這個說法更強的版本:不是好手在這裡會避免和棋,而是局面除了走向勝負以外無處可去。一個沒有子力可兌、也沒有殘局可磨的遊戲不可能後繼無力。
大約 28% 的合法著法會立刻輸掉。這是下面這些對局共 1,696 步的平均值,而且值得說精確一點,因為平均值並不代表典型局面:中位數局面只有 10% 的著法會輸,平均值是被少數極其鋒利的局面拉高的。平均值也會爬升,從一局前五分之一的 24% 升到最後五分之一的 37%。不會發生的是有一個平靜的開局階段可以慢慢佈局。即使是前五分之一,也平均有四分之一的著法會當場輸掉。
設計這個遊戲
將帥仍然不得照面。象棋禁止兩將處在同一直線而中間無子,我保留了這一條。另一種做法是把白臉將設為勝利條件,聽起來更好但會壞掉:在 Benedict 規則下只有「移動」的那枚棋子算,所以打開這條線並不算勝,結果將帥就這樣互相照面而棋局繼續下去,直到有人把將帥沿線挪一步來宣稱勝利。
保留這項禁令毫無代價,而且產生了象棋沒有的東西。一枚被牽在將帥同線上的棋子無法被武力移除,因為這裡沒有任何東西會移除任何東西,而把它變色也不會解放它,因為變色是原地換色。你可以贏下那枚棋子,卻仍然不能移動它。只有將帥走離該線才能解放它,所以這個牽制持續的時間,恰好等於牽制方把自己的將帥留在那裡的時間。
士與象綁定的是它所在的區域,而不是擁有者的區域。一枚變色後的士會待在敵方九宮裡,而綁定擁有者的規則會讓它永遠凍結在那裡,那就是一枚死棋加上一條無趣的規則。綁定它所站的九宮,它就還能繼續發揮。從初始佈局來看,兩種解讀是完全相同的,因為每一枚士本來就站在自己的九宮裡,所以這個一般化毫無代價,還把一枚死棋變成了攻擊子力。
兵卒綁定的是擁有者,這是相反的判斷。兵卒的前進方向必須跟著它現在的擁有者,否則變色後的兵卒會永遠朝著自己這一方的反方向行軍。把這兩者弄反,就是那種會悄悄毀掉遊戲的錯誤。
和棋需要一個進展計時。因為沒有吃子,幾乎每一步都是可逆的。單靠三次重複並不足夠,所以計時只在兩種唯一不可逆的事件上重置:一次變色,或一次兵卒的走動。
測試它
現有的引擎都不能下這個。Pikafish、Fairy-Stockfish 以及我自己的 fog 引擎全都寫死了吃子規則,而 Fairy-Stockfish 唯一的變色機制(flipEnclosedPieces)翻轉的是被落子包圍的棋子,黑白棋式的,那是不一樣的規則。
於是:一個 Rust 寫的 alpha-beta 引擎,帶置換表、主要變例搜尋、後期著法縮減,以及針對變色的靜態搜尋。我沒有直接沿用象棋的子力價值,而是用 2,500 盤自我對弈的結果做邏輯迴歸擬合,它在直接對抗賽中擊敗了我手動挑的數值。
我不打算引用擬合出來的數字。早先的草稿引用了,並把它們讀成棋子價值的量測結果,這超出了在 8,000 個彼此相關的局面上做邏輯迴歸所能支撐的範圍:早先用四分之一資料做的擬合,有好幾個係數後來符號整個反轉。留得下來的是它同意的排序,也就是士與象在這裡幾乎毫無價值,而這一點根本不需要迴歸,因為它直接來自這裡沒有東西需要防守。
規則核心是以一條鏈來驗證的,這很重要,因為後來出錯的一切都出在量測而不是規則。一個 JavaScript 原型與我原本就在用的象棋函式庫 elephantops 做了差異測試,涵蓋 720,947 次棋子幾何比對,零分歧。Rust 引擎在 113,000,000 個局面內完全重現該核心的著法數。一個 TypeScript 移植版重現了兩者。
有三個結果在後來每一次改動中都成立,因為它們都不依賴引擎的判斷力:
| 測試 | 結果 |
|---|---|
| 隨機對隨機,6,000 盤 | 先手 50.1%,0 盤和棋 |
| 從初始佈局開始的未縮減證明搜尋,208 億個節點 | 13 步之內沒有必勝著法 |
| 當場輸掉的合法著法比例 | 平均 28%,中位數 10%,共 1,696 步 |
這三者都不會隨引擎改變而變動,這就是我比勝率更信任它們的原因。
這個引擎強到足以用來評斷一個遊戲嗎?它每秒搜尋約 160 萬個局面,幾秒鐘內能到深度 8 或 9。跟成熟的西洋棋引擎差得遠。但它不需要那麼強。
上面那三個結果完全不取決於它下得多好。一個是隨機著法對照組,一個是證明,一個是合法著法的計數。這個證明值得說精確一點,因為普通的搜尋做不出證明:這個引擎使用後期著法縮減,會把較後面的著法搜得較淺,所以一次普通搜尋找不到勝著,只說明了它沒有看遍所有地方。把縮減與靜態搜尋關掉,並在將死門檻上以是非題的方式提問,才真的構成證明,這就是上面那 13 步的意思。而那 78% 也針對這裡真正重要的性質做過檢查,也就是它會不會隨引擎變強而移動。它幾乎不動:每著 300k 節點時是 77.8%,2M 時是 75.4%,兩者相差七倍,而抽樣誤差則有好幾個百分點的上下。
跨引擎強度的穩定性是我信任的檢驗,而它單獨還不夠。這項量測的早先版本用六步隨機開局庫來讓對局多樣化,得到 52.7%,而且在橫跨同樣十倍範圍的三種引擎強度下都維持住。它之所以穩定,是因為開局庫正好花掉了那個被量測的先手:六步隨機著法在任一方引擎走第一步之前就把優勢扔掉了,所以無論底下的遊戲是什麼樣,結果都會漂向均勢。壞掉的溫度計在每個房間都讀到室溫。上面那 78% 沒有用開局庫,而是靠評估值抖動來取得多樣性。
更多算力能買到什麼,是有界而且相當清楚的。位元棋盤與更好的著法排序能到深度 12 到 14,那是我在談深層戰術或真正的開局理論之前想要達到的水準。學習式評估是昂貴的選項,而且很可能是錯的選項:先前在其他變體上的兩次嘗試都在還沒追上它們原本要取代的手工評估之前就飽和了,而這兩次失敗都不是錢的問題。
所以這篇文章描述的是俱樂部強度下的遊戲。我保留下來的主張,是那些在任何強度下都成立的。
引擎、核心與測試框架共有數千行 Rust 與 TypeScript,都放在 GitHub 上的 github.com/brianhliou/benedict-xiangqi,連同那 54 盤對局與完整的開局表。本文中的每一個數字都來自 cargo run。
一步炮決定一切
先手能贏下 78% 的對局。這是從初始佈局在策略內量測的,沒有開局庫,兩種顏色輪流執先:每著 300k 節點時,紅方執先得分 80.5%,黑方執先 75.0%,各 100 盤;在 2M 時分別是 82.5% 與 68.3%,各 60 盤。兩個方向在兩種預算下都遠高於均勢,所以優勢跟著先手而不是顏色,這正是它成為關於這個遊戲的事實而非關於我程式碼的事實的原因。在較高預算下紅方看起來確實是這組配對中較好的那一半,但 60 盤帶有約十二個百分點的誤差,所以我現在還不會從中讀出任何結論。
接著我分別量測了紅方全部 42 個合法首著,而診斷結果變了形狀。
全部 42 個合法首著,每個 24 盤,依紅方隨後的得分排序。
| 著法 | 紅方得分 | 著法 | 紅方得分 | 著法 | 紅方得分 | ||
|---|---|---|---|---|---|---|---|
| c1a3 | 0.0% | h1g3 | 25.0% | a4a5 | 41.7% | ||
| e1e2 | 0.0% | a1a3 | 27.1% | g4g5 | 43.8% | ||
| g1i3 | 0.0% | b3b2 | 27.1% | b3b4 | 45.8% | ||
| b3a3 | 4.2% | h3h2 | 29.2% | b3b7 | 50.0% | ||
| b3c3 | 8.3% | b3d3 | 29.2% | d1e2 | 54.2% | ||
| h1i3 | 12.5% | i4i5 | 29.2% | e4e5 | 54.2% | ||
| h3f3 | 16.7% | b3f3 | 33.3% | a1a2 | 58.3% | ||
| b3g3 | 18.8% | b1a3 | 35.4% | h3h7 | 60.4% | ||
| h3c3 | 20.8% | c1e3 | 35.4% | h3h4 | 60.4% | ||
| i1i2 | 20.8% | b3e3 | 35.4% | b3b6 | 70.8% | ||
| h3g3 | 22.9% | c4c5 | 35.4% | f1e2 | 72.9% | ||
| i1i3 | 25.0% | h3e3 | 37.5% | h3h5 | 79.2% | ||
| h3i3 | 25.0% | h3d3 | 39.6% | b3b5 | 87.5% | ||
| g1e3 | 25.0% | b1c3 | 41.7% | h3h6 | 91.7% |
最好的兩個是同一個想法:把炮提到第五或第六線。得分為零的三個分別是一步象、一步馬走到邊路,以及將帥往中間上一步。
紅方的首著價值從零到 92% 不等,而 42 個當中有三個把全部 24 盤都直接輸掉。所以那 78% 不是「在這個遊戲裡一個先手值 78%」。而是「引擎總是找到那唯一的強力開局」。
象棋的炮需要在它與目標之間恰好有一枚棋子。黑方的初始局面免費提供了那枚棋子,而且在恰當的位置上。這個威脅之所以存在,是因為象棋的初始佈局剛好落在 Benedict 規則能利用的地方。
它下過的每一盤棋
最強設定下的全部 54 盤對局,未經挑選。先手贏了其中 46 盤,而它輸掉的那八盤才是值得打開來看的。
對局的結束不是因為有人出錯。它們結束是因為有一方走完了所有不會輸的著法,而這類著法的數量在棋盤上出現任何看得見的變化之前,已經爬升了二十步。
用派餅規則讓它公平
那 42 個開局之間的差距正是讓這件事可以挽救的原因,而修正方式在其他遊戲裡早已存在。
派餅規則的運作方式就像兩個小孩分蛋糕:一個切,另一個挑。紅方走第一著。黑方接著選擇要繼續當黑方,還是改為接手紅方的局面。這個單一的選項改變了紅方在最佳化的目標,因為當黑方可以直接把它拿走時,走出最強的開局就變得毫無意義。
於是紅方被推向最接近均勢的那一著,而 42 個當中有四個落在那裡。其中兩個是炮放棄自己最好的那格,另外兩個是棋盤上最平靜的發展著法。
炮上一步,離造成殺傷的第五線還差得遠。
炮直接殺到底,此刻兌現,放棄了持續的威脅。
仕起動,讓出d1。將帥同線從第一著就已在局中。
中兵:唯一不算失著的安靜出動之著。
被迫走其中之一,這個遊戲就會落在 50% 附近。不是靠把優勢壓下去,而是靠取消紅方使用它的能力,而這比聽起來更乾淨的修法。Hex 在每一種棋盤尺寸下都是可證明的先手必勝,卻在世界各地被競技性地下著,完全就是因為有交換規則。
誠實的附帶說明是樣本。那四個數字各來自 24 盤,寬到我不會把它們彼此排序,卻又窄到足以說從 0% 到 83% 的差距是真的。在足夠的樣本下確認其中之一維持在 50% 附近,是尚待完成的工作。
派餅規則在不動到鋒利度的情況下修正了公平性。輸著比例是規則的性質,不是誰領先的性質,所以它完全不會變動。它讓對局變得均勢。它並不讓對局變得平靜,而那是不是一個人們想玩的遊戲,是另一個我無法從勝率回答的問題。
目前的狀態
作為競技遊戲還不能出貨。沒有任何積分階梯撐得住先手四盤贏三盤。
這值得跟一個聽起來很像的主張分開。不公平不等於不能玩:反棋(自殺棋)是已證明的先手必勝,卻每天都有人在競技性地下,因為那個證明花了五年、用了八台機器,而且走的是沒有人類找得到的變例。Hex 在每一種棋盤尺寸下都是已證明的先手必勝,而它在交換規則的掩護下被下著。先手優勢本身並不會殺死一個遊戲。
這裡不同的地方在於深度。這個 78% 建立在一句話就能說完的一步棋上,把炮提到第五線,而引擎每一次都找得到。一個已被證明但無法企及的勝法是個奇聞。一個能背下來的勝法就是整個遊戲本身。
派餅規則之外還有兩種修法未經測試。標記將帥而不是把它變色,讓防守方有一步的時間回應,會直接鈍化炮的威脅,而把攻擊的那枚棋子變色本來就是合法的回應。或者改動初始佈局中中路兵卒的位置,因為這個威脅之所以存在,只是因為黑方的卒正好站在炮需要炮架的地方。
無論如何留得下來的是這個設計。在象棋棋盤上的變色機制給出一個沒有殘局的遊戲,炮永遠不會變弱,防守子力毫無價值,而變色後的士會永遠困在敵方陣線後方。這一點在每一個量測版本中都成立,因為規則是我用四種不同方式驗證過的那一部分。
下一步是那條一步先手規則:核心裡的二十行,以及重新執行一次已經存在的測試框架。
未解的問題是紅方是否有必勝著法。上面的一切都是勝率,而勝率是關於兩個特定引擎的陳述。它底下的問題是這個遊戲在雙方最佳著法下是否為先手必勝,而那個問題的答案不取決於任何東西下得多好。
我手上有的是一個下界:13 步之內沒有必勝著法,在 208 億個節點與大約半小時內證明。這排除了便宜的勝法,而一個中位長度 29 步的遊戲,被它探索到的還不到一半。
有兩件事讓這比看起來更難,有一件讓它更容易。子力是守恆的,所以狀態空間永遠不會縮小,這表示沒有殘局資料庫可建,也沒有東西讓前向搜尋在中間會合。逆向分析,也就是每一個已解決的西洋棋殘局背後的技術,在原理上就不可用,而不只是昂貴。策略竊取,也就是從對稱起始局面得出超弱解的常用手法,也同樣不適用:棋子是移動而不是累積,所以先手方不能隨便走一步再把它丟掉。有幫助的是對局很短。中位數是 29 步,而 54 盤引擎對局與 6,000 盤隨機對局中都沒有和棋,所以這棵樹很淺而且終局很鋒利,這正是證明數搜尋擅長的形狀。
下一階是 14,而槓桿不是更多硬體。上面的證明可以按紅方的 42 個首著切開,而它之所以幾乎完美地平行化,恰恰是因為答案是否:既然沒有必勝著法,就沒有任何分支會截斷另一個分支,所以同時搜尋它們不會有任何損失。那讓一台筆電做到了 13。往下切兩步會得到約 1,700 個獨立工作,這是需要叢集的形狀,而證明數搜尋,也就是把力氣花在證明樹最窄的部分而不是均勻地往深處搜尋,則完全還沒實作。最後這一點不是假設:反棋(自殺棋)就是這樣被解決的。Mark Watkins 在八台十六核心機器上執行證明數搜尋超過五年,才證明 1.e3 對白方是勝,這既是這裡需要的技術,也公允地提醒了完成這件事的代價。
如果有人拿出弱解,甚至用我沒想到的論證拿出超弱解,那也同時解決了派餅規則的問題,因為已證明的先手必勝正是交換規則被發明出來要應付的情況。Hex 在每一種棋盤尺寸下都是已證明的先手必勝,而它照樣被競技性地下著。