游戲人工智能 讀書筆記 (四) AI算法簡介——Ad-Hoc 行為編程


本文內容包含以下章節:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM