关于做人工智能—五子棋的总结
前言:
?? 剛學玩C一個月,院里有個程序設計的比賽,于是就動手寫了這玩意兒。
正文:
??? 首先,對于每一盤棋都有很多種下法,當黑方落下第一顆棋子的時候,白方有254種下法,白方在這254種下法中選擇其一后,黑方又有253中下法。如此,將所有的下法都考慮,全部列出來就構成了一顆巨大的博弈樹,也稱搜索數。這顆博弈樹的根節點便是黑方下的第一顆棋子,緊接著的下一層便是白方下棋的所有情況,依次下去,直到結束。而電腦要做的是從下面的子節點中找出最有利于電腦方的節點,也就是最佳走法。誰想得深遠,預測的步數越多,棋局了解到位,就越厲害。如果能將未來的所有情況都考慮到位,那必定處于不敗之地。
??? 在選擇最佳走法中,我們要用到極小極大搜索算法。我們先假設有個對棋局的評價函數,即可以對任何一個局面對電腦方打分,分越高越對電腦有利,分越低卻對玩家有利,也就是說這個評價函數是對電腦方而言的,計算機贏則為無窮大,輸則無窮小。其它所有情況都在這之間。
如上圖就是所謂的博弈樹,假如:甲對應電腦方,乙對應玩家。當前該電腦方下棋,即甲0節點,下一層的子節點即該玩家下。雙方都希望在未來的局勢中,往最有利于己方的方向進行。計算機當然想自己的分高,所以會選子節點分值最大的乙1,分值為2,所以甲0分值為2。而乙1想分值低,于是,他會選甲3-4中分值最小的甲3,分值為2,乙1的值因此為2。所以,在極小極大搜索算法中,稱想要最大值的甲的節點為極大值點,想要最小值的乙的節點為極小值點。在這個極大值極小值的搜索過程為極小極大搜索算法。
??? 前面所說的,都是理論上可行的方法,因為在這復雜度指數級增長的過程中,想要搜索完這么龐大的博弈樹是不可能的。我們只能往后預測幾步,就用靜態估值函數算出分數,然后一步步倒推上去,而到當前最佳的走法。這時,我們就需要一個對任意局面評分的評價函數。如果給評估函數打分呢?我是把把棋面分成72路,橫15 + 豎15 + 左斜21 + 右斜21 = 72(因為斜著的共有16路形成不了五子)。為72路是為了收集具有形成5子可能性的特征。一個棋局具有電腦方的這些特征越多,棋局分數便越高,具有玩家方特征越多,分值便越低,因此玩家的特征是記負分的。當然不同的特征分值也不同。我將電腦方棋子抽象為1,空格抽象為0。例如:遇到11111便賦值9999999,011110賦值300000,011100或001110賦值為3000等等。如果是玩家的棋就賦相應的負分。最后把72路的分數加起來,黑白雙方的總分相加就得到整個棋局的分數。其實,我把每一路的情況分成一個個長度為6的字符串,一一跟原來預定的16個特征做比較,留下每一路分值最大的特征。然而,在沒一次判斷下那一步的時候,要預測的情況數是巨大的,而沒一種情況都要調用靜態估值函數,進行大概7000次特征的匹配。為了減少匹配的次數,我用了哈夫曼樹編碼的思想,把所有的特征編成了一個哈夫曼樹(如下圖,當時的草稿),每個節點都由0、1組成,這也是我之前把棋子抽象為1,空格抽象為0的原因。具體就不在詳述,哈夫曼樹編碼的思想我是參考的《算法與數據結構》151頁。如此,每一個長度為6個字符就不必跟16個特征一一線性的比較,這樣就大大的減少了計算量。
??? 光憑上面的還遠遠不夠,我實驗過,僅預測三步的時候,電腦下一步,需要等待近半個小時。因此,我在極小極大搜索算法中還加入了Alpha-Beta剪枝剪枝法,可以叫它為Alpha-Beta搜索算法。如下圖:
上述的極小極大值搜索過程中,遍歷了整棵的博弈樹,每一個節點都訪問了一次,這樣的搜索算法粗糙,效率低下,搜索量非常大。假如將葉節點的評估,計算倒推值與樹的產生同時進行,就可能大量減少所需搜索的節點數目,而且保持搜索效果不變。具體思路如下:
Alpha剪枝:
如圖:極大值點甲3及下面所有節點
1.因為乙7為2,因此甲三大于或等于2;
2.甲19為1,則乙8小于或等于1;
3.既然甲2是取最大的,暫且等于2,就不用考慮小于或等于1的乙 8了。
? 因此,甲20不需要考慮,把它剪掉。
Beta剪枝:
同理,如圖:極小值點乙1及下面所有節點,一樣分析。
這樣,就減少了不少分支,剪掉的分部,既不用創建合法的走法,也不用在評估時大量的計算,在一定程度上優化了許多。但是,Alpha-Beta剪枝剪枝法在減枝過程搜索效率與節點的排列順序有很大關系。因此,可以在產生走法的時候對它進行排序來達到進一步的優化。按理說,對極小節點按從小到大的順序排序,對極大節點按從大到小的順序排序是最理想的,但是在最后的節點沒產生之前,不可能得到評估值。所以,排序的時候可以根據威脅性大的點周圍進行優先搜索的辦法達到優化的效果。
??? 最后便是我在最后兩天想出來的優化辦法,在開頭的幾十步里,它的優化效果比Alpha-Beta剪枝剪枝法好上幾十倍。這里我用到的是縮小搜索范圍算法,其實是我瞎起的一個名字。前面的Alpha-Beta剪枝剪枝法是在產生一定子節點后剪掉部分枝節而達到優化效果,而縮小搜索范圍算法則是從根節點處直接砍掉分支,從頂端砍下來的話,完全舍棄了其下龐大的分支。具體思路如下:我們在下棋的時候,基本都是在已有的棋子周圍下子,所以還有很多空位是完全不需要搜索的。就是基于這一點,我們就能可以確定搜索范圍,先判斷已下了的棋子最大對角線,在此對角線的基礎上畫矩形,然后在向外圍擴大一兩格,這樣就在根節點上減少了分支,更不需要考慮在其范圍外的以后幾步的情況了。不過,在預測的過程中,我們要假設下了某子,這些預測的下子會很快的將范圍擴大到邊界,而當預測完一個父節點及以下子節點的所有情況后,與前面父節點同一深度的節點并不需要那么大的搜索范圍,因此理想的搜索范圍應和預測前同樣的。這里,我們可以采用堆棧的結構,用入棧操作就可將一方下的棋子存入棧中,得到新的搜索范圍,當搜索過程的回溯發生時用出棧操作退出一子,即可恢復到前一搜索范圍。這樣就能步步為營,有進有退,盡可能的減少不必要的計算。
展望未來:
?? 前面講的改進搜索算法的目標在于將不必搜索的(冗余)分枝從搜索的過程中盡量剔除,以達到盡量搜索少的分枝束降低運算量的目的。但是仍然在有限時間內預測幾步而已,想要讓電腦達到世界級水平還是不夠的。開頭我提過中原大學的碩士學位論文《五子棋棋略的演化學習法》中核心講的“基因演算法”,也稱遺傳算法,是讓程序具有學習功能,在大量的實踐中,程序會不段的更改評估函數特征的權值,這樣便克服了人為主觀因素的片面性。而在啟發式搜索過程中,其實有很多局面是重復的,就增加了很多不必要的重復計算,為了解決這個問題,可以使用基于哈希表的置換表搜索算法,將搜索過的局面存如數據,不需要了就清除,遇到相同的局面只需調用前面計算得出的數據,就避免重算。另外,現在的超線程技術和多核技術也能達到進一步的優化。
感想:
??? 這此做人工智能五子棋,多少也有點收獲,相對以前更加懂得怎么在有限的時間內完成自己看似不可能或很難完成的任務,此類任務最好的方法便是證比求易。當中肯定有自己不曾具備的知識,當處于當今這個時代,我們需要的這點知識還是很輕易的找到。然而最關鍵的還是自己以最快的方法消化弄懂它,讓前人或大師研究過的寶貴成果迅速成為自己的技能。作為一名程序員,這次經歷給我的感受是,完成一個程序,最重要的絕對是算法,這此做五子棋,人工智能模塊的算法我也弄了幾天才搞清楚,也許只有某一個點有疑惑,它也可以讓你一個下午也弄不明白。當算法完成之后,就可以立即開始組織編碼,從數據結構入手,理清程序流程,然后把各個子模塊一一攻破。算法花了幾天,而編碼一天內便能完成,我們日后的重點也因在構思這塊。碼農于軟件工程師的共同點是都會編程,而軟件工程師之所以不同是因為他能以程序化思想解決問題本身,而編碼是碼農們的事了。
總結
以上是生活随笔為你收集整理的关于做人工智能—五子棋的总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#实现文本发音
- 下一篇: 东华oj1-求长方形的面积和周长C++