本文内容包含以下章节:

Chapter 2 AI Methods

Chapter 2.1 General Notes

本书英文版: Artificial Intelligence and Games - A Springer Textbook

这个章节主要讨论了在游戏中经常用到的一些基础的人工智能算法。这些算法大部分都出现在一些人工智能和机器学习的入门书籍中。在讲解算法在游戏中的应用的时候,会以吃豆人(Ms Pac-Man)作为样例,讲解怎么用行为树算法,树搜索算法,监督学习算法,无监督学习算法,强化学习算法和进化算法来构建一个玩游戏的AI。

吃豆人

 

一. AI算法的基本要素

这些AI算法虽然形态各有不同,但是本质上都是基于两个基本的要素来做文章。一个是算法的表示(Representation), 另外一个是效用(Utility)。

首先是怎么把学到的玩游戏的知识用某种数据结构来表示出来。这个数据结构也是和要用的算法强相关的。比如如果用文法演化算法(Grammatical Evolution),最后学到的就是一些文法(Grammars);如果用概率模型或者有限状态机,最后知识就表示成一个图(Graphs);而行为树和决策树还有遗传算法会学到一颗树(Trees),神经网络自然是联结主义的(Connectionism), 遗传算法和演化策略会带来一些基因编码(Genetic Representation), 而TD-learning 和 Q-learning则学到了一下状态转移的表格(Tabular)。

当然,寻找一个最优的算法表示通常是很难的,并且世界上没有免费的午餐,算法之间总是各有利弊的。不过一般来说,我们希望选择的算法的表示尽可能的简单,和占用更小的空间。而我们需要有一些先验知识来找到一个较好的算法表示。

另外一方面,我们会用效用(Utility)这个来自博弈论的术语来指导算法的训练。不严格的来说,可以把它看做一个函数,输入是当前状态和算法可能的动作(Action),输出是算法做出某个动作所能够获得的好处。理论上来说,如果我们能得到一个准备的Utility Function, 我们的算法每次都可以找到最优的路径。但实际上,我们只能够得到一个估计值,或者更确切的,在没有先验知识的情况下,我们只能通过记住我们探索过的状态和路径及其获得的回报来估算一个效用值来表示某个走法的好坏(Measure of Goodness)。如果游戏本身的状态空间比较小,我们可以通过遍历所有的可能情况来得到一个准备的Utility Function。而通常我们面对的问题都有着极大的搜索空间,因此我们希望能够尽可能的探索到更多的路径,然后在探索到的数据上进行采样来得到一个估计的Utility。

Utility 在不同的算法上的叫法会有所不同,在含义上也有细微的差别。例如在一些树搜索算法上,我们会用启发式的规则(Heuristic)来指导算法的收敛。而在遗传算法上,它又被叫做适应度函数(Fitting Function)。在优化算法上,我们更常用的词语是Loss, Error, Cost; 而在强化学习,Reward是一个更常用的单词,这里最主要的原因是:做RL的人由于整天面对着逆天难的问题,所以喜欢用reward(相对于loss)来激励自己。(大雾)

这样,我们训练AI的过程就是寻找一套Representation最好的参数,可以最大化Utility。因此,能够学到一个好用的模型,取决于Utility Function是否设计的合理,和我们的目标是否完全一致。对于监督学习来说,Utility等价于其Label;对于强化学习来说,Utility来自于环境的反馈。而无监督学习的Utility则来自于数据本身的结构和共性。

二. 基于有限状态机的AI实现

实现一个NPC,最简单的方法当时就是写一些规则,但这样子显然比较low,不过在很多场景下其实也能满足需要了。如果我们把规则(If, else 语句)抽象,就变成了有限状态机(Finite State Machine, FSM) 或者 行为树(Behavior Trees, BT)。

FSM直到21世纪的前十年都还是广泛的应用到各种游戏之中。我们可以把FSM理解为一个图(Graphs), 游戏中的状态是图的一个节点(Nodes), 可以相互转化的状态之间有连线(Edges),连线之间定义了状态转移(Transitions)的条件,而在每个状态中,定义了一系列的动作(Actions),当AI处于该状态时,就执行具体的动作,如向左或向右或者更复杂的组合动作。

一个FSM的吃豆人AI, 定义了3个状态:躲避Ghosts, 追逐Ghosts和寻找豆子以及状态之间的转移条件

例如上面的一个基于FSM的吃豆人AI,首先定义了状态和状态转移的条件。当在寻找豆子的状态的时候,可以给AI编程具体在每个状态的行为。比如在寻找豆子的状态,一开始随机游走,如果看到豆子就去吃掉它,如果看到Ghost,就进入到躲避Ghost的状态。下面是一个简单的3种状态下动作的伪代码:

def seek_pellet: while 1:
   if ghost_in_sight:
     return evade_ghost_state
   if power_pill_eaten:
     return chase_ghost_state
   if pellet_in_sight:
     go_and_eat_pellet() #using pathfinding algorithm find best action
   else:
     move_randomly()def evade_ghost:
 while 1:
   if not ghost_in_sight:
     return seek_pellet_state
   if power_pill_eaten:
     return chase_ghost_state
   leave_the_ghost() # using tree search to find best actiondef chase_ghost:
 while 1:
   if power_pill_expired:
     return seek_pellet_state
   find_the_ghost() # using tree search to find best action

三. 基于行为树的AI实现

可以看到FSM的AI的模式是非常固定的,玩家很容易发现其中的pattern,这个通过模糊逻辑(Fuzzy Logic)和增加概率可以得到一定缓解。另外,对于一些要完成比较难的任务的NPC,需要为其设计很多不同的状态和状态转移方式,整个过程是非常复杂和难以调试的。因此人们定义了行为树(BT),通过模块化(Modularity)的设计,可以把复杂的行为拆解成简单的任务,从而减轻整个系统的复杂度和提高可维护性。因此,自光晕2(Halo 2)之后,BT就取代了FSM,成为游戏工业界最常用的NPC算法。

行为树是把FSM的图转变成为一颗树结构。因此行为树是有一个Root节点,然后往下有一些中间节点,最后是叶子节点。我们从根节点遍历行为树,每一个子节点被执行的时候都按预设的时间间隔回传三种信息给到父节点:

  1. Run: 表示这个节点还在继续执行

  2. Success:表示这个节点已经成功执行了

  3. Failure: 表示这个节点执行失败了

而行为树的节点有3种类型:

  1. Sequence: (如上图的蓝色方块)表示该父节点会顺序执行它的子节点,并且知道它的所有子节点都成功执行了,它才会回传Success给更上层的节点。

  2. Selector:(如上图的红色方块)表示该父节点会从其子节点中选择其中一个执行,只要有一个子节点执行成功,该父节点就会返回Success。除非所有子节点都执行失败,该父节点才会返回失败。父节点选择子节点的顺序有两种方式:a.
    Probability:按概率选取子节点执行的顺序;b. Priority: 该预设的顺序选取子节点。

  3. Decorator:(如上图紫色的方块)相当于节点执行增加一些条件,比如限定执行的时间,或者失败重新执行的次数。如图中是限定了一个条件,只有在看到Ghost的时候,吃豆子(Eat Next Pellet)这个节点才会返回Fail 状态。

可以看到,行为树的结构可以比较方便的把复杂的行为分解成层次的简单结构,方便维护。例如我可以在吃豆子(Eat
Next
Pellet)这个Node上设定新的很复杂的算法,但不会影响整个树的其他节点。同时测试起来也比较方便,我们可以针对行为树的某个子树来进行测试,而不影响整个大的框架。

当然,行为树和FSM也有同样的问题,就是NPC的行为的可预见性还是比较大的,整个行为的模式还是受限于整体的行为树框架。虽然可以通过一些概率的方式来增加一些随机性,但整体来看还是有很多局限性的。

在行为树里面,选择不同子树的方式还是稍显简单,通过一定的规则,或者预设的概率来选择不同的子节点(子树)来执行。因此人们在上面添加了基于效用的方式(Utility-based)。简单说来,我们定义一个Utility
Function: u = F(S,a)。 根据当前游戏的环境状态S, 得到某一个行为a的效用值u。这个Utility
Function是可以通过规则设定,也可以通过一些复杂的学习方法来得到。比如我们可以用一个神经网络去预测在当前状态下,做哪个动作更好。比如在吃豆人游戏中,可能我们就不需要来写一些规则来判断该做什么动作(比如看到Ghost就停止吃豆子的子树执行),而可以用更动态的方式来控制(比如Ghost在多远的地方,往哪个方向走,豆子和Ghost和NPC的位置关系怎么样)是否停止吃豆子的子树执行。当然更General的说,其实后续的强化学习,监督学习也好,都是在学一个Utility
Function来控制NPC的动作。本质上,Utility-based AI是一种构建NPC的思想,可以应用到不同的AI方法上。

更多推荐

游戏人工智能 读书笔记 (一)前言与介绍

游戏人工智能 读书笔记 (二) 游戏人工智能简史

游戏人工智能 读书笔记 (三) 游戏和人工智能的相互影响


“深度兼容测试”现已对外,腾讯专家为您定制自动化测试脚本,覆盖应用核心场景,对上百款主流机型进行适配兼容测试,提供详细测试报告。

点击:https://wetest.qq.com/cloud/deepcompatibilitytesting 了解更多详情。


如果使用当中有任何疑问,欢迎联系腾讯WeTest企业QQ:2852350015

游戏人工智能 读书笔记 (四) AI算法简介——Ad-Hoc 行为编程的更多相关文章

  1. 【转】Verilog HDL常用建模方式——《Verilog与数字ASIC设计基础》读书笔记(四)

    Verilog HDL常用建模方式——<Verilog与数字ASIC设计基础>读书笔记(四) Verilog HDL的基本功能之一是描述可综合的硬件逻辑电路.所谓综合(Synthesis) ...

  2. 《MFC游戏开发》笔记四 键盘响应和鼠标响应:让人物动起来

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9327377 作者:七十一雾央 新浪微博:http:// ...

  3. 【转载】MDX Step by Step 读书笔记(四) - Working with Sets (使用集合)

    1. Set  - 元组的集合,在 Set 中的元组用逗号分开,Set 以花括号括起来,例如: { ([Product].[Category].[Accessories]), ([Product].[ ...

  4. how tomcat works 读书笔记四 tomcat的默认连接器

    事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足下面几个要求 1 实现org.apache ...

  5. 《实战Java高并发程序设计》读书笔记四

    第四章 锁的优化及注意事项 1.锁性能的几点建议 减小锁持有时间: 系统持有锁时间越长锁竞争程度就越激烈,只对需要同步的方法加锁,可以减小锁持有时间进而提高锁性能. 减少锁的持有时间有助于降低锁冲突的 ...

  6. JavaScript DOM编程艺术读书笔记(四)

    第十章 实现动画效果 var repeat = "moveElement('"+elementID+"',"+final_x+","+fin ...

  7. 深入理解Java虚拟机之读书笔记四 性能监控与故障处理工具

    JDK的bin目录下存在很多有效的命令行工具,它们就是jdk\lib\toos.jar类库的封装. 一.jps:虚拟机进程状况工具,查询出LVMID. 二.jstat:虚拟机统计信息监视工具, 三.j ...

  8. Machine Learning for hackers读书笔记(四)排序:智能收件箱

    #数据集来源http://spamassassin.apache.org/publiccorpus/ #加载数据 library(tm)library(ggplot2)data.path<-'F ...

  9. 《鸟哥的Linux私房菜》读书笔记四

    1.Linux的目录配置以『树状目录』来配置,至於磁碟分割槽(partition)则需要与树状目录相配合! 请问,在预设的情况下,在安装的时候系统会要求你一定要分割出来的两个Partition为何? ...

随机推荐

  1. 面试准备——(三)Selenium面试题总结

    一.Selenium基本知识 1. 什么是Selenium? Selenium是浏览器自动化工具,主要用来Web的自动化测试,以及基于Web的任务管理自动化.它支持的语言有:python.Java.r ...

  2. 记录 iOS 各种跳转到系统应用

    MARK ----拨打电话 NSString* phoneVersion = [[UIDevice currentDevice] systemVersion]; if (phoneVersion.fl ...

  3. 使用Redis存取数据+数据库存取(spring+java)

    RoleMapper接口: package com.wbg.springRedis.dao; import com.wbg.springRedis.entity.Role; import org.sp ...

  4. sizeof笔试题--转

    转自http://blog.csdn.net/yanyaohua0314/archive/2007/09/17/1787749.aspx sizeof笔试题 http://www.xici.net/b ...

  5. DIAView组态软件笔记

    1.为了节省成本,我们往往会在PLC将多个开关量整合到同一个word中,这样关联的变量可以从原有的16个变成现在的一个.这样做带来的麻烦就是需要我们在脚本中自己来解析出数据,通过对2求余(mod 2) ...

  6. python3爬虫编码问题

    使用爬虫爬取网页经常遇到各种编码问题,因此产生乱码今天折腾了一天,全部总结一遍环境:win10,pycharm,python3.41.首先先来网页编码是utf-8的:以百度首页为例:使用request ...

  7. ARM对异常的处理

    所谓中断就是中断SoC的CPU核(异常可以引起CPU的中断) ARM对异常的处理 1.初始化: 1)设置中断源让它可以产生中断.如某个按键可以产生中断,那么可以设置它的GPIO引脚为中断引脚: 2)设 ...

  8. 09 OCP知识点讲解 之 LRU链与脏LRU链

    OCP知识点讲解 之 LRU链与脏LRU链 分类: Oracle 2012-06-30 10:49:26   一.LRU链: 任何缓存的大小都是有限制的,并且总不如被缓存的数据多.就像Buffer c ...

  9. Unity3D Errors And Fix

    Author Error: Shader warning in 'Custom/ShowAnimation': Not enough temporary registers, needs 9 (com ...

  10. AtomicReference 原子引用

    AtomicReference和AtomicInteger非常类似,不同之处就在于AtomicInteger是对整数的封装,底层采用的是compareAndSwapInt实现CAS,比较的是数值是否相 ...