五子棋AI算法第五篇-算杀

关于剪枝问题

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

6层搜索的棋力其实相当弱,碰到经常玩五子棋的人基本都会输,更不要说对五子棋有研究的玩家。以目前的平均一个节点有50个子节点的搜索方式,把搜索效率提高50倍则可以增加一层搜索深度。而除了前面讲到的AlphaBeta剪枝和启发式搜索,其他的剪枝算法基本都是非安全剪枝。也就是说后序我们会使用一些非安全的剪枝算法来提升搜索效率,而这样做的话,有一定的概率会剪掉一些有益的分支。

这里我们把启发式搜索也当做一种剪枝算法,其实它的实际作用就是剪枝。

克服水平线效应

假设我们目前搜索深度为N层,那么AI只会考虑N层以内的利益,而对N+1层以及以后的局势全然没有任何考虑。那么造成的结果就是AI就是一个视力为N的近视眼,很容易因为N层以内的短期利益而造成大局上的劣势,这就是水平线效应。

如果我们提高搜索深度,依然会存在水平线效应,只是这个水平线会隐藏的更深。想要克服水平线效应,那么就需要在一定情况下对N层以外的节点进行搜索。当然,我们不可能搜索整个博弈树,所以只能在某些前提下进行一些代价不高的深入搜索。

这里我们先实现一种最简单的克服水平线效应的方式-算杀。所谓算杀就是计算出杀棋,杀棋就是指一方通过连续的活三和冲四进行进攻,一直到赢的一种走法。
很显然,同样的深度,算杀要比前面讲的搜索效率高很多。因为算杀的情况下,每个节点只计算活三和冲四的子节点。所以同样是1秒钟的时间,搜索只能进行4层,而算杀很多时候可以进行到12层以上。
为了方便,我们把前面的讲到全面的极大极小值搜索简称为搜索

而且很容易想到,算杀其实也是一种极大极小值搜索,具体的策略是这样的:

  • MAX层,只搜索己方的活三和冲四节点,只要有一个子节点的能赢即可
  • MIN 层,搜索所有的己方和对面的活三和冲四节点(进攻也是防守),只要有一个子节点能保持不败即可。

算杀的代码实现如下:

/*
 * 算杀
 * 算杀的原理和极大极小值搜索是一样的
 * 不过算杀只考虑冲四活三这类对方必须防守的棋
 * 因此算杀的复杂度虽然是 M^N ,但是底数M特别小,可以算到16步以上的杀棋。
 */

/*
 * 基本思路
 * 电脑有活三或者冲四,认为是玩家必须防守的
 * 玩家防守的时候却不一定根据电脑的棋来走,而是选择走自己最好的棋,比如有可能是自己选择冲四
 */

var R = require("./role.js");
var hasNeighbor = require("./neighbor.js");
var scorePoint = require("./evaluate-point.js");
var S = require("./score.js");
var win = require("./win.js");
var config = require("./config.js");

//找到所有比目标分数大的位置
var find = function(board, role, score) {
  var result = [];
  for(var i=0;i<board.length;i++) {
    for(var j=0;j<board[i].length;j++) {
      if(board[i][j] == R.empty) {
        var p = [i, j];
        if(hasNeighbor(board, p, 2, 1)) { //必须是有邻居的才行

          if(role == R.empty) {
            var s1 = scorePoint(board, p, R.com);
            var s2 = scorePoint(board, p, R.hum);
            var s = s1+s2;
            if(s > score) {
              p.score = s;
              result.push(p);
            }
          } else {
            var s = scorePoint(board, p, role);
            if(s >= score) {
              p.score = s;
              result.push(p);
            }
          }
        }
      }
    }
  }
  //注意对结果进行排序
  result.sort(function(a, b) {
    return b.score - a.score;
  });
  return result;
}

var max = function(board, role, deep) {
  var w = win(board);
  if(w == role) return true;
  if(w == R.reverse(role)) return false;
  if(deep < 0) return false;

  var points = find(board, role, S.BLOCKED_FOUR);
  if(points.length == 0) return false;
  for(var i=0;i<points.length;i++) {
    var p = points[i];
    board[p[0]][p[1]] = role;
    var m = min(board, role, deep-1);
    board[p[0]][p[1]] = R.empty;
    if(m) {
      if(m.length) {
        m.unshift(p); //注意 unshift 方法返回的是新数组长度,而不是新数组本身
        return m;
      } else {
        return [p];
      }
    }
  }
  return false;
}

//只要有一种方式能防守住,就可以了
var min = function(board, role, deep) {
  var w = win(board);
  if(w == role) return true;
  if(w == R.reverse(role)) return false;
  if(deep < 0) return false;
  var points = find(board, R.empty, S.FOUR);
  if(points.length == 0) return false;

  var cands = [];
  for(var i=0;i<points.length;i++) {
    var p = points[i];
    board[p[0]][p[1]] = R.reverse(role);
    var m = max(board, role, deep-1);
    board[p[0]][p[1]] = R.empty;
    if(m) {
      m.unshift(p);
      cands.push(m);
      continue;
    } else {
      return false; //只要有一种能防守住
    }
  }
  return cands[Math.floor(cands.length*Math.random())];  //无法防守住
}

算杀算法的集成

有了算杀模块之后,我们可以直接对当前棋局进行算杀,但是显然更好的做法是在搜索中进行算杀,通过N层搜索结合M层算杀,我们可以最多搜索到N+M层。

至于如何组合算杀和搜索,最简单的是在每一个非连五的叶节点都进行一次算杀。为了避免算杀出现非最优解,还需要进行迭代加深,具体做法下一章再讲。

  • 11
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
作品背景: “α狗”是“Alpha Go”,智能计算系统。“α”是第一个希腊字母,代表了开始,一个人工智能时代的开始,为了顺应科技潮流,我们设计并实现了βGo的五子棋的人机对战 设计思路: (1)初始化:首先,建立盘面数组Table[15][15]、 棋型表Computer[15][15][4] Player[15][15][4]; (2)主循环控制模块:主要担当一个调度者的角色。 (3)玩家下子:在Table[15][15]数组的相应地方记录‘2’,以 表明该子是玩家下的。 (4)盘面分析填写棋型表:人工智能算法的根本依据! (5)电脑下子:现在要作的就是让电脑知道在哪一点下子了。其中最简单的计算方法,就是遍历棋型表Computer[15][15][4]和Player[15][15][4]找出其中数值最大的一点,在该点下子即可。 (6)胜败判定:务须多言,某方形成五子连即获胜; 五子棋MFC界面展示: 心得体会: 这次制作人机对战五子棋,从一横一竖的棋盘制作,到键盘鼠标控制棋子下落,最终达到MFC的界面优化。从简单的人人对战判断输赢,到最终实现人机对战,甚至运用极大极小算法设置计算机智力等级。一行行代码,一次次编译失败,一次次调试,我们收获了C的乐趣,体会了编程的魅力。 未来展望: 当然还有太多的需要改进和提升的地方了,我们暂时还没有实现悔棋和添加残局的功能,希望在以后的学习中能够实现。
1.项目代码均经过功能验证ok,确保稳定可靠运行。欢迎下载体验!下载完使用问题请私信沟通。 2.主要针对各个计算机相关专业,包括计算机科学、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师、企业员工。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、初期项目立项演示等用途。 4.当然也鼓励大家基于此进行二次开发。在使用过程中,如有问题或建议,请及时沟通。 5.期待你能在项目中找到乐趣和灵感,也欢迎你的分享和反馈! 【资源说明】 Java开发基于Alpha-Beta剪枝极大极小博弈算法五子棋AI游戏源码+项目说明.zip 程序设计分析 在开始编写程序之前,我们应该先要对五子棋游戏要做的事进行剖析,明确设计任务,功能要求等等。通过程序要实现的功能,设定具体的每个模块所完成的每一个功能,然后连接每一个模块来实现所需要的功能设计。 画棋盘 绘制左侧的棋盘:绘制棋盘线、绘制天元和星、绘制预选框、绘制左侧数字和底下字母、绘制棋子、给棋子添加序号。 绘制右侧的功能区:添加显示棋子估值信息区域、模式:人人对战、人机对战、智能:估值函数、估值函数+搜索树、搜索树:搜索深度、每层节点、其他:显示落子顺序、悔棋、新游戏、人类先手、机器先手 添加事件 在下棋区域鼠标移动,预选框跟随鼠标移动、在交叉点点击落子、悔棋、新游戏、显示落子顺序、人类先手、机器先手、在棋子上点击鼠标右键显示估值 人人下棋 在交叉点落子的时候,判断输赢 人机下棋 估值函数、极大极小值搜索、Α-β剪枝优化。 估值函数:计算某个点的多价值有高。 首先设计好可能出现的棋型,并且根据会赢的概率给出相应的分值。 攻:对我自己有利的棋型的价值 守:对对方有利的棋型的价值 某个点的价值 = 攻价值+守价值 估值函数有个弊端,只考虑眼前的事 极大极小值搜索: 思想:轮到自己下棋的时候,找最大值。轮到对方下的时候,寻找最小值。但是这样计算量是比较大的。开始游戏有,棋盘上是有一个黑子。人工下一个白子。搜索5层。第一个层可选择位置:223。第二层:223x222。第三层 223 x 222 x 221 …… Α-β剪枝优化:Α-β是两个值,Α自己,β对手。初始值Α是无穷小的数。β是无穷大的数。A<β这个场景才会合法。否则这枝(树枝)将被剪掉(不进行遍历) 如果是自己的棋,得到得分以后,和Α进行比较,如果得分大于Α。Α=score 如果是对手的棋,得到得分以后,和β进行比较,如果得分小于β。Β=score

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值