規則
國際象棋的部落棋(Horde)是它的母版:36 個兵、沒有王,對抗正常的一方軍隊。這次移植儘量保留象棋本身的規則。紅方是兵團:只有兵,沒有帥,紅方先行。黑方是標準全軍。紅方靠將死取勝;黑方靠吃掉紅方所有子取勝。由於棋盤上只有一個將,兵團一方不存在被將,也不存在白臉將。
輪到行棋而無合法著法的一方判負,與象棋相同;Lichess 的部落棋判為和棋,下面的終局規則一節會說明這有多大影響。三次重複局面判和;連續 60 或 120 步無吃子也判和。標準兵走法與象棋相同:向前一格,過河後才能橫走,永不後退。老兵從第一步起就有過河兵的走法。無論哪種設定,全軍一方的五個兵都是標準兵。
設計空間
五個旋鈕。一種設計由一種兵的走法和一種初始陣形構成,我們下了二十四種。
| 旋鈕 | 試過的設定 |
|---|---|
| 兵的走法 | 標準;老兵(第一步即可橫走) |
| 兵的數量 | 18, 27, 31, 32, 36, 40, 45 |
| 兵陣起始位置 | 靠底(第 1 線往上)、前壓(第 2 或第 3 線往上)、過河(前排位於第 6 線) |
| 兵陣的形狀 | 整排;母版的形狀(成排再加成對的前哨);象棋的五個兵位 |
| 終局規則 | 60 或 120 步的無吃子步數限制;困斃判負(象棋)或判和(母版) |
每種設計都跑在原版 Fairy-Stockfish 上,只用一小段配置,不改引擎程式碼,由一個規則核心擔任裁判,並在讀取任何對局之前就與引擎的著法生成器逐一核對一致(檢查見「我們是怎麼核驗的」一節)。每種陣形下四局,引擎在四檔搜尋預算下自我對弈,無隨機成分;再以每著一百萬節點下一局。棋盤上沒有紅帥,引擎無法使用它的象棋神經網路,因此只靠古典評估下棋。它照樣把棋下完了。
標準兵:車從背後吃光兵團
十二種陣形裡有九種四局全歸全軍,靠的是同一個機制。標準兵只控制它正前方那一格,所以兵團是九列縱隊,而不是一道牆。某一列一旦走空,車就從這一列直落底線,一著吃一個兵地從背後蠶食兵陣,而兵團沒有任何子力能向後攻擊。形狀不起作用:前哨是靶子,不是掩護。
另外三種陣形是同一個例外:在車做完這件事之前,有足夠多的兵過了河。第 1 至 5 線的 45 兵是五層深的兵陣,四局結果是全軍兩勝、兵團將死一局、和棋一局。兩種從過河處起始的陣形一開局就與全軍的兵短兵相接。27 兵仍然要輸;36 兵則是五五開,它跳過了行軍階段,預演了老兵版的棋局。
| 兵數 | 起始 | 全軍 | 兵團 | 和 | 百萬節點局 | 步數 |
|---|---|---|---|---|---|---|
| 18 | 第 1-2 線 | 4 | 0 | 0 | 全軍,全滅 | 46 |
| 18 | 第 3-4 線 | 4 | 0 | 0 | 全軍,全滅 | 70 |
| 27 | 第 1-3 線 | 4 | 0 | 0 | 全軍,全滅 | 84 |
| 27 | 第 2-4 線 | 4 | 0 | 0 | 全軍,全滅 | 108 |
| 27 | 第 4-6 線 | 3 | 0 | 1 | 全軍,全滅 | 154 |
| 36 | 第 1-4 線 | 4 | 0 | 0 | 全軍,全滅 | 134 |
| 36 | 第 2-5 線 | 4 | 0 | 0 | 全軍,全滅 | 184 |
| 36 | 第 3-6 線 | 2 | 2 | 0 | 兵團,將死 | 195 |
| 45 | 第 1-5 線 | 2 | 1 | 1 | 全軍,全滅 | 198 |
| 32 | 象棋的五個兵位 | 4 | 0 | 0 | 全軍,全滅 | 122 |
| 31 | 母版形狀 | 4 | 0 | 0 | 全軍,全滅 | 112 |
| 40 | 母版形狀 | 4 | 0 | 0 | 全軍,全滅 | 158 |
老兵:兵團守得住,圍城是和棋
車之所以有路可走,是因為標準兵不攻擊身旁任何一格,而象棋裡本來就有一種能做到的兵:過河兵,可向前或橫向一格行走與吃子。老兵就是一開始就已經升變的兵團。兵團的每個兵從第一步起就有這種走法,其餘一切不變,玩家不需要學任何新東西。
這條路被堵死了。老兵縱隊會攻擊身旁各格,車沒法白站在它旁邊。兵團成了真正的一方,而在實力相當的雙方之間,棋局變成圍城:48 局,37 和。全軍只贏得了對 18 兵的局,從 27 兵往上它一局不勝。
| 兵數 | 起始 | 全軍 | 兵團 | 和 | 百萬節點局 | 步數 | 全軍損失 |
|---|---|---|---|---|---|---|---|
| 18 | 第 1-2 線 | 2 | 0 | 2 | 全軍,全滅 | 258 | 9 |
| 18 | 第 3-4 線 | 3 | 0 | 1 | 全軍,全滅 | 308 | 10 |
| 27 | 第 1-3 線 | 0 | 0 | 4 | 和棋,步數限制 | 459 | 7 |
| 27 | 第 2-4 線 | 0 | 0 | 4 | 和棋,步數限制 | 490 | 8 |
| 27 | 第 4-6 線 | 0 | 0 | 4 | 和棋,步數限制 | 166 | 9 |
| 36 | 第 1-4 線 | 0 | 0 | 4 | 和棋,步數限制 | 1,058* | 13 |
| 36 | 第 2-5 線 | 0 | 3 | 1 | 兵團,困斃 | 435 | 15 |
| 36 | 第 3-6 線 | 0 | 1 | 3 | 和棋,重複局面 | 703 | 13 |
| 45 | 第 1-5 線 | 0 | 1 | 3 | 和棋,步數限制 | 1,102* | 12 |
| 32 | 象棋的五個兵位 | 0 | 0 | 4 | 和棋,步數限制 | 449 | 7 |
| 31 | 母版形狀 | 0 | 0 | 4 | 和棋,步數限制 | 247 | 5 |
| 40 | 母版形狀 | 0 | 1 | 3 | 和棋,步數限制 | 1,016* | 12 |
終局形態。兵團行軍推進,路上吃掉全軍的炮和相,抵達九宮時發現將和兩隻車在那裡等著。到了這一步,兵衝不過車,車也吃不動互相保護的兵陣。起始橫線和形狀只決定兵團在路上能吃掉全軍多少子,別無其他。母版的 31 兵展示了同一條規則的另一面:四局在 155 至 245 步和棋,過河的兵從未超過兩個,因為沒動過的兵陣無法被攻擊,而一動就要丟掉動過的那個兵。
我們把 40 兵那局在第 754 步的殘局拿出來,讓引擎雙方各以每著五百萬節點再下 200 步:只吃了一個子,而引擎自己對兵團優勢的評估值從 2.3 兵滑到 0.7。在一百萬節點下,全軍在同樣這段裡吃了兩個子,接下來的 120 步裡一個也沒吃,步數限制結束了對局。262 步吃兩個兵,算不上贏下殘局。
取勝的那一局。前壓一線的 36 兵吃掉了兩隻車,並在第 435 步把光將困斃:無著可走又未被將,象棋判負。這是兵團唯一佔優的陣形,四局贏三局,一百萬節點那局也贏了:離河近一線,抵達時全軍已經損失更多,而剩下的兵也足夠換掉兩隻車。兵團其餘的勝局都是同樣的收束,全部在第 390 步之後,六勝裡有四勝是困斃。
終局規則
這裡有三種讓棋局無勝負而終止的方式,它們並不是一回事。重複局面:同一局面、同一方行棋出現三次;判和。步數限制:連續若干步無吃子,60 步或 120 步;判和,而且兵向前一步並不重置計數。上限:測試框架在 400、1,000 或 2,000 步處終止對局。這不是棋規,所以被截斷的對局是未下完,而不是和棋。有三局一百萬節點的老兵對局撞上了 1,000 步上限,隨後繼續下到步數限制終止;而四檔預算的對局沒有一局到過 2,000 步,因此表格裡的每個結果都屬於前兩種。
步數限制。象棋的無吃子規則是 60 步。用標準兵時,48 局裡只觸發過一次。用老兵時它是常見的收束:37 和裡有 30 局如此,另外七局是重複局面。設成 60 步會在行軍還在進行時截斷還活著的棋局,所以老兵對局和每一組四局樣本都用 120 步。步數限制同時也是引擎遵循的規則,所以 60 步限制下的一局和 120 步限制下的一局,從第 2 步起就是兩局不同的棋,而不是同一局在不同處被截斷。
困斃。按母版的規則,將無著可走又未被將判和棋。把實驗室裡的每局都按這條規則重新判定:老兵、實力相當條件下兵團的七勝裡有五勝變成和棋,標準兵的四勝裡有一勝變成和棋;對隨機走子的全軍,標準兵下兵團的 35 勝裡有 20 勝、老兵下 38 勝裡有 16 勝變成和棋;老兵預算階梯測試的十勝裡有五勝同樣如此。困斃大約佔了兵團勝法的一半,而母版的規則會把它拿掉。
兵為什麼不行,換一種兵又需要什麼
棋局分兩個階段,象棋的兵在每個階段都以不同方式失靈。
過河之前,兵陣無法自保。標準兵只攻擊正前方一格。於是兵團是九列縱隊,而不是一道牆:車站在某列旁邊或它後面都碰不到,任何走空的縱線都是通往底線的大路。兵團在自己這半邊、40 兵及以下的所有陣形,都在這一階段被這一隻車判定,四局全輸。到 45 兵時,兵陣夠厚,有時能熬過去,那一種陣形於是結果分化。
過河之後,兵陣動不了。過河兵(或老兵)也能橫向攻擊,所以兵陣原地就能互相保護。但它只保護自家兵能走到的格子,絕不保護鄰兵正前方那一格。向前一步的兵落在沒人控制的格子上,在鄰兵跟上來並列之前就是白送的一子。於是兵陣只能整排整排地推進,每推一排要走九著,而全軍只需等待;任何提前走動的兵都會被點掉。而全軍一方也攻不動一個沒動過的兵陣。十二種老兵陣形裡有九種就在這裡終止,或全部對局如此,或只有一局例外:將和兩隻車守在九宮,一堆兵圍在外面,雙方都無事可做。
同一步棋,三張棋盤:中間那個子剛向前走了一格,綠圈是它這一方此刻保護的點。國際象棋的兵斜著吃子,所以 d4 的兵控制著 e5,走到那裡的兵車吃不得:兵成鏈推進。象棋的兵怎麼走就怎麼吃,直著向前,所以 d4 和 f4 控制 d5 和 f5,沒有任何子控制 e5;車白吃這個兵。老兵(右)也能橫向攻擊,所以兵陣原地就能互相保護,但兵要走到的那一點仍然沒有任何子控制。兵團守得住陣地,卻離不開它。
國際象棋的兵構造不同。它斜吃,所以 d4 的兵在 e 兵走到 e5 之前就已經控制了 e5:兵是成鏈推進的,一次走一個,每一步落點都有保護,而兵鏈可以從底部瓦解。這就是部落棋在國際象棋裡成立、而國際象棋棋手根本不必考慮這件事的原因。在象棋盤上,河是一道懸崖:過去的路上沒有掩護,一旦過去又是全覆蓋,而數量、起始橫線、形狀、步數限制怎麼調都改變不了這一點。這些正是我們轉過的旋鈕。除了老兵之外,我們沒有碰的那個旋鈕,就是兵本身。
下一次嘗試就從那裡開始。兵團缺的那條性質是路上的掩護:一個能保護鄰兵即將踏上的那一格的兵,好讓兵陣一次推進一個子,而不是一次推進一排。任何能提供這條性質的設計都是候選,改動從小到大不等:斜前吃子但仍直走的兵、第一步可走兩格、像舊時「五虎」讓子那樣一回合走兩個兵、到底線後再升一次變,或者乾脆直接用國際象棋的兵,那就把這變成了大棋盤上的部落國際象棋。我們止步於老兵,因為它是這些方案裡唯一象棋本來就有的;其餘每一個都是新子力,而新子力就是另一種遊戲。規則在實驗室裡就是資料,所以其中任何一個都只是一行配置加一下午的引擎時間,而這裡的表格就是需要超越的基線。
自己跑一遍
整個變體就是 Fairy-Stockfish 的 variants.ini 裡的一段。下面是母版陣形配老兵與 120 步限制(nMoveRule 按回合計,所以 60 就是 120 步);要用標準兵,刪掉 customPiece1 那一行,並在初始局面裡把 V 寫成 P。
[hordexiangqi:xiangqi]
startFen = rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/VVVVVVVVV/VVVVVVVVV/VVVVVVVVV/VVVVVVVVV w - - 0 1
customPiece1 = v:fsW
extinctionValue = loss
extinctionPieceTypes = *
flyingGeneral = false
chasingRule = none
stalemateValue = loss
perpetualCheckIllegal = false
nFoldValue = draw
nMoveRule = 60stockfish check horde.ini # parses the stanza
setoption name VariantPath value horde.ini
setoption name UCI_Variant value hordexiangqi
position startpos
go nodes 1000000有兩行很關鍵、又很容易漏掉。chasingRule = none 是必需的:在無將的一方,引擎的捉子檢測會去讀那個不存在的將的位置。flyingGeneral = false 其實不改變任何東西,只是記錄一個事實:引擎把白臉將規則掛在它的王類子力上,一方沒有王時會靜默關掉它。
我們是怎麼核驗的
引擎確實在按這些規則下棋。一個規則核心(就是跑原子象棋那一個,加上了無將一方、全滅取勝和老兵的介面)為每一局擔任裁判:引擎提出一著,核心校驗並落子,如果引擎悄悄按別的規則下棋,執行會中止,而不是產出另一種遊戲的記錄。在讀取任何對局之前,兩者必須在每一個合法著法上一致:從每個陣形做深度 3 的 perft、九個手工構造的局面按精確著法集合比對、以及隨機走子產生的 300 個局面。零分歧,兩種兵的走法、全部十二種陣形。
引擎懂這個遊戲。在 100,000 節點下,它用標準兵對隨機走子方 70-0,用老兵 80 局勝 78 局。十倍搜尋預算對十分之一預算,在標準前壓陣形上 17-1-2,在老兵上 11-0-9。一條腳註:在母版陣形配標準兵時,十倍預算只拿到 55%,因為無論哪一檔預算坐哪一邊,每一局有勝負的棋都歸全軍;決定結果的是座位,不是搜尋。
搜尋有多深。用節點預算而不是時間,所以結果不依賴機器:每著 200,000、一百萬和五百萬節點,在這些局面上大致達到深度 12、15 至 20、24 至 33。在這些預算下棋力還沒飽和:標準兵的過河陣形,七檔預算跑出來依次是和、全軍、兵團、兵團、全軍、兵團、和。標準兵的結論在試過的每一檔預算下都成立,機制一致,這個論斷是穩的。圍城是搜尋最不擅長的局面型別:一百步的計劃在深度 20 下完全看不見,而古典評估根本不知道這種殘局值多少(在全軍正在蠶食兵陣時,它給這個堡壘打了兵團 +2.3)。所以和棋的論斷比負局的論斷要弱,有計劃的人類在任何一邊都可能下得比引擎好。
一局只是一個樣本。每種陣形都下了四局,每著 150,000、200,000、300,000 和 500,000 節點,每著都取引擎首選,沒有隨機開局,所以表格裡的計數是一個比率,而一百萬節點對局是五局之一,不是結論。確定性的引擎從同一起始會重現同一局棋,所以正是預算讓這四局不同;指令碼會拒收其中兩局完全相同的陣形(沒有出現過)。結果分化時,分化本身就是結果。關於老兵資料還有一條說明:引擎把老兵當作自定義子力,其價值是按機動性估出來的,未經調優,所以它對「一兵換一子」的判斷比對標準兵更粗糙。它在五百萬節點下都攻不破的那個堡壘是真實的;而它推進路上舍掉的子力,可能並非最佳著法。
它目前處於什麼水準
對這個系列而言,一個變體要算遊戲,需要在可測量的對弈中越過三道門檻:在高手之間有勝負,而不是預設和棋;任何一方都不能從開局就必勝;兩個座位都有事可做。部落象棋在試過的任何設計裡都一道也沒越過。
標準兵:兵團起始於自己這半邊、40 兵及以下的所有陣形,全軍全勝,36 局全勝,靠的是同一個招法。有勝負,但從第一著就定了。到 45 兵,或從過河處起始,陣形就變成由開局幾手交換決定的五五開。
老兵:兩位高手之間是一場圍城,48 局裡 37 局和棋,每局 137 至 1,098 步。兵團勝六局,其中四局靠困斃;全軍勝五局,全都是對 18 兵。沒有勝負,而且兩個座位都沒有計劃:全軍的最佳下法是待在九宮裡等,兵團的最佳下法是把兵陣一格一格地挪。兵團唯一佔優的陣形只是同一場圍城分數更好一點,不是另一種遊戲。
對付弱手時兵團很危險:在 100,000 節點下它對隨機走子的全軍 35 局全勝,其中 20 局靠困斃,而人比一百萬節點的搜尋要弱。這讓它成了一個陷阱,而不是一個遊戲。
什麼能讓它重新開啟:人類對局的結果、一種能給兵陣提供路上掩護的兵的規則(候選已列在上面),或者車被吃的比率遠高於這裡測到的 48 局中 6 局。就象棋原本的兵而言,目前沒有任何跡象指向那個方向。兵就是設計空間,河是其中的一道懸崖,下一個設計必須改兵。
已有工作檢索。我們查過 Fairy-Stockfish 的變體檔案、PyChess、Lichess、Chess.com 的官方列表、PlayStrategy、Ludii、中英文維基百科的象棋變體條目、chessvariants.com 的 9x10 索引、知乎上一份 231 種民間象棋變體的清單,以及百度百科上九種帶「兵」字名稱且有規則的變體,沒有找到部落式的變體。貼吧的民間棋盤和 Chess.com 的使用者變體我們看不到,所以這是一次盡力的否證,而非窮盡的否證。象棋本身的不對稱讓子棋(滿洲棋、五虎、九子讓先)走的是反方向,是從一方拿掉子力,先拿兵。國際象棋的部落棋源自 BrainKing 的 Horde Chess(2002)和 Lord Dunsany 的「Pieces against Pawns」(1942),Lichess 在 2015 年於 scalachess 定下了它的規則。
引擎是 Fairy-Stockfish;產出這些對局的測試框架,連同引擎校驗關卡和相關命令,是變體實驗室。
每一盤棋,逐步回放
引擎對局中有五十八盤在下面的檢視器裡:引擎在各個陣形上自戰,每著 200,000、一百萬和五百萬節點,標準兵 28 盤,老兵 28 盤;另有兩盤標準兵前壓陣形上的階梯對局,100,000 節點對 10,000 節點:兵團最快的一勝,第 151 步將死,以及一盤無吃子步數到限的和棋。檢視器先開啟上面那盤兵團的勝局。列表裡的任何一盤都可以選。
我們發佈的是什麼,而不是一個對局頁面
含兩種兵的走法和全部十二種陣形的規則內核、Fairy-Stockfish 的變體配置、全部 387 盤引擎對局、它們背後的各項檢驗,以及一個不需要引擎、半分鐘內就能對照規則重放每一盤並重新計算統計的校驗器。如果你有一種能給兵陣路上掩護的兵的走法,下一次嘗試就從那裡開始;去那裡提一個 issue,文章會如實寫明。