AI实现的基本思路-极大极小值搜索算法

五子棋AI教程第二版发布啦,地址:https://github.com/lihongxun945/myblog/labels/%E4%BA%94%E5%AD%90%E6%A3%8BAI%E6%95%99%E7%A8%8B%E7%AC%AC%E4%BA%8C%E7%89%88

当前这个是旧版教程,强烈建议阅读新版教程。

五子棋看起来有各种各样的走法,而实际上把每一步的走法展开,就是一颗巨大的博弈树。在这个树中,从根节点为0开始,奇数层表示电脑可能的走法,偶数层表示玩家可能的走法。

假设电脑先手,那么第一层就是电脑的所有可能的走法,第二层就是玩家的所有可能走法,以此类推。

我们假设平均每一步有50种可能的走法,那么从根节点开始,往下面每一层的节点数量是上一层的 50被,假设我们进行4层思考,也就是电脑和玩家各走两步,那么这颗博弈树的最后一层的节点数为 50^4 = 625W 个。

先不考虑这么多个节点需要多久能算出来。有了对博弈树的基本认识,我们就可以用递归来遍历这一棵树。

那么我们如何才能知道哪一个分支的走法是最优的,我们就需要一个评估函数能对当前整个局势作出评估,返回一个分数。我们规定对电脑越有利,分数越大,对玩家越有利,分数越小,分数的起点是0。

我们遍历这颗博弈树的时候就很明显知道该如何选择分支了:

  • 电脑走棋的层我们称为 MAX层,这一层电脑要保证自己利益最大化,那么就需要选分最高的节点。
  • 玩家走棋的层我们称为MIN层,这一层玩家要保证自己的利益最大化,那么就会选分最低的节点。

这也就是极大极小值搜索算法的名称由来。直接盗一个图说明这个问题:

此图中甲是电脑,乙是玩家,那么在甲层的时候,总是选其中值最大的节点,乙层的时候,总是选其中最小的节点。

而每一个节点的分数,都是由子节点决定的,因此我们对博弈树只能进行深度优先搜索而无法进行广度优先搜索。深度优先搜索用递归非常容易实现,然后主要工作其实是完成一个评估函数,这个函数需要对当前局势给出一个比较准确的评分。

这篇博客讲的五子棋算法已经全部用JS实现并且是开源的,源码地址: https://github.com/lihongxun945/gobang ,预览地址: http://gobang.light7.cn/

可以clone这个仓库,参考其中js目录下的源码。因为代码量不小,后面要讲的只是关键部分的代码实现,不会贴完整的代码。而且关键代码可能会省略部分实现,UI因为跟AI算法无关,也不会讲,请自行参考源码。

我们下面来分部实现其中几个关键的部分。

极大极小值搜索

五子棋是一个15*15的棋盘,因为棋盘大小不会变动,所以目前来看用 15*15 的二维数组来存储,效果是最好的。

极大极小值的搜索比较简单,就是一个DFS,直接上代码,看不懂的可以

var maxmin = function(board, deep) {var best = MIN;var points = gen(board, deep);     //这个函数的作用是生成待选的列表,就是可以下子的空位。后面会讲这个方法。for(var i=0;i<points.length;i++) {var p = points[i];board[p[0]][p[1]] = R.com;     //尝试下一个子var v = min(board, deep-1);     //找最大值//如果跟之前的一个好,则把当前位子加入待选位子if(v == best) {bestPoints.push(p);}//找到一个更好的分,就把以前存的位子全部清除if(v > best) {best = v;bestPoints = [];bestPoints.push(p);}board[p[0]][p[1]] = R.empty;     //记得把尝试下的子移除}var result = bestPoints[Math.floor(bestPoints.length * Math.random())]; //在分数最高的几个位置中随机选择一个return result;
}var min = function(board, deep) {var v = evaluate(board);     //重点来了,评价函数,这个函数返回的是对当前局势的估分。if(deep <= 0 || win(board)) {return v;}var best = MAX;var points = gen(board, deep);for(var i=0;i<points.length;i++) {var p = points[i];board[p[0]][p[1]] = R.hum;var v = max(board, deep-1);board[p[0]][p[1]] = R.empty;if(v < best ) {best = v;}}return best ;
}var max = function(board, deep) {var v = evaluate(board);if(deep <= 0 || win(board)) {return v;}var best = MIN;var points = gen(board, deep);for(var i=0;i<points.length;i++) {var p = points[i];board[p[0]][p[1]] = R.com;var v = min(board, deep-1);board[p[0]][p[1]] = R.empty;if(v > best) {best = v;}}return best;
}

源码里面是有 Alpha Beta剪枝的,不过剪枝代码其实就几行,可以直接看源码,在 js/max-min.js 里面。

评估函数

在源码中 js/evaluate.js 就是评估函数,它接受一个二维数组,返回一个整型数。

我们对五子棋的评分是简单的把棋盘上的各种连子的分值加起来得到的,对各种连子的基本评分规则如下:

  • 成五,100000
  • 活四, 10000
  • 活三 1000
  • 活二 100
  • 活一 10

如果一侧被封死但是另一侧没有,则评分降一个档次,也就是死四和活三是相同的分

  • 死四, 1000
  • 死三 100
  • 死二 10

score.js 中是对各种情况估值的定义。

按照这个规则把棋盘上电脑的所有棋子打分,之和即为电脑的单方面得分 scoreComputer,然后对玩家的棋子同样打分 得到 scoreHuman

scoreComputer - scoreHuman 即为当前局势的总分数。

那么如何找出所有的连子呢,目前我的方式是先把二维的棋盘按照横竖撇捺四个方向变成N个一维数组,这个过程称为 flat。flat.js 是一个专门实现这个功能的模块。
然后我们就可以对所有的一维数组进行得分计算。
评分函数代码领比较大,请自行参考 evalute.js

请注意,这里我们说的评估函数是对整个棋局的评估,后面讲启发式搜索的时候还会有一个启发式评估函数是对某一个位置的评估。这两个评估函数是不同的

generator函数

generator顾名思义,就是在每一步生成所有可以落子的点。并不是所有的空位我们又要搜索,很多位置明显不合适的我们可以直接排除。
这个函数非常重要,是优化性能的关键。不过目前我们只做一个最简单的实现。就是把所有周围有邻居的空位认为是合适的位子。

有邻居的定义是:想个两步以内至少有一个不为空的点即可。比如 b[7,7] 有一个子,那么 b[6,7]是他的邻居,b[5,7] 也是,但是 b[4,7] 就不是,因为相隔了三步。

var gen = function(board, deep) {var neighbors = [];var nextNeighbors = [];for(var i=0;i<board.length;i++) {for(var j=0;j<board[i].length;j++) {if(board[i][j] == R.empty) {if(hasNeighbor(board, [i, j], 1, 1)) { //必须是有邻居的才行neighbors.push([i, j]);} else if(deep >= 2 && hasNeighbor(board, [i, j], 2, 2)) {nextNeighbors.push([i, j]);}}}}return  neighbors.concat(nextNeighbors)
}

这里做了一个简单的优化,就是按照邻居的远近做了一个排序,为什么要排序。后面讲AB剪枝的时候会讲到。排序对AB剪枝的效率起了决定性作用。

另外一些简单的函数,没有列出,直接去看源码就行了

下面我们将讲一个重点: Alpha Beta 剪枝算法。

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

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

    剪枝是必须的 五子棋AI教程第二版发布啦,地址:https://github.com/lihongxun945/myblog/labels/%E4%BA%94%E5%AD%90%E6%A3%8BAI% ...

  2. alpha-beta剪枝五子棋c语言,五子棋AI算法第三篇-Alpha Beta剪枝

    剪枝是必须的 上一篇讲了极大极小值搜索,其实单纯的极大极小值搜索算法并没有实际意义. 可以做一个简单的计算,平均一步考虑 50 种可能性的话,思考到第四层,那么搜索的节点数就是 50^4 = 6250 ...

  3. java五子棋AI算法人机对战(春物彩羽版可下载试玩PC端)

    五子棋AI算法 前言: 坐标西安,写于疫情封城期间.改进了之前写的基于极大极小值策略AI五子棋游戏,是用java实现的,采用了java老旧的jframe窗体和绘图类.写好之后整理成了这篇博客. 游戏采 ...

  4. 基于博弈树的五子棋 AI 算法及其 C++ 实现

    基于博弈树的五子棋 AI 算法及其 C++ 实现 摘要 一   五子棋的游戏规则 二   五子棋对弈的算法描述 2.1 博弈树搜索算法 2.2 α ─ β 剪枝 2.3 估价函数 三   五子棋对弈的 ...

  5. 五子棋AI算法-Alpha Beta剪枝

    上一篇讲了极大极小值搜索,其实单纯的极大极小值搜索算法并没有实际意义. 可以做一个简单的计算,平均一步考虑 50 种可能性的话,思考到第四层,那么搜索的节点数就是 50^4 = 6250000,在我的 ...

  6. 【AI测试】也许这有你想知道的人工智能 (AI) 测试--第二篇

    概述 此为人工智能 (AI) 测试第二篇 第一篇链接 第一篇主要介绍了 人工智能测试.测试什么.测试数据等.第二篇主要介绍测试用例和测试报告. 之后的文章可能具体介绍如何开展各项测试,以及具体项目举例 ...

  7. (只此一篇便绝b能懂的)五子棋AI算法原理,博弈树、极大极小搜索、αβ剪枝

    我在最近撰写五子棋AI程序设计报告时,翻阅了很多的资料博客,但却发现大佬们的博客,没有一篇是能让我只看它就能理解全部的AI算法.在看了众多博客后,我终于对博弈树.极大极小搜索.αβ剪枝恍然大悟,其实这 ...

  8. 课程设计书五子棋AI算法及其实现

    五子棋AI,能根据棋盘局势判断棋子应落在何处获胜,主要算法有权值法和博弈树法两种实现方案. 权值法 在数理统计中,有一种名为蒙特卡洛法的方法常被使用,其主要内容为:根据事件出现的概率估计某些特征,并将 ...

  9. 五子棋算杀c语言,五子棋AI算法-算杀(示例代码)

    关于剪枝问题 前面讲到的通过Alpha-Beta剪枝和启发式搜索可以将4层搜索的平均时间降低到1秒以下.只有这两个优化方式其实目前最多可以做到6层搜索,就是把AI和玩家各向后推算三步. 6层搜索的棋力 ...

最新文章

  1. angular 字符串转换成数字_蓝盟IT外包,Python算法的一般技术和嵌入式库|python|字符串|key|算法|调用...
  2. 数据结构之【线性表】(顺序表、链表的基本操作实现)
  3. Apiggs —— 非侵入性的 RestDoc 文档生成工具
  4. DNS_ARP_DHCP协议
  5. 局内网用户访问wamp本地站点
  6. (转)Geth控制台使用及Web3.js使用实战
  7. HASH暴力破解工具-Hashcat
  8. SparkStreaming Exception in thread main java.lang.IllegalArgumentException xxx is not a valid
  9. mybatis中collection中的ofType=“String“时
  10. 每天学一点flash(11) as3.0 与asp 通信 (1)
  11. 某公司防火墙配置-2
  12. opencv中的Mat图使用CDC显示
  13. python闭包和函数调用区别_对python闭包(内嵌函数)的理解
  14. Linux下的定时任务Cron
  15. 推荐一款好用的telnet工具
  16. 用Python画填色的中国分省地图(数据+源代码)
  17. [LeetCode] 969. 煎饼排序
  18. python爬小说一本一本爬_【学习笔记】Python爬取某一本小说
  19. 百度地图AR识别SDK免费推出
  20. Java消息队列与JMS的诞生

热门文章

  1. 西部数据在磁盘里加NAND却不用做缓存?
  2. ArrayList - 系列
  3. 成天路由器,路由器究竟是什么
  4. PTA 7-4 十进制转二进制 (10 分)
  5. 案例5.3 我国各地区财政支出的因子分析
  6. 微信收费的真相,你不可不知道!
  7. Vue知识点梳理(思维导图版)
  8. 精简WordPress eXtended Rss (WXR) 文件格式
  9. mysql sql%rowcount_SQL%ROWCOUNT应用
  10. ttk Treeview