五子棋AI算法-Zobrist

  这个博客不是把五子棋算法研究透彻之后再写的,而是一边研究算法一边写代码,同时一边写博客,所以有些博文的顺序不太对,比如 Zobrist 其实应该放在算杀之前就讲的。不过这并没有大的影响,总体上的顺序是OK的。

  另外,这一系列博客讲的五子棋代码其实是一个开源的项目,源码地址:https://github.com/lihongxun945/gobang

  由于是边写代码边写博客,所以博客中的代码不是最新的,甚至是有bug的,所以源码请尽量参考上述开源项目中的代码。比如之前讲极大极小值搜索改为负极大值的时候,对玩家的评分就出现了一个重要bug,在后序的提交中修正了这个bug。

Zobrist

  Zobrist 是一个快速Hash算法,非常适合用在各种棋类游戏中(事实上也是在各种棋类游戏中有大量应用)。

  我们前面讲了负极大值搜索和算杀,其实很多时候会有重复的搜索,比如这种:

[7,7],[8,7],[7,6],[7,9]

  其实它和下面这种的走法只是顺序不同 ,最终走出来的局面是一样的:

[7,6],[7,9],[7,7],[8,7]

  那么如果我们搜索中碰到了上面两种情况,我们会对两种情况都进行一次打分,而其实有了第一次的打分,完全可以缓存起来,第二次就不用打分直接使用缓存数据了。除了这种情况,其实以前的搜索结果也可以存下来,可以用在启发式搜索中。

  那么现在的问题就是,我们应该怎么表示一种局面呢?显然需要通过一种哈希算法,而且这个算法不能太慢,不然可能反而会降低搜索速度。而 Zobrist 就是一种满足我们需求的快速数组哈希算法。关于Zobrist算法请参考 https://en.wikipedia.org/wiki/Ben_Zobrist

  Zobrist 效率非常高,每下一步棋,只需要进行一次 异或 操作,相对于对每一步棋的打分来说,这一次异或操作带来的性能消耗可以忽略不计。Zobrist具体实现如下:

  • 初始化一个两个 Zobrist[M][M] 的二维数组,其中M是五子棋的棋盘宽度。当然也可以是Zobrist[M*M] 的一维数组。设置两个是为了一个表示黑棋,一个表示白旗。
  • 上述数组的每一个都填上一个随机数,至少保证是32位的长度(即32bit),最好是64位。初始键值也设置一个随机数。
  • 每下一步棋,则用当前键值异或Zobrist数组里对应位置的随机数,得到的结果即为新的键值。如果是删除棋子(悔棋),则再异或一次即可。

对应的JS代码如下:

 1 var Zobrist = function(size) {
 2   this.size = size || 15;
 3 }
 4
 5 Zobrist.prototype.init = function() {
 6   this.com = [];
 7   this.hum = [];
 8   for(var i=0;i<this.size*this.size;i++) {
 9     this.com.push(this._rand());
10     this.hum.push(this._rand());
11   }
12
13   this.code = this._rand();
14 }
15
16 Zobrist.prototype._rand = function() {
17   return Math.floor(Math.random() * 1000000000);  //再多一位就溢出了。。
18 }
19
20 Zobrist.prototype.go = function(x, y, role) {
21   var index = this.size * x + y;
22   this.code ^= (role == R.com ? this.com[index] : this.hum[index]);
23   return this.code;
24 }

  源码在 zobrist.js 文件里。

  注意每次走棋都要进行一次zobrist操作。千万不要自行设计哈希函数,除非你能保证你的哈希函数比一次64位整数的异或操作更简单,并且同时证明冲突的概率很低。Zobrist数组中的随机数的 质量 很重要,不过我用JS内置的 Math.random() 生成的随机数暂时没有发现问题,如果这个随机度不够高,可以考虑换用一些更好的随机函数。

  有了这个快速hash算法,我们就可以通过一个64位的整数来表示一个棋局。至于该存哪些信息,该怎么使用,下一篇再讲。

时间: 2024-12-19 11:55:59

五子棋AI算法-Zobrist的相关文章

五子棋AI算法

之前说想写一些比较大型的算法,想了半天,还是觉得写五子棋的AI比较合适.一则因为自己研究过这个,有一些基础,二则尽管现在网上有很多五子棋AI算法的资料,但是确实都有些晦涩难懂.就想着借这个机会,凭自己的理解,尽量的讲通俗一些.然而,这个算法确实有些复杂,想要通俗一些需要较大的篇幅,一篇博客难以讲完,这里就分很多个章节,一点一点的深入探讨.为了让文章更加通俗一些,我会略去一部分很简单但是占用篇幅很长的代码,改为用几行注释说明. 框架的搭建 首先,我们计划是做一个五子棋AI,也就是说让玩家和这个AI

五子棋AI算法第二篇-极大极小值搜索算法

AI实现的基本思路-极大极小值搜索算法 五子棋看起来有各种各样的走法,而实际上把每一步的走法展开,就是一颗巨大的博弈树.在这个树中,从根节点为0开始,奇数层表示电脑可能的走法,偶数层表示玩家可能的走法. 假设电脑先手,那么第一层就是电脑的所有可能的走法,第二层就是玩家的所有可能走法,以此类推. 我们假设平均每一步有50种可能的走法,那么从根节点开始,往下面每一层的节点数量是上一层的 50被,假设我们进行4层思考,也就是电脑和玩家各走两步,那么这颗博弈树的最后一层的节点数为 50^4 = 625W

五子棋AI算法-算杀

关于剪枝问题 前面讲到的通过Alpha-Beta剪枝和启发式搜索可以将4层搜索的平均时间降低到1秒以下.只有这两个优化方式其实目前最多可以做到6层搜索,就是把AI和玩家各向后推算三步. 6层搜索的棋力其实相当弱,碰到经常玩五子棋的人基本都会输,更不要说对五子棋有研究的玩家.以目前的平均一个节点有50个子节点的搜索方式,把搜索效率提高50倍则可以增加一层搜索深度.而除了前面讲到的AlphaBeta剪枝和启发式搜索,其他的剪枝算法基本都是非安全剪枝.也就是说后序我们会使用一些非安全的剪枝算法来提升搜

五子棋AI算法第三篇-Alpha Beta剪枝

剪枝是必须的 上一篇讲了极大极小值搜索,其实单纯的极大极小值搜索算法并没有实际意义. 可以做一个简单的计算,平均一步考虑 50 种可能性的话,思考到第四层,那么搜索的节点数就是 50^4 = 6250000,在我的酷睿I7的电脑上一秒钟能计算的节点不超过 5W 个,那么 625W 个节点需要的时间在 100 秒以上.电脑一步思考 100秒肯定是不能接受的,实际上最好一步能控制在 5 秒 以内. 顺便说一下层数的问题,首先思考层数必须是偶数.因为奇数节点是AI,偶数节点是玩家,如果AI下一个子不考

人机ai五子棋 ——五子棋AI算法之Java实现

人机ai五子棋 下载:chess.jar (可直接运行) 源码:https://github.com/xcr1234/chess 其实机器博弈最重要的就是打分,分数也就是权重,把棋子下到分数大的地方,我获胜的概率就更大. 而在下棋过程中,大部分的点的得分都很小,或者接近,因此无需对每一个点都打分,只需要在我方附近(进攻)或者敌方附近(防守)的几个点进行打分. 具体原理大家可以看源码中的注释,说明的很清楚. 参考 http://blog.csdn.net/pi9nc/article/details

五子棋AI算法-迭代加深

前面讲到了算杀,其实在算杀之前应该讲一下迭代加深.因为这些文章是我边做边写的一些笔记,所以顺序上可能不是那么严谨. 按照前面的所有算法实现之后(当然不包括算杀),会发现一个比较严重的问题,就是电脑在自己已经胜券在握的情况下(有双三之类的棋可以走),竟然会走一些冲四之类的棋来调戏玩家.这种走法出现的本质就是因为现在的AI只比较最终结果,并没有考虑到路径长短.所以很容易出现在6层搜索到一个双三,其实在4层的时候也有一个双三,因为分数一样,AI会随机选择一个走法.就导致了明明可以两步赢的棋,AI非要走

五子棋AI算法-重构代码

为什么需要重构 之前的代码有很多松散的模块组合在一起.在把 Zobrist 集成进去时,会发现全部需要走棋的操作其实都需要进行一次 Zobrist 异或操作.另外在逻辑上,其实很多模块都是可以合并到同一个类的,所以这次把代码进行了一次大的重构.所以如果发现博客说的一些模块找不到了也是很正常的,因为大部分模块都被移到了 Board 类中. 这次重构主要的工作就是 把AI相关的代码分成了四个模块: Board ,所有和棋子相关的操作都在这里,包括打分,判断胜负,zobrist缓存,启发函数等. ne

H5版俄罗斯方块(3)---游戏的AI算法

前言: 算是"long long ago"的事了, 某著名互联网公司在我校举行了一次"lengend code"的比赛, 其中有一题就是"智能俄罗斯方块". 本着一向甘做分母, 闪耀分子的绿叶精神, 着着实实地打了一份酱油. 这次借学习H5的机会, 再来重温下俄罗斯方块的AI编写. 本系列的文章链接如下: 1). 需求分析和目标创新 2). 游戏的基本框架和实现 这些博文和代码基本是同步的, 并不确定需求是否会改变, 进度是否搁置, 但期翼自己能

棋牌源码搭建教程之棋牌游戏AI算法

棋牌游戏客户端实现采用Flash 9开发,服务端采用Win32+VC6开发(基于IOCP),数据库网关采用Win32+VC6开发(基于IOCP,MySQL5实现了处理线程池和数据库连接池).虽然服务器端去年就已经完成,但相应的机器人AI算法一直没有能力去实现.今天把它拿到Blog上来希望有机会和感兴趣的兄弟们探讨下. Kevin在他的Blog上给出了他的实现,其给出的算法思想是用宽度优先生成一棵搜索树,再根据玩牌的技巧进行剪枝与判权,机器人的AI能够像养成类游戏那样,实现在蹂躏下慢慢成长,水平逐