对弈类游戏的人工智能(4)--游戏AI的落地
前言:
对弈类游戏的智能算法, 网上资料颇多, 大同小异. 然而书上得来终觉浅, 绝知此事要躬行. 结合了自己的工程实践, 简单汇总整理下. 一方面是对当年的经典<<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的落地的更多相关文章
- 对弈类游戏的人工智能(5)--2048游戏AI的解读
前言: 闲得没事, 网上搜"游戏AI", 看到一篇<<2048游戏的最佳算法是?来看看AI版作者的回答>>的文章. 而这篇文章刚好和之前讲的对弈类游戏AI对 ...
- 一款类似塔防类的保卫羊村游戏android源码
一款类似塔防类的保卫羊村游戏源码,这个游戏很像我们平时玩的塔防游戏的,游戏的源码比较完整的,大家可以修改一下或者添加一些广告就可以上线到应用商店了,而且这个游戏目前已经上线国内的一些应用商店了,360 ...
- 为什么我们要让人工智能玩游戏:微软Project AIX
<我的世界>游戏 2016年7月注:Project AIX已正式更名为Project Malmo 注:本文编译自Project AIX: Using Minecraft to build ...
- 使用cocos2d 2.1制作一条河游戏(4): 主要的游戏逻辑BaseLayer设计
前段时间一直忙着.没有时间更新博客.今天,仍然需要一段时间才能实现对游戏的一小部分,最后打动他. BaseLayer.h: #import <GameKit/GameKit.h> #imp ...
- 2017人工智能元年,AI在喧嚣和质疑中一路走来
前百度首席科学家吴恩达说:就像100年前的电力.20年前的互联网一样,AI也会改变每一个产业! 有人说,现在就像1995年,那一年,第一家互联网公司--网景上市,一天之内大涨208%,互联网正式登上历 ...
- 【读书笔记《Android游戏编程之从零开始》】20.游戏开发基础(游戏数据存储)
对于数据的存储,Android 提供了4种保存方式. (1)SharedPreference 此方法适用于简单数据的保持,文如其名,属于配置性质的保存,不适合比较大的情况,默认存放在手机内存里 (2) ...
- 【读书笔记《Android游戏编程之从零开始》】19.游戏开发基础(游戏音乐与音效)
在一款游戏中,除了华丽的界面 UI 直接吸引玩家外,另外重要的就是游戏的背景音乐与音效:合适的背景音乐以及精彩的音效搭配会令整个游戏上升一个档次. 在 Android 中.常用于播放游戏背景音乐的类是 ...
- 地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了
地图四叉树一般用在GIS中,在游戏寻路中2D游戏中一般用2维数组就够了 四叉树对于区域查询,效率比较高. 原理图
- 丢沙包游戏(或杀人游戏)的C语言实现
丢沙包游戏(或杀人游戏)用C语言实现: 游戏简述: 杀人游戏(或者丢沙包游戏),设定一些人(人数为:num)一起玩游戏,从某个指定的人(设定为:start)开始轮流扔沙包,扔沙包人的下一个人为1,每隔 ...
随机推荐
- ubuntu删除软件命令
第一步,apt-get remove xxx :就是卸载xxx 或者 apt-get remove --purge xxx :卸载xxx并清除配置. 这两条命令对于依赖则是不管的.因为别的软件可 ...
- angular之控制器(0)
一.控制器的含义 在angularJS中,controlle是一个javascript函数/类,用于操作作用域中,各个对象的初始状态以及相应的行为 二.控制器的作用 1. 控制 AngularJS 应 ...
- \boot 空间不足解决方法
ubuntu系统总是更新,有时是内核,有时是软件,最近的一次更新download中,提示\boot目录空间不足,我是将\boot单独划分在一个分区中的,当该目录空间不足时,可以利用命令删除没有用的镜像 ...
- C#:类和结构的区别
第一.引用类型和值类型 类属于引用类型,而结构属于值类型. 结构在赋值时进行复制. 将结构赋值给新变量时,将复制所有数据,并且对新副本所做的任何修改不会更改原始副本的数据. 第二.继承性 类可以继承类 ...
- Scrum Meeting 7-20151209
任务安排 姓名 今日任务 明日任务 困难 董元财 服务器购买记录接口 请假(编译攻坚) 无 胡亚坤 发布记录和购买记录 请假(编译攻坚) 无 刘猛 完成Scrum Meeting 请假(编译攻坚) 无 ...
- == 区别 equals
==操作符专门用来比较值是否相等 int a=10; int b=10; 则 a==b, 返回 true. 但是, String a = new String("foo"); St ...
- hdu2243考研路茫茫——单词情结(ac+二分矩阵)
链接 跟2778差不多,解决了那道题这道也不成问题如果做过基本的矩阵问题. 数比较大,需要用unsigned longlong 就不需要mod了 溢出就相当于取余 #include <iostr ...
- Html_Img元素 设置图片与其他元素横排高度一致
<img id="numAdd" src="~/Images/jia.jpg" style="width:30px;height:30px;ve ...
- Linux vi/vim
所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正 ...
- angularjs简述
1.MVC设计模式 MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式 Model(模型)表示应用程序核心(比如数据库记录列表 ...