前言:
  对弈类游戏的智能算法, 网上资料颇多, 大同小异. 然而书上得来终觉浅, 绝知此事要躬行. 结合了自己的工程实践, 简单汇总整理下. 一方面是对当年的经典<<PC游戏编程(人机博弈)>>表达敬意, 另一方面, 也想对自己当年的游戏编程人生做下回顾.
  承接上三篇博文:
  (1). 评估函数+博弈树算法
  (2). 学习算法
  (3). 博弈树优化
  这篇博文着重谈谈游戏AI落地的问题, 游戏AI不是追求AI的无敌性, 而是应该迎合不同级别的用户水平. 同时游戏本身的用户体验, 是需要游戏开发者, 好好思索和斟酌的.

案例反思:
  (1). 案例一:
  以前写过J2ME版的中国象棋(模拟器性能好于真机的幸福时代).在模拟器上测试, 搜索深度设置为3,在时间消耗和智能表现达到很好的均衡,基本在2秒之内决策完成.
  后来客户用真机去测试的时候, 反馈没有丝毫响应, 当时想: 糟了,是不是遇到了机器相关的问题? 后来再反馈的时候,说是等了80多秒,才走了一步.
  这件事, 对我个人而言吸取的教训还挺大的, 有些参数不能基于经验来设置,对于不同机器和配置,需合理的选定配置值.
  总而言之: 适配很重要, 不光在不同机器的分辨率上需要, 性能预估也需要.
  (2). 案例二:
  有次写完黑白棋, 一开始各种被虐(内心其实很挺开心的). 在不断的尝试各种路线后,终于找到一种方式击败电脑, 由于电脑采用了静态评估函数, 每次选最优解. 导致电脑没有反馈能力, 一直在犯同一个错误. 这个问题让我(玩家)索然无味. 体验很不好.
  由此可见, 在智能AI中, 需要引入模糊性, 或者说是不确定.

迭代搜索:
  再解决上述问题之前, 让我们先来讲讲迭代搜索的思路和实现方式.
  迭代搜索逐步加深搜索深度, 进行博弈过程.

void negamax_driver(GameState S, int depth, Move best_move) {
  // 负无穷 ~ 正无穷
  (alpha, beta) <= (-INFINITY, INFINITY)
  foreach ( move in candidate list ) {
    S' = makemove(S);
    value = -negamax(S', depth - 1, -beta, -alpha);
    unmakemove(S')
    // 博弈树第一层不存在alpha+beta剪枝, 用于保存最优解
    if ( value > alpha ) {
      alpha = value;
      tmp_best_move = move
    }
  }
best_move = tmp_best_move
}

  函数negamax_driver不同于negamax函数, 它是极大极小搜索的第一层, 其不存在alpha+beta剪枝, 而且用于保存实际最优的解. 因此单独抽取出来.

Move iterative_deepening_search(GameState S) {
  // 定义best_move
  Move best_move
  // 遍历深度, 从 1 逐步加深
  for ( depth = 1; ; depth++ ) {
    negamax_driver(S, depth, best_move)
    // 判断是否满足退出条件, 一遍为超时判断
    if ( timeout() ) {
      break;
    }
  }
  return best_move
}

  函数iterative_deepening_search则形象描述了迭代搜索的整个过程, 逐步加深搜索深度, 然后调用负极大值搜索. 其中退出条件特别重要. 一般采用超时判断来作为退出条件的检测.
  迭代深搜提供了一个很好的思路, 或许你会问: 迭代搜索不是存在很多的重复计算吗? 其性能会不会很糟糕吗?
  其实不然, 我们简单算一笔账:

假设F(n)为深度n的性能消耗, m为可候选步数.
递推归纳公式为:
  F(n+1) = m * F(n)                     (m 远大于2)
对于单独进行深度为n的性能消耗为:
  F(n) = m^(n-1) * F(1)
进行1~n的迭代深搜性能代价为:
  S(n) = F(1) + F(2) + ... + F(n) = F(n) * (1 + 1/m + .. + 1/m^(n - 1))
幂级数的极限:
  S(n) = F(n) * m / (m - 1) (n 趋向无限大, m >> 2)
结论:
  F(n) < S(n) < m / (m - 1) * F(n)

  由此我们可得: 迭代深搜和一般深搜相比, 其性能多消耗的那部分, 基本可忽略.

自动适配:
  对于案例一, 一种解决思路是: 引入一个带超时的搜索接口. 这样对于任何硬件(CPU, 内存)条件, 既能充分利用资源达到最好的效果, 又能保证时间适度的用户体验.
  而我们在阐述迭代搜索的原理过程, 实际上提供了很好的思路去解决这个问题.
  对于带超时的搜索方式, 原本的迭代加深代码框架, 已能完成任务. 但其超时判断有些延后, 我们可以再做修改, 来精确控制超时.
  (1). 超时判断添加至函数negamax开头, 即深入到每个搜索节点上
  (2). 只要搜索节点判断超时, 就立即跳跃回调用顶层, 并宣告该深度搜索失败

bool demo(S) {
  // 判断超时, 若超时返回 false
  if ( timeout() ) {
    return false;
  }
  // 任务拆分/过程递进
  for ( successor S' in S ) {
    // 若遇到子调用汇报超时, 则立马返回
    if ( !demo(S') ) {
      return false;
    }
    // 正常业务处理
  }
  // 执行成功, 没有超时
  return true;
}

  该代码框架, 演示了如何立即返回调用顶层的技巧.
  (3). 带超时的迭代搜索, 选择没有超时的最深高度求解的决策步, 作为最终的决策步.

模糊化:
  一般的游戏AI并没学习模型, 用于反馈增强. 对于案例二的问题, 模糊化势在必行.
  这边简单谈谈几种可行的方式:
  (1). 引入多套评估函数, 每次随机选择一种
  (2). 引入抖动, 在设置权重向量时, 可以随机微调个别因素权重值
  (3). 在决策过程中, 偶尔按概率选择 次优, 次次优, 甚至其他可行步
  这些都是避免游戏AI固定化的一种思路.

总结:
  本文讲解了对应案例一, 案例二的合理解决方案. 游戏AI要落地, 需要对算法本身做一些润色工作.

写在最后:
  
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

  

对弈类游戏的人工智能(4)--游戏AI的落地的更多相关文章

  1. 对弈类游戏的人工智能(5)--2048游戏AI的解读

    前言: 闲得没事, 网上搜"游戏AI", 看到一篇<<2048游戏的最佳算法是?来看看AI版作者的回答>>的文章. 而这篇文章刚好和之前讲的对弈类游戏AI对 ...

  2. 一款类似塔防类的保卫羊村游戏android源码

    一款类似塔防类的保卫羊村游戏源码,这个游戏很像我们平时玩的塔防游戏的,游戏的源码比较完整的,大家可以修改一下或者添加一些广告就可以上线到应用商店了,而且这个游戏目前已经上线国内的一些应用商店了,360 ...

  3. 为什么我们要让人工智能玩游戏:微软Project AIX

    <我的世界>游戏 2016年7月注:Project AIX已正式更名为Project Malmo 注:本文编译自Project AIX: Using Minecraft to build ...

  4. 使用cocos2d 2.1制作一条河游戏(4): 主要的游戏逻辑BaseLayer设计

    前段时间一直忙着.没有时间更新博客.今天,仍然需要一段时间才能实现对游戏的一小部分,最后打动他. BaseLayer.h: #import <GameKit/GameKit.h> #imp ...

  5. 2017人工智能元年,AI在喧嚣和质疑中一路走来

    前百度首席科学家吴恩达说:就像100年前的电力.20年前的互联网一样,AI也会改变每一个产业! 有人说,现在就像1995年,那一年,第一家互联网公司--网景上市,一天之内大涨208%,互联网正式登上历 ...

  6. 【读书笔记《Android游戏编程之从零开始》】20.游戏开发基础(游戏数据存储)

    对于数据的存储,Android 提供了4种保存方式. (1)SharedPreference 此方法适用于简单数据的保持,文如其名,属于配置性质的保存,不适合比较大的情况,默认存放在手机内存里 (2) ...

  7. 【读书笔记《Android游戏编程之从零开始》】19.游戏开发基础(游戏音乐与音效)

    在一款游戏中,除了华丽的界面 UI 直接吸引玩家外,另外重要的就是游戏的背景音乐与音效:合适的背景音乐以及精彩的音效搭配会令整个游戏上升一个档次. 在 Android 中.常用于播放游戏背景音乐的类是 ...

  8. 地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了

    地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了 四叉树对于区域查询,效率比较高. 原理图

  9. 丢沙包游戏(或杀人游戏)的C语言实现

    丢沙包游戏(或杀人游戏)用C语言实现: 游戏简述: 杀人游戏(或者丢沙包游戏),设定一些人(人数为:num)一起玩游戏,从某个指定的人(设定为:start)开始轮流扔沙包,扔沙包人的下一个人为1,每隔 ...

随机推荐

  1. 关于Youtube URL的十个技巧

    你一定很熟悉Youtube了,知道它是一个视频分享网站.是的,youtube目前十分流行,你也许会常常访问.这里有一些关于youtube url的技巧,了解了这些技巧,你就可以更好的利用youtube ...

  2. Sublime Text的使用代码块安装的模块

    在众多的开发工具IDE当中.作者现在唯独深爱sublime text(以下简称st).以前做后台开发使用visual studio(以下简称vs),以及实行前后端分工也是配合后台使用vs.这里要讲述两 ...

  3. Http 状态码对照表

    1xx 消息 1. 100 Continue       2. 101 Switching Protocol       3. 102 Processing 2xx 成功 1. 200 OK      ...

  4. 5----table类型

    table类型是非常重要的Lua数据类型,也是Lua唯一能描述数据结构的类型 table类型可以很灵活的描述多种数据结构,其本身是基于键值对的形式存储数据的 字典结构 字典结构的table 的两种创建 ...

  5. javascript Demo

    var vm=(function(){ var name="jasper"; var changename=function(v){ name=v; }; return { nam ...

  6. python成长之路【第二篇】:列表和元组

    1.数据结构数据结构是通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构.在Python中,最基本的数据结构是序列(sequence) ...

  7. php总结 --- 4. 对象

    一. 恩聪 设计模式 因为php本身的问题,所以他能做的模式有限,就在这边列出了

  8. mysql 命令(一)

    1. 函数向日期添加指定的时间间隔 DATE_ADD(date,INTERVAL expr type)eg:DATE_ADD(CURDATE(),INTERVAL 1 MONTH) //在当前时间加一 ...

  9. 查看本机的IP地址方法:

    查看本机的IP地址方法:对于XP系统:方法一:如果右下角系统托盘区有本地连接的小电脑,双击小电脑→支持,就可以看到本机IP地址.无线连接也是一样.方法二:开始→运行cmd /k ipconfig,IP ...

  10. Android权限声明

    1.网络 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/ > < ...