您的位置:首页 > 大数据 > 人工智能

【五子棋AI】AI的基本结构——招法生成器

2013-10-06 21:43 459 查看
        在五子棋中,招法的生成其实“很容易”凡是没有子的点都是招法点,但这样做无疑是“低效”的,有很多文章(包括我前期的代码)都采用了“临近点”搜索的策略,例如下子H8之后,搜索周围5格以内的空点,但这也存在问题:随着棋子量的增多尤其是冲4然后对方堵死的情况,这种“棋盘剪裁”将迅速扩大搜索范围。经过观察我们可以发现,下五子棋的时候无非是要连得更长(无禁手),当长度超过4就胜利了。所以下子时所挑选的点无疑在同一线上,基于这种考虑我们完全可以只生成“冲棋点”作为走法。当然,这种做法也不是没有异议,尤其是开局阶段很多招法并不是这样的,于是有一些文章将“威胁空间搜索”视为一文不值,其实这个问题很简单,在后面讨论“启发算法”的时候,我会给出解决方法,并且寥寥几行代码,不仅很好的解决了这个问题而且有非常好的启发作用。

        

        接下来我们貌似可以讨论招法如何产生的问题了,但在这之前,必须要明确的问题是模板。很多五子棋程序定义模板并给出评分,开始我也是这么做的,并且做得比较彻底:手工制作模板从5长度一直到11长度(下一子之后受影响的大约都在5格之内,但实际上这不准确),但后来我测试VCF代码时发现我的模板存在不一致性,并且存在一些评分不正确的情况。于是用了一些时间把模板改为自动生成,这些自动生成的函数的工作非常快并且准确,至少现在我没有发现任何问题。而且我不再使用直接给出评分的方式,而是简单的归类。例如在PosInsHelper.vb中用这样一段代码生成从1到15长度的所有模板(模板当中只有有子、无子之分):

        For len = 1 To 15               

            For bitkey = 0 To 2 ^ len - 1    

                idxkey = bitkey Or ((17 - len) << (len + 1))

                If dm(idxkey) IsNot Nothing Then Throw New Exception("位置冲突避免算法错误,或重复调用填充函数")

                Dim cline(len - 1) As Byte

                For i = 0 To len - 1

                    If ((bitkey >> i) And 1) = 1 Then

                        cline(i) = LinkType.lnll

                    End If

                Next

                dm(idxkey) = cline

                If idxkey > keySpaceMax(len) OrElse idxkey < keySpaceMin(len) Then Throw New Exception("key空间算法错误")

            Next

        Next

这段代码当中唯一的亮点可能就是利用了二进制的特点用循环代替了递归调用,当得到全部模板后,用一个双层循环来得出胜利情况:

        For len = 5 To 15                                   

            For idxkey = keySpaceMin(len) To keySpaceMax(len)     

                Dim linkcount As Integer

                For i = 0 To len - 1

                    linkcount = 1

                    If ((idxkey >> i) And 1) = 1 Then  

                        For j = i + 1 To len - 1        '向低位检测连接长度

                            If ((idxkey >> j) And 1) = 1 Then

                                linkcount += 1

                            Else

                                Exit For

                            End If

                        Next

                        For j = i - 1 To 0 Step -1       '向高位检测连接长度

                            If ((idxkey >> j) And 1) = 1 Then

                                linkcount += 1

                            Else

                                Exit For

                            End If

                        Next

                        If linkcount = 5 Then

                            dm(idxkey)(i) = LinkType.lwin

                        ElseIf linkcount > 5 Then

                            dm(idxkey)(i) = LinkType.llng

                        End If

                    End If

                Next

            Next

        Next

当这个循环运行完成之后,dm当中对应的byte就会被设置为lwin或llng,即胜利或长连。当然,长连在后续处理中与胜利合并了。以此类推(注意顺序,要从“高”棋型向“低”棋型生成),直到得到最小的棋型(在我的代码中是冲1或活1)。

           好了,接下来我们可以讨论招法生成器是如何运行的。其实在这之前,额…………我们确实要考虑这样一些问题:

1、快速得到冲棋点

2、快速分辨冲棋点类型,如43肯定要比41来的重要……

3、按冲棋点类型排序冲棋点

          在我的代码中,定义了两个表:

    '各点的冲棋值表                 

    dim cpTable(1)(239) As Integer

    '冲棋点排序表

    Private cpSort(1)(239) As Integer       

当然这种定义方式是不允许的,只是为了更明确,这里这么写了。当添(提)一子时,根据坐标和走棋者,确定verkey更新的4个元素,进行更新后查找模板dm,确定需要更新的点坐标并将cpsort按冲棋点类型(2所述)进行分段排序(事实上cpsort是一个链表)。这样做更新的量就非常少、很准确、省去排序(为何排序在“剪裁函数”会解释)过程。

           接下来生成招法就简单了,按顺序逐个生成就可以了。当然,如果需要足够优化的话,不要一次性生成全部走法,原因也在于剪裁过程,但我的代码一直都是全部生成(其实没有啥好处的,只是我懒得改)。当然,招法生成不仅仅是这一点生成和“排序”,启发算法有很多是基于招法生成的。

           好了,至此招法生成就介绍完毕了。貌似只介绍了一段,其他的都是铺垫……

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

http://www.vbdevelopers.org

http://www.softos.org
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  五子棋