易码技术论坛

 找回密码
 加入易码
搜索
查看: 328033|回复: 7

[归档] [转]电脑游戏中的人工智能制作

[复制链接]
发表于 2006-8-5 12:57:38 | 显示全部楼层
[再来一篇]:浅谈人工智慧
在去年十二月的杂志中,笔者曾经就游戏的人工智慧和各位探讨过一些人工智慧的设计问题,没有想到在杂志
  出刊的几天後,就从网路上接到几位读者询问这些问题的信件,因此趁着大家对前一篇文章的记忆还深的情况
  下,我们就再来看看关於人工智慧的设计方式。

  网路上的网友问到,到底在设计人工智慧的时候,是如何来决定出每一行判断式的重要性。其实笔者可以很轻
  松的告诉各位,在设计人工智慧的时候,先想想自己是如何去思考的,然後就依自己认为的重要程度来划分每
  一行判断式的重要性。这种设计方法的优点就是会相当的「人性化」,设计者很容易就可以在游戏中看到电脑
  以自己的思考方式去进行各项行动,但是相对的缺点就是若是设计者的本事很差,那麽人工智慧所表现出来的
  一定也会很差,因为这样子的人工智慧完全会反映出设计者的思路。由於这种设计方式所表现出来的是设计者
  的思路,因此在一些国外的西洋棋游戏中,设计公司经常会和某些高段的棋手配合,让他们来提出各项的建议
  ,使整个游戏的人工智慧更加精确,这就是为什麽他们会这样做的原因。

  但是现在问题又来了,西洋棋游戏可以这样作,战略游戏怎麽办呢?有那一个人是真正的战略天才呢?因此笔
  者在这里提出一种比较法让各位能够了解设计时会碰到的问题。我们就以战略游戏为例好了,也就是目前市面
  上最流行的所谓RSLG。在游戏中,当一名电脑控制的角色要行动的时候,它需要考虑以下的几件事情:

   1. 行动范围内有没有可以攻击到的敌人。
   2. 移动到地理位置较好的地方。
   3. 生命是不是已经低到会死的地步。
   4. 本身是不是拥有什麽特殊的能力可以使用。

  以上是笔者粗略的将可能会发生的情况归类成四项,现在我们就一项一项的来看看。首先是第一项的行动後可
  以攻击到敌人,光是这一个情况我们就必需要做比较仔细的推算,像是不是只有一名可以攻击到的敌人?若是
  不只有一名可以攻击到的敌人时,要怎麽挑选攻击的目标?以及这样的攻击是不是可以将敌人击毙?是不是要
  以可以在这一次就击毙的敌人为目标?以及在这一次攻击之後是不是会在下一个回合就遭到敌人的回击等等。
  如果先将这些情况加以判断,那麽可能会归类出以下的几条判断式:

   A. 若是只有一名可以攻击到的敌人,那麽目标就是它。
   B. 若是有数名可以攻击到的敌人,那麽选择最弱的一名。
   C. 若是有可以在攻击後击毙的敌人,那麽它会是目标。
   D. 在攻击後在多少名敌人的攻击范围内。

  还记得十二月的文章中笔者曾经提过条列式以及积点式两种人工智慧的设计方式吗?现在我们就以这两种方法
  来看看关於这一部份的判断式重要性。以笔者主攻的眼光来看,若是有一名敌人可以被击毙,那麽这名敌人一
  定是最重要的目标,接下来才是攻击最弱的敌人,接下来是可以攻击到的敌人,最後才去判断会进入多少敌人
  的攻击范围内。因此这四条判断式若是以条列式的判断法来排序的话,将会是以下的情况:

  C > B > A > D

  在这样子的设计下,这些由人工智慧所控制的角色将会展现出相当强悍的主攻个性,就算是这一次的攻击後可
  能会遭到敌方的围攻也毫不在乎。若是各位读者觉得自己并不喜欢由人工智慧控制的角色会这样子行动,希望
  它们能够适当的避开一些会遭到敌人围攻的情况,那麽判断式的排列顺序可能会变成:

  C > B > D > A

  在这样的情况下由人工控制的角色不会一看到有可以攻击到的敌人时,就会像疯狗似的追着打,而会考虑一下
  这样子的行动会不会就遭到敌人的围攻。但是当有敌人会被击毙,或是生命相当低的时候,就会不考虑那麽多
  了。如果各位读者觉得这样子还是不好,那麽也可以将判断式的排列顺序改成如下:

  D > C > B > A

  在这样的情况下,由人工智慧控制的角色将会相当的小心,就算是可以将敌人击毙,但是若在下一回合有被其
  馀的敌人围攻的可能,就不会发动攻击。

  接下来我们看看以积点式来设计的话,会是什麽样子。同样的判断式用积点式来设计的话,就必需要给每一行
  算式不同的积点,但是同时必需要将算式做一点修正,因为在积点式中会有复数计算的情况发生,因此这些判
  断式会变成:

   A. 可以攻击到一名敌人的位置。
   B. 可以攻击到的敌人中最弱一名的位置。
   C. 攻击时可以击毙敌人的位置。
   D. 一名敌人的攻击范围内。

  各位读者可以看到,其中的第四条就是可能会复数计算的。在程式进行判断的时候,这一条可能会因为有多个
  敌人角色都合乎这个条件,而导致积点的重复计算,因此若是重复计算的次数够多了之後,反而可以将原本一
  些有利的情况抵消。以前面的这四条算式来举例,若是A的积点是+2、B的积点是+4、C的积点是+8、
  D的积点是  1,那麽当一个地点虽然可以将一名敌人击毙但是又会被五名敌人围攻的话,那麽这个地点的重
  要性就会低於一个生命力较低的敌人的位置。也由於积点式的计算方式比较精密,因此人工智慧所控制的角色
  就不会那麽的死板,而会显得比较聪明。虽然条列式的作法可以用增加判断式的办法来达到相同的目的,但是
  比较起来就是差了些。

  由以上的这个例子中,我们可以看到不论是一种人工智慧的设计方式,整个人工智慧的表现都是控制在设计者
  的手上。条列式表现在判断式的排冽顺序,而积点式表现在积点数的不同,这些都是人工智慧有「人性」的地
  方。

  网路上有网友问到,像笔者在十二月号中拿宾果作说明,但是并没有很详细的以实例说明,所以他实在是不明
  白为什麽在那篇文章中会说(No A) = 4 x 2 > (No B) = 7 x 1 。因此现在笔者就将那
  一期的范例拿出来详细的解说一下。

  首先各位看看以下的这个积点表,在这个表中列出了各种情况的重要性。至於为什麽积点设定为这样,也就是
  要让积点真正的能够发挥作用:

      ┏                        ┳   ┓                              
       判           断          式  绩点                              
      ┣                        ╋   ┫                              
       划一个号码後能够完成一条线        31                              
      ┣                        ╋   ┫                              
       划一个号码後在这一条线中出现四个点   15                              
      ┣                        ╋   ┫                              
       划一个号码後在这一条线中出现三个点    7                              
      ┣                        ╋   ┫                              
       划一个号码後在这一条线中出现两个点    4                              
      ┣                        ╋   ┫                              
       划一个号码後在这一条线上只有一个点    1                              
      ┗                        ┻   ┛

  现在我们看看下面的这个宾果盘,上面的那些「   」符号表示这个地点的数字已经被划掉了。

      ┏   ┳   ┳   ┳   ┳   ┓                                       
            6  11  16  21                                       
      ┣   ╋   ╋   ╋   ╋   ┫                                       
               12  17  22                                       
      ┣   ╋   ╋   ╋   ╋   ┫                                       
        3   8  13  18  23                                       
      ┣   ╋   ╋   ╋   ╋   ┫                                       
        4   9  14  19  24                                       
      ┣   ╋   ╋   ╋   ╋   ┫                                       
           10  15      25                                       
      ┗   ┻   ┻   ┻   ┻   ┛

  现在各位看看这个宾果盘,有两条已经有三个点的线以及两条有两个点的线,因此现在应该要划那一点比较好
  呢?我们就用上面的那积分表来计算每一个点的重要性,可以得到以下的数值:

    o 03:9+1=10
    o 04:9+1=10
    o 06:3x2=6
    o 08:3+1=4
    o 09:3+1x2=5
    o 10:5+3=8
    o 11:3+1=4
    o 12:5+1=6
    o 13:5+3+1x2=10
    o 14:3x2=6
    o 15:5+1=6
    o 16:3x2=6
    o 17:5+3x2=11
    o 18:3+1=4
    o 19:3+1=4
    o 21:3x2+1=7
    o 22:5+1=6
    o 23:3x2=6
    o 24:3x2=6
    o 25:5x2+1=11

  从这些算式中,我们可以看到若是划下17或是25将可以赚得最多的积点,因此这两个数字就是我们现在要考虑
  的。接下来再以这两个数字在宾果盘上的重要地位来判断,25的位置要比17要好,因此就可以决定划下25这个
  数字。这种计算方式就是积点式的典型算法,人工智慧会将整的盘上的所有数字都做一番推算,然後才选出最
  好的一点。

  网友还问到,在上一次的文章中笔者曾经说过积点式的人工智慧在进行判断的时候为什麽会花掉比较多的时间
  。以下来说明:

  各位读者可以想想看,若是每一格的计算需要花掉一分钟的话,那麽在积点式的判断中,每一次不论如何的行
  动,都需要花掉相同的时间。而条列式的判断方式会因为当时判断的位置不同而略有不同。或许各位读者会说
  ,不过是几个步骤而已嘛,时间上能够差多少?但是各位不要忘了,在这篇文章中笔者只不过是举例,所以看
  起来判断式好像不多,但是在真正游戏中的人工智慧却不只是这麽短短几行的判断。试着想想看,若是每一行
  判断式是十秒钟,那麽当判断式一多了之後会是怎麽样呢?

  这一次再向各位读者解释人工智慧的运算方式,希望各位读者都能够看得懂,若是各位对游戏企划有任何问题
  ,或是对笔者所介绍的地方有疑问,都欢迎各位来信询问。
发表于 2006-8-6 05:09:47 | 显示全部楼层
我感觉比较合适的AI是一种实现"录制"好的AI,由特定条件触发的特定动作加上一些变数

例如HL2中的电脑AI就是靠地图上由好多"patch"组成的线路

PS:具体请参考半条命 第二章  打开讲解,在医院的那关有一个讲解提到了AI并在地图上显示了AI路线
这个路线就是"录制"好的一种方式
发表于 2006-8-7 10:02:03 | 显示全部楼层
在哪有?偶对CS的感兴趣。
发表于 2006-8-8 14:46:06 | 显示全部楼层
好东东!值得研究研究。顶一个先!
怎么还没有。。
。。。。。
。。。。
。。。。。
人。。。。。啊!!!
。。。
发表于 2006-8-8 19:46:24 | 显示全部楼层
引用第2楼laugj2006-08-06 05:09发表的“”:
我感觉比较合适的AI是一种实现"录制"好的AI,由特定条件触发的特定动作加上一些变数

例如HL2中的电脑AI就是靠地图上由好多"patch"组成的线路

PS:具体请参考半条命 第二章  打开讲解,在医院的那关有一个讲解提到了AI并在地图上显示了AI路线
.......

我也想知道。
发表于 2006-8-8 22:12:40 | 显示全部楼层
对不起  一时打错了
应该是半条命2 第一章 (原先的 《劫后余生》)

医院那关  有一个地方有对于半条里的AI的一个简单讲解

呵呵 这么长时间了 才发现.... 可能是因为这个第一章跟原始的半条命2有点"冲突吧" 下意识的写成第二章了


据说第二章到现在还是只有中文讲解  语音还是英文的....好期待第2章啊~~~~~ 要等到圣诞节才有第2章....
发表于 2006-8-8 22:12:53 | 显示全部楼层
见笑了 8好意思  嘿嘿
 楼主| 发表于 2006-8-5 12:52:03 | 显示全部楼层 |阅读模式
  电脑游戏随着硬件执行效率与显示解析度等大幅提升,以往很多不可能或非常难以实现的电脑游戏如此都得以顺利完成。虽然电脑游戏的呈现是那么地多样化,然而却与我们今日所要探讨的主题,人工智能几乎都有着密不可分的关系。
  在角色扮演游戏中,程序员与企划人员需要精确地在电脑上将一个个所谓的“怪物”在战门过程中栩栩如生地制作出来;所以半兽人受了重伤懂得逃跑,法师懂得施展攻性法术。
  目前能让人立刻想到与人工智能有密切关系的游戏有两种:
   一是所谓的战棋/策略模拟游戏,二则是棋弈游戏。人工智能的比重与深浅度,在不同的游戏类型中各有不一。有的电脑游戏非标榜着高人工智能不可,不然没有人买;有的则是几乎渺茫到让玩家无法感觉有任何人工智能的存在。            

 导向式思考

  AI最容易制作的的方式,同时也是早期游戏AI发展的主要方向就是规则导向或称之为假设导向。在一些比较简单的电脑游戏中,程序员可以好不困难地将游戏中的规则与设定转化成一条条的规则,然后将它们写成电脑程序。让我们以角色扮演游戏为例。决大多数的企画在设定所谓电脑怪物时,所设定的属性通常有以下几种:

  生命值 攻击力 防御力 法力  属性

  最后一个“属性”是我在设定时喜欢增加的项目之一。透过这项属性的设定,我可以把怪物设定成“贪生怕死的”,也可以把战士设定为“视死如归”。以目前我们所掌握的资料,在战门系统中的大纲如是诞生了:                          

规则一

if (生命值< 10) // 边临死亡了吗 
{  if (属性== 贪生怕死)               
   结果 = 试图逃跑               
  if (有任何恢复生命值的物品或法术可用)        
   结果 = 使用或施展相关物品或法术         
}                                           
        

规则二
   
if (可施攻击性法术 && 有足够法力)  
{                          
   结果 = 施展攻攻击性法术               
}                          

  由以上一连串的“如果--就--”规则设定,建立了最基本的AI。说这样的制方式只能建立基本AI其实并不当然正确。只要建立足够及精确的规则,这样的方式仍然有一定水准的表现。
  规则导向的最大优点就是易学易用。在没有深奥的理论概念的前提下,仍有广大的使用群。所以很多老道的玩家常常没两下就摸清楚敌人的攻击策略,移动方式等等。

 推论式思考

  相信曾经接触过电脑语言课程,或是自习过相关书籍的朋友们,都曾曾经听过一个著名的程序,那就是井字游戏。用井字游戏作为讨论AI的入门教材,我个人觉得是最适当的例子。或许有人还不知道井字游戏怎么玩。只要任何一方在三乘三的方格中先先成一线便胜利了。我们在前面谈过的规则导向,在这里也可以派得上用场。

 if任何一线已有我方两子&&另外一格仍空//我方即将成一线吗
  结果 = 该空格                       
 if任何一线已有敌方两子&&另外一格仍空//防止敌方作成一线 
  结果 = 该空格                       
 if任何一线已有我方一子&&另外两格仍空//作成两子    
  结果 = 该空格  

  有一次我在某本电脑书上,同样地也看到某些以井字游戏为介绍的范例。不同的是,我几乎看不到任何规则导向的影子。但在仔细分析该程序码后,我得到了极大的启发,原来AI是可以不用这么多规则来制作的。它用的方法正是在电脑AI课程中重要的概念:极大极小法。我在这里只说明这法则的概念。继续以井字游戏为例,电脑先在某处下子,接着会以假设的方式,替对方下子,当然,必须假设对方下的是最佳位置,否则一切则毫无意义。在假设对方下子的过程中,自然又需要假设我方的下一步回应,如此一来一往,直到下完整局游戏为止。

底下是节录书中的程序片段:                         
   
bestMove(int p, int*v)  
{   int i;   
   int lastTie;                    
   int lastMove;                   
   int subV;                                     
/*First, check for a tie*/              
    if (isTie()) {                
     *v=0;                 
     return(0);                
   };  
/*If not a tie, try each potential move*/  
 for (*v=-1, lastTie=lastMove=-1,i=0;i<9;i++)  
  {  
   /*If this isn&#39;t a possible, skip it*/            
   if (board!=0) continue;  
   /* Make the move. */  
    lastMove=i;   
    board=p;                               
   /* Did it win? */                         
    if (hasWon(p)) *v=1;                       
    else{                               
   /*If not, find out how good the other side can do*/  
     bestMove(-p,&subV);                        
   /* If they can only lose, this is still a win.*/  
      if (subV==-1) *v=1;         
   /* Or, if it&#39;s a tie, remember it. */           
       else if (subV==0){                   
          *v=0;         
          lastTie=i;   
          };                            
       };                                
/* Take back the move. */             
           board=0;            
/*If we found a win, return immediately  
     (can&#39;t do any better than that)*/       
  if (*v==1) return(i);                       
/*If we didn&#39;t find any wins, return a tie move.*/           
  if (*v==0) return(lastTie);                        
/*If there weren&#39;t even any ties, return a loosing move.*/       
  else return(lastMove);   
};      

  国外的一些论坛曾举行过256字节的游戏设计比赛。作品非常多,其中有一件作品正巧也是井字游戏。作者用区区两百多行就写了与上述程序演算方式完全相同的作品,可见功力确实了的。另外,我也很希望类似的活动能在国内推展起来。对了,在这样的比赛条件限制下,除了汇编语言外,几乎没有其它的选择了。    

  .386c                        
  code      segment byte public use16      
          assume cs:code, ds:code        
                            
          org   100h              
                            
  tictac     proc  far             
                            
  start:                         
          push  cs               
          pop   ds               
          mov   ax,0B800h     ; 清除屏幕
          mov   es,ax       ;      
          xor   di,di       ;      
          mov   cx,7D0h      ;      
          mov   ax,0F20h      ;      
          rep   stosw       ;      
          xor   cx,cx       ;      
          mov   dl,5              
  loc_1:                         
          call  printBoard           
  loc_2:                         
          mov   ah,8        ; 等待按键
          int   21h             
                            
          movzx  bx,al            

          sub   bl,31h       ; 如果不是1..9
          jc   loc_2       ; 则重新输入 
          cmp   bl,8                
          ja   loc_2              
          cmp   data_1[bx],al          
          jne   loc_2              
          mov   byte ptr data_1[bx],&#39;x&#39;     
          dec   dl                 
          jz   short loc_3           
          mov   al,&#39;o&#39;               
          call  bestMove              
          mov   [si],al             
          call  isWin   ; 判断是否已取得胜利 
          jnc   loc_1              
  loc_3:                            
          call  printBoard             
          mov   ax,4C00h              
          int   21h               
                              
  data_1     db   &#39;12&#39;                
  data_2     db   &#39;3456789&#39;            
  data_3     db   0                
                              
  tictac     endp                    
                              
                              
  printBoard   proc  near                
          mov   si,offset data_1          
          mov   di,548h               
          mov   cl,3                
                              
  locloop_4:                         
          movsb                  
          add   di,5                
          movsb                  
          add   di,5                
          movsb                  
          add   di,133h             
          loop  locloop_4            
                              
          retn                    
  printBoard   endp                    
                              
                              
  isWin      proc  near                
          mov   bx,1                
          mov   bp,3                
          call  sub_3    ; 检查横向是否完成 
          inc   bx                 
          inc   bx                 
          dec   bp                 
          dec   bp                 
          call  sub_3    ; 检查纵向是否完成   
          call  sub_4    ; 检查斜向是否完成  
          clc  
          retn                    
  isWin      endp                    
                              
  loc_5:                           
          stc                   
          retn                    
                                           
                 
  sub_3      proc  near                
          mov   ah,3                
          mov   si,offset data_1          
  loc_6:                           
          mov   di,si              
          call  sub_5              
          add   si,bp               
          dec   ah               
          jnz   loc_6              
          retn                    
  sub_3      endp                  
                             
  sub_4      proc  near              
          mov   di,offset data_1         
          inc   bx                
          call  sub_5             
          mov   di,offset data_2        
          dec   bx                 
          dec   bx                 
          call  sub_5              
          retn                    
  sub_4      endp                    
                              
                              
  sub_5      proc  near                
          mov   cl,3                
                              
  locloop_7:                         
          cmp   [di],al             
          jne   short loc_ret_8         
          add   di,bx              
          loop  locloop_7            
                              
          add  &
您需要登录后才可以回帖 登录 | 加入易码

本版积分规则

Archiver|手机版|小黑屋|EMAX Studio

GMT+8, 2024-4-20 11:39 , Processed in 0.011959 second(s), 18 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表