五子棋分為有禁手和無禁手,有禁手就是在無禁手的規則的基礎上加上禁手規則,具體的規則不講了。所以就人機對戰來講,從無禁手來做,比較好做。這裡只講無禁手的情況。無禁手就是只要能連成5個子,或者5個子以上,就算贏。
而計算機博弈要解決的問題,抽象地講只有乙個問題「下一步怎麼走」,對五子棋來講,就是下一步在哪個點落子。
一般的思路在 棋類人機對戰的一般原理 - billysir - 已經有說。下面重點講五子棋特有的演算法思路。
1.如果下一步能贏,就走這一步
2.如果下一步會輸,就阻止對方贏
3.計算得分決定在**落子。
其中 阻止對方贏,就是在對方能贏的點上落子,一般會有1到3個點。
前面2點比較好理解,重點是第3點,計算得分。
得分的思維很普遍,就是棋盤上每個空位置,算出乙個分,所有空位置的分中,哪個點(位置)得分最高就落子在那個點。
每個點的分數怎麼算?
掃瞄棋盤上所有空位置(沒有白子也沒有黑子的點),計算每一點的得分。
每一點的得分受到8個方向的影響,圖形上就是乙個「公尺」字。這個點就是公尺字的中心點。把同一線上的2個方向看作1個方向,即是4個方向的影響。就是掃瞄4個方向,看看這個點如果落子會構成什麼(對自己一方而言)
如果是5,當然就是贏了,得分最高
從得分最高到最低的順序是:
五(或者五以上)> 活四 > 衝四 > 活三 > 衝三 > 活二 > 衝二 > 活一 > 衝一
(ps.活四活三經常有聽說,活二衝一這些也不知道棋壇有沒有這種說法,不過不影響我們對軟體的編寫。)
掃瞄出乙個方向上的情況,對應上面的9種情況之一。查得分表,得到分數。得分表是上面的每種情況給乙個固定的數字作為分數。4個方向的得分加起來,當作總分。
這還只是落子對自己一方而言的情況,就是攻的得分。
落子也會給對方造成阻礙,所以落子除了有「攻」的作用之外,還有「防」的作用。
但是攻就是自己先手(肯定是輪到自己的時候計算機才思考如何下),防來講就是後手(我方走一步後輪到對方走,對對方而言,算後手),同樣一種情況,先手得分與後手得分是不一樣的。於是得分表就有3列:情況、先手得分、後手得分。
總結一下,對於棋盤上乙個空點,它的得分來自8個數字相加,其中4個是4個方向的攻分,另4個是4個方向的防分。
同一種情況,攻分大於防分,所以特殊情況下,雙方都是衝四活四的情況,自然是先完成自己的五,而不是先堵住對方的四。
前面講的是一次掃瞄,得到每個點的分數,挑最高分的點落子。但我們知道,五子棋,要贏,往往是最後幾步連著下的,比如先成【衝四活三】,然後再走幾步才能贏。而如果發現對方準備【衝四活三】就要趕快阻止,而不是等到【活四】再堵,那樣已經來不及了。所以,需要在記憶體中演練接下來的幾步,雙方可能怎麼走。這就是搜尋。
回到剛才的情況,計算機在思考這一步應該在哪個點落子,我們挑出最高分的幾個點(可以設高於衝三的才算,也可以設最高的x個點,比如8個點),假設我方下了其中乙個點(這算第1步),那麼下一步就輪到對方下子了,對方也是一樣的思維,從對方的角度,計算出每個點的得分,挑最高分的幾個點試(這算第2步),然後又輪到我方下子,思維和演算法是一樣的。這麼就可以搜尋好幾步。這樣,情況就分支為很多情況,成為一棵樹。由於每一步都在試幾種不同的走法,所以最終還是要從幾種走法中確定一種對自己(相對而言)最有利的走法。每一步都是這樣。這個叫最大原理。如果作為旁觀者,始終站在一方進行評價,就是最大最小化原理。
由於不可能一直搜尋到有勝負出現,所以搜尋到一定的步數後就要停止往下搜,於是要判斷當前局面對自己有利的程度,就需要乙個演算法評定乙個靜態的局面的得分。簡單的可以以局面上所有點的得分總和當作局面的得分(考慮攻防的情況)
每一步都選擇對自己(相對而言)最有利的情況,於是回到第1步的幾種情況,肯定選那種對自己最有利的情況來落子,這個點就不一定是一開始(一次掃瞄)得分最高的那個點了。這種搜尋比直接選一次掃瞄得分最高的點落子,棋力更高。
整個思維就是這樣,至此已經解決了「在**下子」這個問題,也是唯一的乙個計算的目標。
但是,實際問題冒出來了。
由於搜尋這棵樹有很多的葉子和分支,每乙個葉子要計算局面得分,在計算一次性得分,局面得分,都是計算量很大,再加上樹的分支,把這種計算量放大了很多倍,於是搜尋變得很慢。總不能讓人機對戰的人每次等電腦走一步要等很久吧,比如3分鐘,1分鐘,如果是你,受得了?所以,人機對戰往往花大量精力在優化搜尋。前面講的,挑出幾個最高的分作嘗試,而不是每一步對所有空位置都嘗試,也是一種有效的優化。
剪枝就是剪掉一些分支,從而減少計算量。如何剪掉分支,而不影響計算的結果,或影響小到忽略就是值得深入研究。而alphabeta剪枝法就是在【最大最小值搜尋】中,可以剪掉一些分支,而不影響計算結果的一種優化演算法。
alpha剪枝
補充一點,棋類的搜尋一般是深度優先,也有在深度優先的基礎上,再組合廣度優先,一步一步加深的。本質上還是深度優先。
如上圖,從5,8,4中得到最小值4,然後到了a節點,a的2個孩子已經有結果了,6和2。由於2已經比它的伯伯4小了,所以,不管2的弟弟b節點大小如何,a的值肯定不會大於2,而最高一層是要取最大數(注意左邊的max和min),既然a最大值不會超過2,所以,不再搜尋a的第3個孩子b節點以及後面的孩子。
同理,a的弟弟(c節點)的第1個孩子已經是3(比4小),也剪掉。
beta剪枝,與alpha剛好相反。如上圖,既然2和9的伯伯是7,而2和9之間最大是9,所以2和9的父親a節點,至少是9,而上層是要取小,肯定取7,所以不用計算9的弟弟(b節點)們的得分。
alpha-beta剪枝搜尋 - ithinktan -
棋盤上每個空點都有乙個分數,整個棋盤就是一張2維的分數盤,顯然在乙個子落子的前後,這個分數盤會變化,但變化範圍是有限的,只需要重新計算可能受到影響的點的分數,就可在原有分數表的基礎上產生新的分數盤,而不必每個空點都重新計算。而每個點落子後影響範圍也是以這個點為中心的「公尺」字範圍。所以只需要重新計算這個公尺字上的空點即可。
五子棋演算法,我基本講完了。
還有一些做法更貼近棋原理的演算法,比如空步。我們人算的時候為了簡單,假設對方不走,自己能怎樣進行。空步可以用來提高局面估值的正確性。
死點進行標記,下次不再搜尋。比如腳部,有些棋型永遠不能成5,永遠成不了5的點不搜尋!
下五子棋的bot 五子棋演算法
include include include include include include jsoncpp json.h c 編譯時預設包含此庫 define n 7 每個節點的分支數 以下為各棋型的識別碼 權重 define win 1 4000 define lose 2 4000 defi...
窮舉五子棋
本想窮舉五子棋必勝點,可惜呀,這貨窮舉太不現實了,寫出來了,根本沒辦法執行出來結果 include include include define rl 17 char s 14 int five rl rl void init void void print void int cs int i,in...
普通 五子棋
五子棋 五子棋是世界智力運動會競技專案之一,是一種兩人對弈的純策略型棋類遊戲,是世界智力運動會競技專案之一,通常雙方分別使用黑白兩色的棋子,下在棋盤直線與橫線的交叉點上,先形成5子連線者獲勝。棋具與圍棋通用,起源於中國上古時代的傳統黑白棋種之一。主要流行於華人和漢字文化圈的國家以及歐美一些地區,是世...