一.追逐与闪躲
Bresenham算法的原理是,计算每一点与终点之间的的横轴与纵轴,然后比较两轴的长度,哪个轴比较长,就往该方向前进,如果两轴等长,则往斜边前进。
if (deltaCol > deltaRow)
{ fraction = deltaRow * 2 - deltaCol;
while (nextCol != endCol)
{ if (fraction >= 0)
{ nextRow += stepRow;
fraction -= deltaCol;
}
nextCol += stepCol;
fraction += deltaRow;
pathRow[currentStep] = nextRow;
pathCol[currentStep] = nextCol;
currentStep++;
}}
else
{ fraction = deltaCol * 2 - deltaRow;
while (nextRow != endRow)
{ if (fraction >= 0)
{ nextCol += stepCol;
fraction -= deltaRow;
}
nextRow += stepRow;
fraction += deltaCol;
pathRow[currentStep] = nextRow;
pathCol[currentStep] = nextCol;
currentStep++;
}}}
连续环境中的视线算法:在函数中定义了4的局部变量。u和v是Vector类型的,Vector类是一个自定义类(见附录A),负责处理基本向量算术,比如向量加法,减法,数量积,交叉乘积,和其他操作。另两个局部变量是一对布尔变量,left和right。它们是用来分析哪个操纵力启动,两个初始都是false。定义局部变量的下一行是计算从掠夺者到被掠夺者的视线。实际上,这条线不仅仅是计算线的位置。它还会计算宏观上掠夺者到被掠夺者之间相对位置的向量,通过使用代码 (Prey.vPosition - Predator.vPosition) ,然后把结果向量传递给VRotate2D函数,把它转换成掠夺者本地,本体坐标。VRotate2D表现成一个标准的结合系统,把有关earth-fixed的系统转换成body-fixed对应系统方向(看下边的“全局与局部坐标系统”)。结果保存在u里,然后规格化u即(u.Normalize()),把它转换成单位长度向量。最后一行代码调用刚性体类的SetThrusters成员函数,在模拟循环的当前反复中为掠夺者提供转向力。
void DoLineOfSightChase(void)
{ Vector u, v;
bool left = false;
bool right = false;
u = VRotate2D(-Predator.fOrientation,
(Prey.vPosition - Predator.vPosition));
u.Normalize();
if (u.x < -_TOL)
left = true;
else if (u.x > _TOL)
right = true;
Predator.SetThrusters(left, right);
}
二.群聚规则
首先根据角度检测,对于不同的视野检测方法是不同的(比如,宽视野,窄视野),下面的例子是对于宽广视野的检测
·计算d=目标单位位置-当前单位位置,w为d以目标单位反方向的坐标系中的向量坐标。
·如果w.y>0,则目标单位必然在视野范围内。
·如果w.y<0,则要看x,y坐标构成的线段斜率,是否在设定的视野区域之外,如果fabs(w.x)>fab(w.y)*_BACK_VIEW_ANGLE_FACTOR,则目标单位在视野范围内。其中的_BACK_VIEW_ANGLE_FACTOR就是视野角度系数。该系数等于1时,视野弧线的直线与x轴夹角是45度,该系数越大,两条线越接近x轴,不可见区域越大,反之,系数越小,两条线越接近y轴,不可见区域越小。
视野检查
if (WideView) //宽广视野的检查
{InView=((w.y>0)||((w.y<0)&&(fabs(w.x) >fabs(w.y)*BACK_VIEW_ANGLE_FACTOR))) ;
RadiusFactor =_WIDEVIEW_RADIUS_FACTOR;
}
if(LimitedView) //有限视野的检查
{ InView=(w.y >0);
RadiusFactor =_LIMITEDVIEW_RADIUS_FACTOR;
}
if (NarrowView) // 狭窄视野的检查
{ InView (( (w.y >口) && (fabs(w.x) 0)) { // DoFlock=true:启用凝聚规则,N>0:邻近单位数量大于零
Pave = Pave / N; // 邻近单位的平均位置向量
v = Units[i].vVelocity; // 当前单位的速度向量
v.Normalize();
u = Pave-Units[i].vPosition; // 邻近单位平均位置向量与当前单位向量的差值,相对位置向量
u.Normalize();
w.VRotate2D(-Units[i].fOrientation, u);
if(w.x < 0) m = -1; // 相对位置向量(即u)在当前单位的右边,需要右转当前单位
if(w.x > 0) m = 1; // 相对位置向量(即u)在当前单位的左边,需要左转当前单位
if(fabs(v*u) < 1) // 确保反余弦函数可以正常运行
Fs.x += m * _STEERINGFORCE * acos(v * u) / pi;
// Fs.x使用的坐标系是应该也是前面刚刚转化过的坐标系
// acos(v*u):计算相对位置向量与当前单位的速度向量之间的夹角,除以pi是为了把弧度数值转化为标量
}
2.4 对齐
对齐考虑的是当前单位的速度向量v和邻近单位的平均速度向量u,其他部分与凝聚部分的代码都一样,很好理解。
if(DoFlock && (N>0)) {
Vave = Vave / N;
u = Vave; // 邻近单位的平均速度向量
u.Normalize();
v = Units[i].vVelocity;
v.Normalize();
w.VRotate2D(-Units[i].fOrientation, u);
if(w.x < 0) m = -1;
if(w.x > 0) m = 1;
if(fabs(v*u) < 1)
Fs.x += m * _STEERINGFORCE * acos(v * u) / pi;
}
2.5 分隔
前面的凝聚和对齐规则,都会尝试让单位相互靠近一点。使用的分别是(速度向量,位置向量)和(速度向量,速度向量)。
对于分隔,需要当前单位与邻近的每个单位分别比较,对于进入最小间隔的单位,算出一个力,加到Fs.x上去。
部分代码如下:
if(InView) { // 如果在视野内
if(d.Magnitude() <= Units[i].fLength * _SEPARATION_FACTOR) {
if(w.x < 0) m = 1; // 这里是分隔,方向与凝聚和对齐规则正好相反
if(w.x > 0) m = -1;
Fs.x += m * _STEERINGFOCE * (Units[i].fLength*_SEPARATION_FACTOR) / d.Magnitude(); // 分隔越小,力越大
}}
double magnitude(struct complex_struct z) { return sqrt(z.x * z.x + z.y * z.y); }
避开障碍物
替我们的单位安装虚拟触角(feeler)。基本上,这些触角会处在单位的前方,如果触角碰到某种东西,就是那些单位要转向的时候了。模型的形式很多,比如可以装上三个触角,分别位于三个不同方向,不但能检测出是否有障碍物,而且检测该障碍物位于单位的那一侧。运算向量a,这只是该单位和障碍物位置间的差值。接着,我们取a 和v 的内积,将a 投射到v 上,由此可得向量p 。把向量p减去向量a,可得向量b。现在,要测试v是否和障碍物的某处相交,我们得测试两种情况。首先,p的数值必须小于v的数值;其次,b 的数值必须小于该障碍物的半径r。如果两者都是如此,则需要校正转向力,否则,该单位可继续沿当前方向前进。
Vector a,p,b;
for (j=0; j<_NUM_OBSTACLES; j++)
{ u =Units[i].vVelocity;
u.Normalize();
v=u*_COLLISION_VISIBILITY_FACTOR * Units[i].fLength;
a=Obstacles[j] - Units[i].vPosition;
p = (a * u) * u;
b = p - a;
if((b.Magnitude()< _OBSTACLE_RADIUS)&&(p.Magnitude() 0) m =-1;
Fs.x += m * STEERINGFORCE *(_COLLISION_VISIBILITY_FACTOR*Units[i].fLength/a.Magnitude());
}}
三.势函数
Lenard-Jones势能代表的是,分子间吸引和排斥的势能。U代表的是原子内的势能,和分子的间隔距离r成反比。A和B是参数(可表示引力与斥力的强度),与m和n这两个指数一样(分子距离)。如果我们取该势函数的导数(derivative),就可得到一个代表某力的函数。这个力函数根据这两个分子的接近程度,产生引力和斥力,就我们的情况而言,分子指的就是游戏中正在行动的单位。通过调节参数,可以转化引力与斥力,这样就可以实现追逐和闪避。除了追逐和闪避之外,使用斥力进行障碍物躲避,使用引力形成群体等。
算法基础:
int j;
Vector Fs; // 总净转向力
Vector Pfs; // Fs 施加的位置
Vector r,u;
double U, A, B, n, m, d;
//群聚AI开始
Fs.x =Fs .y =Fs.z =0;
Pfs.x =0;
Pfs.y =Units[i].fLength/2.0f;
1. 追逐和闪避
例:有两架飞机,Craft1和Craft2,Craft1由玩家控制,Craft2由计算机控制。通过计算Craft2与Craft1的势能,得到Craft2的力,从而控制Craft2的移动方式。
void DoAttractCraft2(void) {
Vector r = Craft2.vPosition - Craft1.vPosition;
Vector u = r;
u.Normalize();
double U, A, B, n, m, d;
A = 2000; // 引力强度
B = 4000; // 斥力强度
n = 2; // 引力衰减
m = 3; // 斥力衰减
d = r.Magnitude() / Craft2.fLength; // 考虑到尺度伸缩的目的
U = -A/pow(d, n) + B/pow(d, m); // 这里实际上求出的是势能,后面就把这个势能当做力的大小了
Craft2.Fa = VRotate2D(-Craft2.fOrientation, U * u); // U*u给力一个方向,然后通过旋转坐标轴后,在把该力加到单位上
Craft2.Pa.x = 0; // 最后这几句没看懂
Craft2.Pa.y = Craft2.fLength / 2;
Target = Craft1.vPosition;
}
追逐基本算法:
if (Chase)
{ r=Units[i].vPosition - Units[0].vPosition;
u=r;
u.Normalize();
A =10000;
B =10000;
n =1;
m =2 ;
d =r.Magnitude()/Units[i].fLength;
U =-A/pow(d,n) + B/pow(d,m);
Fs +=VRotate2D(-Units[i].fOrientation,U*u);
}
·(A)中,追击者朝猎物前进,当猎物和他擦身而过时,他会绕回来。当追击者太接近时,会突然转一下,以维持两单位间的某些分隔距离,感觉像是跑的太快跑过了,又回头追。
·(B)中,我们减少引力分量的强度(把A的值减少一点),其结果就很像拦截,感觉像是跑的刚刚好,撞到了。
·(C)中,我们增强引力的强度,结果很像基本视线算法。增强到多少呢,比A中的还大?这个不得而知了就。
·(D)中,我们减少引力,增加斥力,并调整指数参数,结果计算机控制的单位就会逃离玩家。
2.避开障碍物
基本思路就是:把A参数设为0,只留下斥力分量,通过调整参数B,决定斥力强度,以及指数m,决定衰减程度。
举例来说,地图上有很多障碍物,计算机控制一个单位在地图上移动并且能够避开障碍物。在计算斥力的时候,需要把所有的单位对该单位产生的斥力都考虑进去,综合的结果,就是对该单位实际作用力。
if (Avoid)
{ for (j=0; j <_NUM_OBSTACLES; j++)
{ r=Units[i].vPosition - Obstacles[j];
u=r;
u.Normalize();
A =0;
B =10000;
n =2;
m =2.5;
d =r.Magnitude()/Units[i].fLength;
U =-A/pow(d,n) + B/pow(d,m);
Fs +=VRotate2D(-Units[i].fOrientation,U*u);
}}
3. 成群结队
使用势能函数实现群体行为,不需要基本群聚的规则,只需要计算任意两个单位之间的势能,从而得到每个单位身上的力就够了。
另外,如果让群体能够追猎物,躲避障碍物,那么就把追击和躲避产生的势能与群体势能加在一起就好了,当然,实现不同目的的势能函数的参数是不同的。
if(Swarm)
{ for (j=1; j <_MAX_NUM_UNITS; j++)
{ if(i!=j)
{ r=Units[i].vPosition - Units[j].vPosition;
u=r;
u.Normalize();
A =2000;
B =10000;
n =1;
m =2 ;
d =r.Magnitude()/Units[i].fLength;
U =-A/pow(d,n) + B/pow(d,m);
Fs +=VRotate2D(-Units[i].fOrientation,U*u);
}}}
四.寻路算法
1. 基本的路径寻找
简单追逐追逐方法和视线追逐方法。
1.1 随机移动避开障碍物
if(玩家在视线内) 采用直线路径走向玩家
else 以随机方向移动
1.2 绕行障碍物
方法说明:· 假设单位透过障碍物看到目的地,但是不能够越过障碍物到达目的地。
· 让单位朝着目的地使用直接走过去,当遇到障碍物了,就沿着障碍物的边缘走。
· 当障碍物与目的地之间没有障碍物的时候,再朝着障碍物走过去。
1、 直接绕行:
遇到障碍物便开始绕行,问题:何时停止绕行?
2、
改良后的绕行:
算出从开始绕行的点到所学抵达的目的地之前的线段。计算机控制的角色会一直在绕行状态,直到与该线相交,到了交点时,就会换回简单路径寻找状态。
3、 绕行并整合视线算法:
在沿路走的每一步,我们都会用视线算法,确认是否可采用直线的实现路径抵达目的地。即沿着障碍物边缘前进,每走一步,检查目的地是否在计算机控制角色的视线内,若是,就从绕行状态切换到视线路径寻找状态。
2. 以面包屑寻找路径
面包屑路径寻找方式:每次玩家走一步时,都会毫无所知地再游戏世界中留下看不见的标记或者面包屑。当游戏角色碰到面包屑时,就能凭着面包屑一路走下去。游戏角色会跟着玩家的足迹,直到追上玩家。
思考:角色智能与否,要看玩家的路径是否智能,如果玩家在转了一个圆圈,再走出去,那么角色会完全复制玩家的路径,这样就不那么智能了。对于成群的单位也是这样,如果单位都是按照领头者的路线走,那么就只有一种路线,太单调了。关键还是要找到合适的使用情况。
A、
记录和每个游戏角色有关的数据的类
B、 初始化足迹数组,一开始的足迹Col[i]=Row[i]=-1;
C、 记录玩家位置,并丢下面包屑,每走一步(往上下左右),调用丢面包屑函数
D、 定义丢下面包屑函数
E、 侦测并跟随面包屑
3. 寻着路径走
让计算机控制的角色以仿真的现实的方式移动。例如,计算机控制的赛车就必须沿着赛车道前进。
核心思路是,每一步尽量沿着上一步的路线走。比如:上一步走的是向左上方,那么对8个方向给出8个权值,左上方的权值为+2,上方和左方的权值都是+1,右下方的权值是-1,其他方向的权值都为0。然后把这八个方向按权值从大到小排序,然后,选取数值最大的,且地形可以走到的位置,作为路径的下一步。最可能的是沿着左上方继续走,最不可能的是原路返回。 替各个方向分配权重。
4. 沿着墙走
一个简单的方法:左侧移动法。即每次巨人总是尽量向其左边移动,这样就能对环境做出较为完整的探索工作。但是要注意,左侧移动法,并不能保证巨人会进入到游戏环境中的每个房间。
5. 航点导航
航点导航减少这种困扰的做法就是,认真地在游戏环境中置放节点,然后,使用预先计算好的路径,或者是简单的路径寻找方法在节点之间移动。图中的每个点,至少都能让另一个节点的视线看见。这样,游戏控制的角色总是能使用简单的视线算法,到达图中的任何地点。下一步,就是规定这些节点之间的关系。有了这个路径关系,两点之间的最短距离,就可以使用Dijkstra算法或者Floyd等算法实现。节点间的路径则由视线算法求出。计算机控制角色先计算哪一个节点和他当前位置最接近,而且在其视线之内。然后,该角色会计算哪一个节点最接近玩家当前位置,而且在玩家视线内。接着,利用节点的连接关系,一旦抵达最终节点,就可以再算出最终节点到玩家之处的视线路径了。只要找出下列表中起点和终点的交叉点,就能求出最佳路径。
空白节点表
填写节点表
Dijkstar算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法思想为:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将 加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
算法具体步骤:
(1)初始时,S只包含源点,即S=,v的距离为0。U包含除v外的其他顶点,U中顶点u距离为边上的权(若v与u有边)或 )(若u不是v的出边邻接点)。
(2)从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
(3)以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u(u U)的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
(4)重复步骤(2)和(3)直到所有顶点都包含在S中。
五.A*算法伪代码:
(1)定义搜寻区域:就是让游戏角色和物体能找出任意两点的最佳路径并避开障碍物,这就是路径寻找算法的工作。
(2)开始搜寻:一旦我们简化了搜寻区域,使其由适当数量节点构成时,就能准备开始搜寻了。我们会以A* 算法找出任何两节点间的最短路径。
以下就是A*算法伪代码:
把起始节点加进open list
while (open list 不空) {
当前节点 = open list中成本最低的节点
if (当前节点 == 目标节点)
{ 路径完成
从目标节点开始寻找其母节点,直到母节点是起始节点位置,得到路径
}
else
{把当前节点移入到close list
检视当前节点的每个相邻节点
for (每个相邻节点)
if (该节点不在open list中 && 该节点不在closed list中 && 该节点不是障碍物)
{ 把该节点移进open list
计算其成本
记录该节点的母节点为当前节点
}}}
if (还没有找到路径) {无法从起始点到达目的地}
具体过程:从起始节点开始搜寻,然后依次去搜寻周围结点
【1】open list是A*算法中的记录方式,开始时,他只有一个节点,也就是起始节点,接着会陆续把其它节点放进去。
【2】建好open list后,接着予以检查,搜寻清单内每个砖块相邻的砖块。即检查每个相邻砖块是否为路径上的有效砖块。八个相邻砖块,有效则加入open list ,无效则忽略。
【3】建立closed list(存放已访问过的成本最低的节点),当某砖块的相邻八个砖块都已经检查过,则将该砖块放进去。
以上,一次循环完成。
在open list中相邻砖块如何连接,做法是记录open list中每个砖块的母砖块,即该角色走到当前位置前的那个砖块。
(3)记分:用路径得分,找出起始砖块和目的砖块间的最佳路径。
【1】计算从起始砖块移到任何指定砖块上的移动成本。
【2】计算从指定砖块移动到目的地砖块所需的移动成本。
(4)搜寻死路:最简单的方法就是监控open list。如果检查到最后的节点,open list中再也没有任何成员,就是遇上死路了。
(5)地形成本:有时候还需要考虑其他因素,最短的路径不一定是最快的,比如不同地形的移动成本是不同的,只有在计算总的移动成本时,考虑到地形因素就可以了。当然对于金钱,燃料或其他类型的资源时,问题就会变得更加复杂一些。
(6)影响力对应:例如:通过任何敌人的视线的节点,有较高的成本。这种成本无法在设计游戏软件阶段时建立,因为游戏角色的位置是会改变的。影响力对应(influence mapping)是一种改变A*节点成本的方法,根据游戏里发生的情节而定。
六.模糊控制
基本思想就是在有限状态机的基础上引入了不确定性。在模糊控制中,即使知道了输入和NPC的当前状态,也无法确定下一个状态,目标状态的转换是由概率决定的。这样我们可以设计一些简单的有限状态机,然后通过设定不同的概率,就可以产生行为各异的NPC。NPC行为的改变只要修改概率设定就可以实现了。
设置归属函数用以计算出现每种状态的不同概率,选取最大概率的状态作为最终形成的状态。
点斜式归属度函数: 三角形归属函数(triangular membership function):
反归属度函数(reverse grade membership function) : 梯形归属函数(trapezoid membership function):
模糊归属函数:
根据以上的表达式写出相应的函数代码:用if else语句实现分段函数。例:
double 点斜式归属度函数(double value,double x0,double x1)
{ double result = 0;
if (value<=x0) result=0;
else if(value >=x1) result=1;
else result=(value/(x1-x0))-(x0/(x1-x0));
return result;}
七.有限状态机:第一部分就是在一个循环中,根据状态机做一些事情,第二个就是,做事的过程中,由于发生了一些情况,导致状态改变了,从而影响到了前面循环的过程。
switch(currentState) {
case kRoam:
if (imBlue==true) currentState=kEvade;
else if(canSeePlayer==true) currentState=kChase;
else if(canSeePlayer==false) currentState=kRoam;
case kChase:
if(imBlue==true) currentState=kEvade;
else if(canSeePlayer==false) currentState=kRoam;
else if(canSeePlayer==true) currentState=kChase;
case kEvade:
if(imBlue==true) currentState=kEvade;
else if(canSeePlayer==true) currentState=kChase;
else if(canSeePlayer==false) currentState=kRoam;
}
八.不确定状态下的决策:贝叶斯技术
1、贝叶斯思想:
(1)何谓贝叶斯网络?
贝叶斯网络是一些图形,可以替特定问题简明表示出随机变量(随机数)间的关系。这些图形有助于在面对不确定状态时,作推理或决策。
贝叶斯规则:P(B|A)=P(B)P(A|B)/P(A)——事件A发生后事件B发生的条件概率等于事件A和B都发生的概率再除以事件A发生的概率。
(2)贝叶斯网络的结构
贝叶斯网络代表随机变量的节点,以及代表随即变量间因果关系的弧或连线组成。
(3)贝叶斯网络的三种基本类型的推理:
简单网络
这三种基本类型的推理形式如下:
诊断推理
诊断推理:可能是使用贝叶斯网络时最常用的推理形式。这种推理配合贝叶斯分类网络,广泛所用于医疗诊断中。 例如,参见图右上角的网络, A 是疾病,而B 和C 是病兆。根据病兆,医生就能推论出该疾病的概率。
预测推理
预测推理:是根据成因的信息推论出结果。例如,参见图左边的网络,如果我们知道A, 而A引起了B,则我们可以推论出B发生的概率。
解释消除
解释消除:牵涉到共通结果网络,如图右下角的网络所示。假设节点都是二元节点,也就是真或假。如果我们知道C 为真,而且知道A 和B引起C ,通过C为真这一事实,就可以推论A 和B 也是真的概率。然而,假设后来我们知道了B 为真,这就表示A 发生的概率实际上降低了。这样的贝叶斯网络会有些有趣的特征,也就是独立性和条件式相互依存。
(4)贝叶斯网络的另一种形式的独立性:D分离: 某个节点阻断一群节点。C引起了D,而D引起了E和 F,但A和B引起了C。然而,如果我们知道C的状态,则A 和B 对D 就没有影响,因此,对E 和F 也没有影响。同样地,如果我们知道D的状态,节点A 、B 、C 就和E 、F 没有因果关系
D分离
(5)只设置陷阱:
(1)两节点链:陷阱->上锁
(2)画树图
(3)求概率
(4)做推论
1、P(T|L)=P(L|T)P(T)/P(L)
2、P(L)=P(L|T)P(T)+P(L|~T)P(~T)
(6)陷阱和宝物:
(1)三节点链:宝物->陷阱->上锁
(1)
(2)
(2)做推论
1、P(T|L)=P(L|T)P(T)/P(L)
2、P(T)=P(T|Tr)P(Tr)+P(T|~Tr)P(~Tr)
3、P(L)=P(L|T)P(T)+P(L|~T)P(~T)
4、P(Tr|L)= P(L|Tr)P(Tr)/P(L)
5、P(L|Tr)= P(L|T)P(T|Tr)+P(L|~T)P(~T|Tr)
2、在游戏中应用:
应用一:想象一个游戏,NPC碰巧遇上储物柜,也许被上锁,也许没上锁。是否上锁,取决于是否存放宝物,或是否设有陷阱。
贝叶斯网络实例
我们假设"设陷阱"节点可以是TRUE 或FALSE 。同时也假设"宝物"节点可以是TRUE或FALSE ,如果"上锁"节点能为TRUE 或FALSE 之一,目。我们需要让"上锁"节点有一个条件概率表,根据"设陷阱"节点以及"宝物"节点的值的每种组合,给"上锁"节点为TRUE的概率,同时,根据"设陷阱"节点以及"宝物"节点的值的每种组合,给定"上锁"节点为FALSE 的概率。表13-1 给出了此例"上锁"节点的条件概率表。
本文档为【AI游戏人工智能浓缩版(袖珍考试参考)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。