規則

國際象棋的部落棋(Horde)是它的母版:36 個兵、沒有王,對抗正常的一方軍隊。這次移植儘量保留象棋本身的規則。紅方是兵團:只有兵,沒有帥,紅方先行。黑方是標準全軍。紅方靠將死取勝;黑方靠吃掉紅方所有子取勝。由於棋盤上只有一個將,兵團一方不存在被將,也不存在白臉將。

輪到行棋而無合法著法的一方判負,與象棋相同;Lichess 的部落棋判為和棋,下面的終局規則一節會說明這有多大影響。三次重複局面判和;連續 60 或 120 步無吃子也判和。標準兵走法與象棋相同:向前一格,過河後才能橫走,永不後退。老兵從第一步起就有過河兵的走法。無論哪種設定,全軍一方的五個兵都是標準兵。

36 個標準兵,第 1-4 線楚 河 漢 界Mistboard36 個老兵,第 2-5 線楚 河 漢 界Mistboard
原版陣形配標準兵,以及我們找到的最接近一盤棋的設計:36 個老兵前移一行,用過河兵的棋子圖案畫出,因為那正是它們的走法。

設計空間

五個旋鈕。一種設計由一種兵的走法和一種初始陣形構成,我們下了二十四種。

旋鈕試過的設定
兵的走法標準;老兵(第一步即可橫走)
兵的數量18, 27, 31, 32, 36, 40, 45
兵陣起始位置靠底(第 1 線往上)、前壓(第 2 或第 3 線往上)、過河(前排位於第 6 線)
兵陣的形狀整排;母版的形狀(成排再加成對的前哨);象棋的五個兵位
終局規則60 或 120 步的無吃子步數限制;困斃判負(象棋)或判和(母版)

每種設計都跑在原版 Fairy-Stockfish 上,只用一小段配置,不改引擎程式碼,由一個規則核心擔任裁判,並在讀取任何對局之前就與引擎的著法生成器逐一核對一致(檢查見「我們是怎麼核驗的」一節)。每種陣形下四局,引擎在四檔搜尋預算下自我對弈,無隨機成分;再以每著一百萬節點下一局。棋盤上沒有紅帥,引擎無法使用它的象棋神經網路,因此只靠古典評估下棋。它照樣把棋下完了。

十二種初始陣形,按下方表格的順序排列。紅方是兵團,黑方是標準全軍;每種陣形都用兩種兵的走法各下過。

標準兵:車從背後吃光兵團

十二種陣形裡有九種四局全歸全軍,靠的是同一個機制。標準兵只控制它正前方那一格,所以兵團是九列縱隊,而不是一道牆。某一列一旦走空,車就從這一列直落底線,一著吃一個兵地從背後蠶食兵陣,而兵團沒有任何子力能向後攻擊。形狀不起作用:前哨是靶子,不是掩護。

原版陣形的一百萬節點對局,每一步都在。車在第 50 步落到底線;從那裡往後一步步看兵陣如何從背後被吃掉。本頁透過規則核心重放。

另外三種陣形是同一個例外:在車做完這件事之前,有足夠多的兵過了河。第 1 至 5 線的 45 兵是五層深的兵陣,四局結果是全軍兩勝、兵團將死一局、和棋一局。兩種從過河處起始的陣形一開局就與全軍的兵短兵相接。27 兵仍然要輸;36 兵則是五五開,它跳過了行軍階段,預演了老兵版的棋局。

兵數起始全軍兵團和百萬節點局步數
18第 1-2 線400全軍,全滅46
18第 3-4 線400全軍,全滅70
27第 1-3 線400全軍,全滅84
27第 2-4 線400全軍,全滅108
27第 4-6 線301全軍,全滅154
36第 1-4 線400全軍,全滅134
36第 2-5 線400全軍,全滅184
36第 3-6 線220兵團,將死195
45第 1-5 線211全軍,全滅198
32象棋的五個兵位400全軍,全滅122
31母版形狀400全軍,全滅112
40母版形狀400全軍,全滅158
全軍、兵團、和棋三欄統計每種陣形的四局,每著 150,000、200,000、300,000 和 500,000 節點,120 步無吃子限制,每著都取引擎首選。之後是每著一百萬節點、60 步限制下的單局及其步數。有四種陣形在每著五百萬節點下又下了一局:全軍在 156 步贏下第 1 至 4 線的 36 兵,在 146 步贏下第 2 至 5 線的 36 兵,在 106 步贏下 32 兵;過河起始的 36 兵在第 104 步因重複局面和棋。

老兵:兵團守得住,圍城是和棋

車之所以有路可走,是因為標準兵不攻擊身旁任何一格,而象棋裡本來就有一種能做到的兵:過河兵,可向前或橫向一格行走與吃子。老兵就是一開始就已經升變的兵團。兵團的每個兵從第一步起就有這種走法,其餘一切不變,玩家不需要學任何新東西。

這條路被堵死了。老兵縱隊會攻擊身旁各格,車沒法白站在它旁邊。兵團成了真正的一方,而在實力相當的雙方之間,棋局變成圍城:48 局,37 和。全軍只贏得了對 18 兵的局,從 27 兵往上它一局不勝。

兵數起始全軍兵團和百萬節點局步數全軍損失
18第 1-2 線202全軍,全滅2589
18第 3-4 線301全軍,全滅30810
27第 1-3 線004和棋,步數限制4597
27第 2-4 線004和棋,步數限制4908
27第 4-6 線004和棋,步數限制1669
36第 1-4 線004和棋,步數限制1,058*13
36第 2-5 線031兵團,困斃43515
36第 3-6 線013和棋,重複局面70313
45第 1-5 線013和棋,步數限制1,102*12
32象棋的五個兵位004和棋,步數限制4497
31母版形狀004和棋,步數限制2475
40母版形狀013和棋,步數限制1,016*12
每種陣形在同樣的四檔預算、120 步限制下各下四局,然後是一百萬節點下的單局;全軍損失一欄統計全軍在那一局裡損失的子力。*有三局一百萬節點對局撞上了測試框架的 1,000 步上限,之後從該局面繼續下,步數限制照常生效;之後未再出現吃子,表中所示步數即為步數限制終止之處。三局結束時都是九或十個兵對將加雙車,其中兩局還多一個象或一個士。

終局形態。兵團行軍推進,路上吃掉全軍的炮和相,抵達九宮時發現將和兩隻車在那裡等著。到了這一步,兵衝不過車,車也吃不動互相保護的兵陣。起始橫線和形狀只決定兵團在路上能吃掉全軍多少子,別無其他。母版的 31 兵展示了同一條規則的另一面:四局在 155 至 245 步和棋,過河的兵從未超過兩個,因為沒動過的兵陣無法被攻擊,而一動就要丟掉動過的那個兵。

第 754 步:12 個兵楚 河 漢 界Mistboard第 1,000 步:10 個兵楚 河 漢 界Mistboard
終局形態,這裡取自 40 兵陣形、每著一百萬節點的對局:第 754 步時,十二個兵對將、士和雙車。兵衝不過車,而車只有在某個兵走出兵陣時才吃得到它:接下來 246 步裡只吃了兩個,分別在第 822 和 896 步。

我們把 40 兵那局在第 754 步的殘局拿出來,讓引擎雙方各以每著五百萬節點再下 200 步:只吃了一個子,而引擎自己對兵團優勢的評估值從 2.3 兵滑到 0.7。在一百萬節點下,全軍在同樣這段裡吃了兩個子,接下來的 120 步裡一個也沒吃,步數限制結束了對局。262 步吃兩個兵,算不上贏下殘局。

取勝的那一局。前壓一線的 36 兵吃掉了兩隻車,並在第 435 步把光將困斃:無著可走又未被將,象棋判負。這是兵團唯一佔優的陣形,四局贏三局,一百萬節點那局也贏了:離河近一線,抵達時全軍已經損失更多,而剩下的兵也足夠換掉兩隻車。兵團其餘的勝局都是同樣的收束,全部在第 390 步之後,六勝裡有四勝是困斃。

兵團的勝局,每一步都在:行軍、圍城、第 267 和第 431 步雙車先後被吃,以及第 435 步的困斃。本頁透過規則核心重放。

終局規則

這裡有三種讓棋局無勝負而終止的方式,它們並不是一回事。重複局面:同一局面、同一方行棋出現三次;判和。步數限制:連續若干步無吃子,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 兵時,兵陣夠厚,有時能熬過去,那一種陣形於是結果分化。

過河之後,兵陣動不了。過河兵(或老兵)也能橫向攻擊,所以兵陣原地就能互相保護。但它只保護自家兵能走到的格子,絕不保護鄰兵正前方那一格。向前一步的兵落在沒人控制的格子上,在鄰兵跟上來並列之前就是白送的一子。於是兵陣只能整排整排地推進,每推一排要走九著,而全軍只需等待;任何提前走動的兵都會被點掉。而全軍一方也攻不動一個沒動過的兵陣。十二種老兵陣形裡有九種就在這裡終止,或全部對局如此,或只有一局例外:將和兩隻車守在九宮,一堆兵圍在外面,雙方都無事可做。

國際象棋:e4-e5 之後,d4 控制 e5
兵:e4-e5 之後,無子控制 e5楚 河 漢 界Mistboard
老兵:仍然無子控制 e5楚 河 漢 界Mistboard

同一步棋,三張棋盤:中間那個子剛向前走了一格,綠圈是它這一方此刻保護的點。國際象棋的兵斜著吃子,所以 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 = 60
stockfish 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 步將死,以及一盤無吃子步數到限的和棋。檢視器先開啟上面那盤兵團的勝局。列表裡的任何一盤都可以選。

全部 58 盤棋,每一盤都經規則核心重放。方向鍵可以逐步走棋盤上的這一盤。

我們發佈的是什麼,而不是一個對局頁面

含兩種兵的走法和全部十二種陣形的規則內核、Fairy-Stockfish 的變體配置、全部 387 盤引擎對局、它們背後的各項檢驗,以及一個不需要引擎、半分鐘內就能對照規則重放每一盤並重新計算統計的校驗器。如果你有一種能給兵陣路上掩護的兵的走法,下一次嘗試就從那裡開始;去那裡提一個 issue,文章會如實寫明。

親自檢驗這些對局學習象棋