五千年(敝帚自珍)

主题:初到贵宝地,灌一点游戏开发的东东 -- foundera

共:💬23 🌺4 新:
全看分页树展 · 主题 跟帖
家园 浅谈电脑游戏中的人工智能制作

浅谈电脑游戏中的人工智能制作

电脑游戏随着硬件执行效率与显示解析度等大幅提升,以往很多不可能或非常难以实现的电脑游戏如此都得以顺利完成。虽然电脑游戏的呈现是那么地多样化,然而却与我们今日所要探讨的主题,人工智能几乎都有着密不可分的关系。

  在角色扮演游戏中,程序员与企划人员需要精确地在电脑上将一个个所谓的“怪物”在战门过程中栩栩如生地制作出来;所以半兽人受了重伤懂得逃跑,法师懂得施展攻性法术。

  目前能让人立刻想到与人工智能有密切关系的游戏有两种:一是所谓的战棋/策略模拟游戏,二则是棋弈游戏。人工智能的比重与深浅度,在不同的游戏类型中各有不一。有的电脑游戏非标榜着高人工智能不可,不然没有人买;有的则是几乎渺茫到让玩家无法感觉有任何人工智能的存在。            

导向式思考

  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't a possible, skip it*/          

if (board[i]!=0) continue;

/* Make the move. */

lastMove=i; 

board[i]=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's a tie, remember it. */         

else if (subV==0){

*v=0;

lastTie=i; 

};                          

};                              

/* Take back the move. */

board[i]=0;

/*If we found a win, return immediately (can't do any better than that)*/  

if (*v==1) return(i);

/*If we didn't find any wins, return a tie move.*/

if (*v==0) return(lastTie);

/*If there weren'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],'x'     

          dec   dl               

          jz   short loc_3           

          mov   al,'o'             

          call  bestMove            

          mov   [si],al             

          call  isWin   ; 判断是否已取得胜利 

          jnc   loc_1              

  loc_3:                          

          call  printBoard           

          mov   ax,4C00h            

          int   21h               

                              

  data_1     db   '12'              

  data_2     db   '3456789'            

  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   sp,4              

          jmp   short loc_5           

                              

  loc_ret_8:                       

          retn                      

  sub_5      endp                      

                                  

  bestMove    proc  near                  

          mov   bx,31FEh                

          mov   cl,9                  

          mov   di,offset data_1            

                                  

  locloop_9:                           

          cmp   [di],bh     ; #empty?        

          jne   short loc_12  ; #no, skip       

          mov   [di],al                 

          pusha                      

          call  isWin      ; #CY: Win       

          popa          ;            

          jnc   short loc_10  ;            

          mov   bl,1                  

          mov   si,di                  

          mov   [di],bh                 

          retn                      

  loc_10:                             

          pusha                      

          xor   al,17h ; good! toggle 'o' / 'x'

          call  bestMove                

          mov   data_3,bl                

          popa                      

          mov   ah,data_3                

          neg   ah                   

          cmp   ah,bl                  

          jle   short loc_11              

          mov   bl,ah                  

          mov   si,di                  

  loc_11:                             

          mov   [di],bh                 

  loc_12:                             

          inc   bh                   

          inc   di                   

          loop  locloop_9                

                                  

          cmp   bl,0FEh                 

          jne   short loc_ret_13            

          xor   bl,bl                  

                                  

  loc_ret_13:                           

          retn                      

  bestMove    endp                      

  code      ends

          end   start                  

 

Copyright &copy; 1998-2003 www.chinagamedev.net All Rights Reserved.

网站维护 [email protected]

站务合作 [email protected]

全看分页树展 · 主题 跟帖


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河