— AI分享站

Archive
Tag "分数系统"

接第一篇的话题,这次来说说AI bug不可重现的问题。确实,对于AI调试来说,很多bug(或者说行为异常),很难找到一个切实的重现的方法,经常是看到后,加好断点,想刻意再玩一下却很难再玩出来了。所以,如何抓住现场,是在AI调试中的一个很值得去考虑问题。

我们很自然的会想到一个解决方案,那就是“回放”。当看到问题时,马上“录像”,然后把这个场景再回放一遍,甚至是可以回放任意遍。这确实是一个自然而完美的方法。一般来说,游戏中的回放分两种:

  1. 逻辑回放(logical playback)
  2. 结果回放(result playback)/ 画面回放(screen playback)

逻辑回放是指,能回到过去的任意时刻,运行游戏的整个逻辑,保证相同的输出结果。结果回放(画面回放)是指,能回到过去的任意时刻,将逻辑运行的结果重新显示出来。简单来说,逻辑回放中所记录的游戏的数据是输入,而结果回放(画面回放)记录的数据是输出,见下图:

ai_debug_1

这两种的回放的实现都依赖于引擎的结构,和引擎实现的耦合度很高,很难做到独立和通用的模块,对于AI调试来说,显然逻辑回放是最好的,因为它能让我们再运行一次游戏逻辑,这样就可以通过设置断点来调试了,但是,现在的引擎一般都是多线程引擎,既然是多线程,就存在一定的时序问题,要做到同样的输入和上下文,两次结果完全一致会相当的困难,除非引擎从设计之初就考虑逻辑回放的问题,如果是改造这样的多线程引擎,工作量会比较大。如果是单线程的引擎,实现起来会容易很多,一般而言,逻辑回放主要记录几个内容:

  1. 时间信息
  2. 设备输入信息(比如,手柄,键盘,鼠标等)
  3. 随机数信息
  4. 当时的游戏世界上下文

对于第4点是针对从任意点回放而言的,如果当前的上下文信息不是很容易获取,可以考虑不支持任意点回放,而是每次都从头开始回放,这样实现起来更容易一点。

就如我前面所说,对于多线程引擎来说,逻辑回放的实现比较困难,那退而求其次,我们可以选择结果回放,结果回放记录的信息就比较“单纯”,就是每次画面需要画的那些Object信息,比如位置等等,回放时,我们只需把记录的结果再重新给渲染器,让它画出来即可。也许大家会问,我们没法重新调试AI的逻辑,这样的回放有什么意义呢?当然,如果只是单纯的把画面重画一遍,是没什么意义,结果回放需要结合另一个AI调试的方法一起来使用。那就是“调试信息”(Debug Draw)。一般引擎都提供一套可以在屏幕上画点圈,画点叉,写点字的接口,AI的调试,就是可以使用这样的接口在屏幕上打印出关键的信息,来帮助我们查看逻辑的“走向”,举个例子来说,我以前写过一篇博文,介绍用分数系统来做AI,如果要调试这样的AI,我们就可以利用调试信息,在屏幕上,将分数的情况都打印出来。由于结果回放是将所有输出到渲染器的信息都保存了下来,所以我们就可以通过回放来观察这些分数的变化,以此来调试AI的行为。结果回放的另一个优势是,它可以轻松的实现任意点的回放,包括后退,前进,暂停等等,因为结果数据和上下文是无关。

不管有没有回放的机制,很多时候AI的调试,都需要调试信息的帮助,可以让我们不用设置断点就知道逻辑的计算,我们甚至需要制作一些工具来获取当前AI的相关状态,比如前几篇说到的那个观察器。这也就是AI调试比较难的地方,一个字,“猜”,当然是有依据的猜,更准确的说,应该是推测吧, :)

不知不觉,写了3篇了,总结下吧,要有好的AI调试体验,我们需要有好的AI架构,这是一切的基础,比如脚本,比如回放,其次要有好的配套工具,来辅助那个“猜”,并能形成独立可复用的模块,然后就是对于标准的遵守,比如不要用“魔数”的问题,当然,还有很多值得我们去想的,这一系列也希望能抛砖引玉,引起大家的思考吧。

相关:

---> 关于调试AI的闲话(1)

---> 关于调试AI的闲话(2)

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

接上篇,我们接着聊。

分数系统的打分公式,是根据情况自己定义的,但也有一些方式来帮助构建出比较合理的公式来,公式的推导就是一个函数的拟合过程,我们可以先取几个样点,然后试着找到一条函数曲线,尽可能的去使样点落在我们的这条函数曲线上。在中学的时候的实验课上,大家应该都做过这种事情。在AI中,我们的打分公式,一般不会很复杂,可能是简单的直线,或者分段的折线,有时也会是二次,或三次的曲线。有了这样一个基本曲线,接着再用测试样例去修正,一般就可以得到一个比较符合需求的打分公式了。

在分数系统中,如果有多个因素需要考量,比如我们上个例子中的EC和HP,这就引出了分数如何组合的问题。就像我们先前对Retreat和Shoot的打分方式。常用的做法是,针对每个因素单独引入打分公式,并且归到[min, max]的分数区间中,然后再对所有的得分组合后,得到一个最终得分。对每个因素单独打分的好处是,我们可以跟专注于单个因素的影响,方便我们构建打分公式,并且可以方便的移除和添加新的因素,而且也能更好的复用我们的打分公式。分数的组合一般可以有两种:

1. 加权平均,s = (k1*s1 + k2 * s2 + ... + kn * sn)/(k1 + k2 + ... + kn)
2. 因子连乘,s = s1 * s2 * ... * sn ( 0 <= si <= 1.0 )

这两种方式有各自的适用范围,第一种方式,我们除了对打分公式要细心调整外,还需要对加权因子加以调整。第二种方式必须保证每个因子被归到[0,1]之间,因为用到了乘法的原因,所以体现了一种并且的关系,一旦有一个因子为0,则整个分数就会是0。

分数系统是一种模糊逻辑,他不像基于规则的系统那样逻辑非常清楚,所以,在分数系统中,有时会出现一种“意外点”,但对AI来说,出现一些意外点,也不是不可接受,有时这样也更显得AI很真实,AI也会犯错的嘛。一旦使用了分数系统,debug的过程就变成了不断调整公式和数值的过程。如果设计的好的话,AI程序员可以提供出一套调节分数的接口,这样design也可以参与其中了。

当然,有时分数系统是会和规则系统联合使用的,用来处理一些意外情况。我常觉得,在AI中,规则系统就是用来修bug的。

好了,就聊到这儿,欢迎大家留言

相关:

---> 基于分数系统(Scoring System)的AI设计(1)

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More

最近在做的一个AI用到了分数系统,这次就来和大家聊聊这个话题。

先来设计一个场景,有一个单兵作战的士兵,在丛林中,他有一些基本的行为,比如射击(shoot),移动(move),撤退(retreat)。他必须结合很多因素来决定自己的行为,比如,敌人的数量,自身的情况,弹瑞脑消金兽药的情况,环境的情况等等,我们现在就来为这个士兵写AI来模拟他的行为。

AI常常用到一些if-else的逻辑判定(这种方式,在AI中叫基于规则的AI,Rule-Based System),对一些简单并且确定的逻辑,这种方式十分的便捷有效。但在上面这个场景中,如果单纯的用if-else,会使代码很快变的杂乱和难以维护,我们可以试着写一下,假设我们需要考虑以下两个个要素:

1. 敌人的数量(Enemy Count,EC)
2. 自己的负伤程度(Hit point,HP,用0~100来表示)

我们定义以下规则

规则1. 如果 EC = 0,那么 Move
规则2. 如果 HP < 30,那么 Retreat
规则3. 如果 EC > 4,那么Retreat
规则4. 如果 0< EC <= 2 并且 HP >= 30 那么 Shoot
规则5. 如果 2< EC <= 4 并且 HP > 70 那么 Shoot
规则6. 如果 2< EC <= 4 并且 HP <= 70 那么 Retreat

这些规则可以转换成相对应的if-else,但这样一个简单的情形(两个条件,三个结果),我们就需要6条规则来描述,如果我们需要其他一些考虑因素,比如,弹瑞脑消金兽药,环境,那用这样的结构就很难维护了。对这种多因素判定的情况,我们就可以尝试用分数系统来做。分数系统的一个基本思想,是为每个单独的行为打分,根据分数的高低来决定做哪个行为,而所有的因素就是打分的依据。上面这个场景中,我们有三个独立行为,在每次的AI选择中,我们来为这些行为打分,下面我们来试着把这个士兵的AI用分数系统来做:

假设我们的分数是从0~100,Clamp(v, l, h)是指如果v<l,则v=l,或者如果v>h,则v=h,Max值取两者较大的那个值

1. Move:   s = Clamp(100*(1 - EC), 0, 100)
2. Retreat:s1 = Clamp(100 - HP, 0, 100)
s2 = Clamp(25 * EC, 0, 100)
s = (s1 + s2)/2
3. Shoot: s1 = Clamp(HP, 0, 100)
s2 = Clamp(100 - 25 * Max(EC, 1), 0, 100)
s = (s1 + s2)/2

我们来做一些测试:

1. HP = 60, EC = 2
Move(0), Retreat(45), Shoot(55) --> Shoot
2. HP = 20, EC = 1
Move(0), Retreat(52.5), Shoot(47.5) --> Retreat
3. HP = 100,EC = 5
Move(0), Retreat(50), Shoot(50) --> Shoot or Retreat
4. HP = 70, EC = 0
Move(100), Retreat(15), Shoot(72.5) -->Move

有上面的测试可以看出,大部分还是能符合我们预先定义的一些规则……

(待续)

ps: 本来想一下写完了,可是太晚了,最近身体不是很好,要早点休息了,明天再补完

————————————————————————
作者:Finney
Blog:AI分享站(http://www.aisharing.com/)
Email:finneytang@gmail.com
本文欢迎转载和引用,请保留本说明并注明出处
————————————————————————

Read More