【五子棋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
接下来我们貌似可以讨论招法如何产生的问题了,但在这之前,必须要明确的问题是模板。很多五子棋程序定义模板并给出评分,开始我也是这么做的,并且做得比较彻底:手工制作模板从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
相关文章推荐
- 【五子棋AI】AI的基本结构——剪裁函数
- 【五子棋AI】AI的基本结构——局面表示
- 【五子棋AI】AI的基本结构——局面评价
- AI:IPPR的数学表示-CNN结构分析(基本结构)
- 【机器学习实战】制作五子棋AI之三:基本规则的建立【1】
- 【机器学习实战】制作五子棋AI之四:基本规则的建立【2】
- AI:IPPR的模式生成-学习/训练方式(基本结构)
- 数据结构(十二) 二叉树的基本操作 --- 创建一个二叉树 前中后序遍历二叉树
- UML——基本结构
- 了解Java虚拟机JVM的基本结构及JVM的内存溢出方式
- hive基本结构与数据存储
- 高质量的C++编程指南总结(一):文件结构、程序版式、命名规则、表达式和基本语句
- 数据库索引的运行原理及基本结构
- JVM学习笔记(一)------基本结构
- 结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆、斜堆
- 基本的网格结构
- Java面试问题集锦04-java程序的基本结构(2)
- Android项目基本结构详解
- 深入理解JVM内幕:从基本结构到Java 7新特性
- 一起写RPC框架(九)RPC服务提供端一--服务端的基本代码结构