五子棋AI循序渐进【5】重点问题重点分析——静态搜索

        仍然在写置换表,但是还存在一些问题,所以还会后延一段时间。不过已经可以明确的是,使用哈希表确实没有数组速度快(较为理想的情况是用纯粹的alpha-beta剪裁和空招剪裁——这也是我最初设计的想法,并没有其他技术,迭代加深至少可以处理7次,多是8次,甚至可以达到10——但是,如果加入其他代码,速度会降下来一些,也就是说,我们的评价函数还是太慢太慢,不过即使我们进行各种剪裁,五子棋的复杂度也还是很高,这也是一方面原因吧。),所以大部分代码还是在借鉴开源的象棋引擎。毕竟我的目标不是要写一个多么出色的五子棋引擎,而是把这些技术介绍给大家,并且用VB.NET代码展示出来。好了,言归正传。

1、什么是静态搜索

它是一个在达到指定深度(迭代加深)之后,用来评价局面的函数,是局面评价的进一步延伸。它是一个不带深度参数的、非常“武断”的alpha-beta剪裁,它只评价对方冲棋或己方冲棋的情况。

2、为什么要进行静态搜索

防止我们搜索到若干步之后,找到的最佳招法是“昏招”,也就是进一步检查一下招法的可靠度。

3、如何实现静态搜索

仿照alpha-beta剪裁函数去写就可以了,只是把条件稍加修改,使之只分析对方冲棋、己方冲棋:

 

 

  '==================================静态搜索==================================
    '静态(Quiescence)搜索过程,实际上和alpha-beta搜索非常相似,但是目标是不同的,ab搜索达到深度就退出了,不管下面发生什么,哪怕下一步可以形成杀棋。
    '而静态搜索是ab搜索的延伸,它将处理这些的情况。
    Private Function SearchQuiesc(vlAlpha As Integer, vlBeta As IntegerAs Integer
        Dim i, nGenMoves As Integer
        nGenMoves = -1
        Dim vl, vlBest As Integer
        Dim mvs(MAX_GEN_MOVES) As Byte
        ' 一个静态搜索分为以下几个阶段
        '1. 到达极限深度就返回局面评价
        If pos.nDistance = LIMIT_DEPTH Then Return pos.Evaluate()
        '2. 初始化最佳值
        vlBest = -MATE_VALUE        '这样可以知道,是否一个走法都没走过(杀棋)

        '评价局面
        vl = pos.Evaluate()
        If vl = -3000 Then
            '3. 如果被冲棋,则取全部冲棋点作为走法。
            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
            '4.如果不被冲棋, 先做局面评价
            If vl > vlBest Then
                vlBest = vl
                If vl >= vlBeta Then
                    Return vl
                End If
                If vl > vlAlpha Then
                    vlAlpha = vl
                End If
            End If
            '5. 如果局面评价没有截断,生成冲棋走法,冲棋走法本身就是排序的无需再次排序。
            If vl = 3000 Then     '杀棋时无需进行评价,根本就没有走法,6的循环会被略过直接进入最后评价。
                '实际上这几行和3中完全一样,可以提取出来,但是为了结构更清晰,还是单独列出了。
                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
            End If
        End If

        '6. 逐一走这些走法,并进行递归
        For i = 0 To nGenMoves
            pos.AddPiece(mvs(i))
            vl = -SearchQuiesc(-vlBeta, -vlAlpha)
            pos.DelPiece(mvs(i))
            '7. 进行Alpha-Beta大小判断和截断
            If vl > vlBest Then         '找到最佳值(但不能确定是Alpha、PV还是Beta走法)
                vlBest = vl             '"vlBest"就是目前要返回的最佳值,可能超出Alpha-Beta边界
                If vl >= vlBeta Then    '找到一个Beta走法
                    Return vl           'Beta截断
                End If
                If vl > vlAlpha Then    '找到一个PV走法
                    vlAlpha = vl        '缩小Alpha-Beta边界
                End If
            End If
        Next
        '8. 所有走法都搜索完了,返回最佳值
        Return IIf(vlBest = -MATE_VALUE, pos.nDistance - MATE_VALUE, vlBest)
    End Function
    '==================================静态搜索结束==================================

 

在象棋中,被“将军”的情况和我们被“冲棋”非常类似。但是象棋被将军往往要生成全部走法,因为吃掉对方将军子,垫子,走将等都可以解决问题,但是,五子棋不是如此,五子棋考虑的是对方的冲棋点即可,或许也该考虑自己是不是比对方冲的更远了。

 

好了,这一集就到这里,关于置换表这里先提一下。置换表这个名字确实不讨人喜欢,因为它是一种“历史表”,只是历史表这个名词早早被占据了——它是记录搜索过程中哪个点产生的招法可能更好以对后续搜索进行排序的。所以,置换表也是一种历史表,它记录着搜索一定深度之后,得到的棋型的评分。这里的深度指迭代加深深度,而棋型本身,就记录着“步数”,因为形成相同局面所需的棋子个数是一样的,但是无论先后。我们要考虑的是,什么时候记录局面的变换,当然这包括下子还包括提子,是不是要考虑空步剪裁呢?如何记录,怎么提取等一系列问题。好了就预告到这里。

 

本集源码:

/Files/zcsor/清月连珠0.5.7z

 

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

http://www.vbdevelopers.org

http://www.softos.org

posted @ 2012-07-15 12:10  zcsor~流浪dè风  Views(2254)  Comments(2Edit  收藏  举报