五子棋AI循序漸進【4】接近人類的思考方式——迭代加深、棋盤剪裁、空步剪裁、沖棋延伸


注:PCGO函數扔未修正添子方法。請自行修改。

一、迭代加深

1、什么是迭代加深

所謂迭代加深,就是讓alpha-beta剪裁運行深度1,然后運行深度1,2,然后運行深度1,2,3。

2、為什么進行迭代加深

這樣做的好處就是可以在搜索完一次之后,得到排序的依據——歷史表,然后下次搜索時,歷史表會給出一個大約的方向——下哪一步更好,也就是更容易產生截斷了。

3、這樣做增加了多少開銷

實際上,單純考慮迭代加深,它可能會多運行一點時間,但是考慮到歷史表,也差不多抵消這些時間了。更何況,還有置換表(我一直不太喜歡這個名字)。

4、如何實現迭代加深

想法很簡單,實際上代碼也很簡單,只需要實現一個循環,for i = 1 to n,i是要掃描的深度,在這個循環中調用alpha-beta剪裁即可。代碼就這么幾行:

 

     ' ===============================迭代加深===============================
     ' 迭代加深搜索過程
     Function SearchMain()  As  Integer
         Dim i, t, vl  As  Integer
         ' 初始化
        pos.ClearnHistoryTable()  '  清空歷史表
        mvsCompare.ms = pos.nHistoryTable
        t = My.Computer.Clock.TickCount  '  初始化定時器
        pos.nDistance =  0    ' 初始步數
        winplayr =  2         ' 勝利者
         ' 迭代加深過程
         For i =  1  To LIMIT_DEPTH_SearchFull -  1
            Debug.Print( " 正在迭代: " & i)
            vl = SearchFull(-MATE_VALUE, MATE_VALUE, i)
             ' 搜索到殺棋,就終止搜索
             If vl > WIN_VALUE  Then   ' 計算機勝利
                winplayr =  1
                 Exit  For
             End  If
             If vl < -WIN_VALUE  Then  ' 玩家勝利
                winplayr =  0
                 Exit  For
             End  If
             ' 超過一秒,就終止搜索
             If My.Computer.Clock.TickCount - t >  1000  Then
                 Exit  For
             End  If
         Next
        Debug.Print( " 迭代加深: " & i)
         Return pos.mvResult
     End Function
     ' ==============================================================================

 

整個循環中,先清空歷史表(但是我感覺不應該同時置換表,當然這是后話);然后調用alpha-beta的遞歸函數就可以了。其他的判斷都很容易理解。

 

 

二、棋盤剪裁

1、為什么剪裁棋盤

這可以大量剪裁掉不合理招法,在五子棋中,我們很容易遇見,如果下的子遠每個離黑子和白子,那一定是臭棋。那么遠離程度是多少呢,可能是4,因為要成5,所以最遠只能離當前子4格,但是真的是這樣嗎?讓我們大膽的猜測:其實第四格也是臭棋!為什么呢,中間有3個格的話,遠離程度也太大了,需要在該方向上再連續下3子才能連起來。而我們,有多大機會能這樣做呢?即使我們想,可我們成功的機會有多大呢?我想微乎其微——五子棋雙方的纏繞(指互相沖堵,當然好棋是沖並且堵着……)是非常嚴重的,所以,我們的結論是3而不是4。

2、如何實現

我的代碼中是先遍歷棋盤,找到黑子或白子,然后把他們周圍的非子點記錄下來。當然這存在一個重復記錄的問題,所以我是記錄在一個bitarray里面,它的操作速度非常快,然后再次遍歷它取出合理點。當然,另一種想法也許更好——遍歷所有點,並記錄每個三格以內有子的空點。但遺憾的是我最初意識到的是第一種做法。

 

這個代碼在上一個示例里面已經在使用了。這里不再羅列。

 

三、空步剪裁

1、什么是空步剪裁

就是輪到自己不下子卻不下,讓對方接着下。

轉載請注明出處:http://www.cnblogs.com/zcsor/

2、為什么進行空步剪裁

它基於這樣一種想法:如果己方不下,那對方會下哪?這對於五子棋來講,好處不僅僅是預測性的剪裁,更重要的是對方沖棋時,可以把沖棋點直接作為合理招法點:因為如果不去封堵,自己就要被殺死了!當然,在我的代碼中,由於評價函數還不完善(這里主要指分數設置方面的不准確性),所以程序會去封堵對方的單個沖3或活2,這完全可以通過重新規划分值解決。

3、如何限制的空步剪裁

首先,什么時候進行空步剪裁。如果對方已經沖棋了(沖4,活3等)那么再讓他走,無疑是不明智的。所以,只有對方不沖棋的時候,我們才進行空步剪裁。

其次,無限制的空步剪裁當然不是一個好辦法。從預測的角度來看,一步貌似有點少,兩步還可以,如果3步對方的沖2都成5了!所以,結論是2步。

4、如何實現

其實代碼很簡單,但是要建立在對alpha-beta剪裁已經充分理解的前提下。首先分析是否被沖棋,如果沒有,那么空步剪裁;如果有,用沖棋點作為接下來迭代的”合理招法“。這就是思路。代碼也就不復雜了,當然需要注意的是,空步剪裁對”深度“的影響:

 

         ' ====================================空步剪裁=====================================
         If pos.nDistance >  0  Then
             ' 1. 到達水平線
             If nDepth <=  0  Then  Return pos.Evaluate  '
             ' 1-1. 到達極限深度就返回局面評價
             If pos.nDistance = LIMIT_DEPTH_SearchFull  Then  Return pos.Evaluate()
             ' 1-2. 嘗試空步裁剪(根節點的Beta值是"MATE_VALUE",所以不可能發生空步裁剪)
             If pos.Evaluate() = - 3000  Then           ' 被沖棋時,根據沖棋點返回值生成走法,而不是生成全部走法。
                 ' 遍歷棋型信息,提取全部沖棋點。
                 For i =  0  To pos.Vectors.lnkinf.Count -  1
                     For j =  0  To pos.Vectors.lnkinf(i).cqpend
                        nGenMoves +=  1
                        mvs(nGenMoves) = pos.Vectors.lnkinf(i).cqp(j)
                     Next
                 Next
             Else                                     ' 未被沖棋時進行空步剪裁
                pos.NullMove()
                vl = -SearchFull(-vlBeta,  1 - vlBeta, nDepth - NULL_DEPTH -  1)
                pos.UnNullMove()
                 If (vl >= vlBeta)  Then
                     Return vl
                 End  If
             End  If
         End  If
         ' ==================================空步剪裁結束===================================

接下來我們討論沖棋延伸

 

四、沖棋延伸

1、什么是沖棋延伸

在對方沖棋時,我們無限制的進行迭代,直至分析出最終解。

2、為什么進行沖棋延伸

當對沖棋時,往往會形成比較險的連沖棋,一步失誤全盤皆輸,所以我們需要分析到最終局面——確切知道到底走哪步棋才是明智的。當然,這非常耗時,但同時也為置換表提供了非常多寶貴的局面。

3、如何實現沖棋延伸

基於我們的想法,我們無限延長迭代,使之達到最終解。但實際上,還是做了一些限制,我們沒有真正分析全部局面的時間。具體的限制就在上面的代碼中,”達到極限深度就返回局面評價“。而真正的沖棋延伸,實現起來很簡單:當被沖棋時,我們的迭代過程傳入的參數不是n-1,而是n,這樣深度不會減少到0,也就會繼續分析下去:

 

             ' =================================沖棋延伸================================
             ' vl = -SearchFull(-vlBeta, -vlAlpha, nDepth - 1)
             ' =========以上為被替換代碼==========
            vl = -SearchFull(-vlBeta, -vlAlpha,  IIf(pos.Evaluate = - 3000, nDepth, nDepth -  1))
             ' ===============================沖棋延伸結束==============================

 

 

那么,這集就結束了。

 

本集代碼:

/Files/zcsor/清月連珠0.4.7z

 

 

下一集預告:

靜態搜索。而靜態搜索之后,將會是置換表。置換表的發布可能要久一些。因為這幾天時間比較少,要實施好爸爸計划,還有一些其他的事情需要處理。

 

全部文章和源碼整理完成,以后更新也會在下面地址:

http://www.vbdevelopers.org

http://www.softos.org

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM