遊戲智能——作業與練習

P&D 過河遊戲智能幫助實現,程序具體要求:

實現狀態圖的自動生成

講解圖數據在程序中的表示方法

利用算法實現下一步的計算

參考:P&D 過河遊戲智能幫助實現

實現狀態圖的自動生成
狀態圖:

在牧師與魔鬼遊戲中,依據船的位置,會有以下的狀態(以船在左邊爲例):

船在右邊時情況也與此相似。因此我們只要直到兩岸牧師和魔鬼的數量以及船的位置就可以構建狀態圖。

講解圖數據在程序中的表示方法
通過學習數據結構我們可以直到,圖的表示可以通過兩種方法:鄰接鏈表和鄰接矩陣。鄰接鏈表圖的一種鏈式存儲結構。對於圖G中每個頂點vi,把所有鄰接於vi的頂點vj鏈成一個單鏈表,這個單鏈表稱爲頂點vi的鄰接表。鄰接矩陣法是對於一個有n個點的圖,它需要一個n*n的矩陣,這個矩陣的第i行第j列的數值表示點vi到vj的距離。

利用算法實現下一步的計算
通過構建一個狀態類來儲存遊戲狀態,然後通過類似廣度搜索的方法來實現下一步的提示。

    public interface IUserAction
    {
        void MoveBoat();
        void Restart();
        void MoveRole(CharacterControllor role);
        int Check();
        int[] getStateInfo();
    }

首先在UserAction中,需要增加一個接口來獲取需要的兩岸牧師與魔鬼數量信息以及船的位置信息。

        public int [] getStateInfo()
        {
            int[] arr = new int[5];
            arr[0] = (start_land.getCharacterNum())[0];
            arr[1] = (start_land.getCharacterNum())[1];
            arr[2] = (end_land.getCharacterNum())[0];
            arr[3] = (end_land.getCharacterNum())[1];
            arr[4] = (boat.GetBoatSign() == 1) ? 0 : 1;
            return arr;
        }

然後在FirstController中實現該接口。

public class GameState : MonoBehaviour
{
    // character amount
    public int lp;
    public int rp;
    public int ld;
    public int rd;

    public bool pos;       //true for right,false for left
    public GameState parent_state;

新建一個GameState類來實現遊戲的智能提示。

    public static GameState BFS(GameState start, GameState end)
    {
        Queue<GameState> queue = new Queue<GameState>(); //store state
        GameState temp = new GameState(start.lp, start.ld, start.rp, start.rd, start.pos, null);
        queue.Enqueue(temp);



        while (queue.Count > 0)
        {
            temp = queue.Peek();

            if (temp == end) //end case
            {
                while (temp.parent_state != start)
                {
                    temp = temp.parent_state;
                }
                return temp;
            }
            queue.Dequeue();

            if (temp.pos) //boat is on the left
            {

                if (temp.lp > 0) //1 p to right
                {
                    GameState next = new GameState(temp);
                    next.parent_state = new GameState(temp); //keep parent state
                    next.pos = false;
                    next.lp--;
                    next.rp++;
                    if (next.isValid() && !queue.Contains(next))
                    {
                        queue.Enqueue(next); //push into queue
                    }
                }
                if (temp.ld > 0) //1 d to right
                {
                    GameState next = new GameState(temp);
                    next.parent_state = new GameState(temp);
                    next.pos = false;
                    next.ld--;
                    next.rd++;
                    if (next.isValid() && !queue.Contains(next))
                    {
                        queue.Enqueue(next);
                    }
                }

                if (temp.ld > 0 && temp.lp > 0) //1 p & 1 d to right
                {
                    GameState next = new GameState(temp);
                    next.parent_state = new GameState(temp);
                    next.pos = false;
                    next.ld--;
                    next.rd++;
                    next.lp--;
                    next.rp++;
                    if (next.isValid() && !queue.Contains(next))
                    {
                        queue.Enqueue(next);
                    }
                }
               
                if (temp.lp > 1) //2 p to right
                {
                    GameState next = new GameState(temp);
                    next.parent_state = new GameState(temp);
                    next.pos = false;
                    next.lp -= 2;
                    next.rp += 2;
                    if (next.isValid() && !queue.Contains(next))
                    {
                        queue.Enqueue(next);
                    }
                }
                
                if (temp.ld > 1) //2 d to right
                {
                    GameState next = new GameState(temp);
                    next.parent_state = new GameState(temp);
                    next.pos = false;
                    next.ld -= 2;
                    next.rd += 2;
                    if (next.isValid() && !queue.Contains(next))
                    {
                        queue.Enqueue(next);
                    }
                }
            }

通過廣度搜索來獲取狀態和進行相應的狀態判斷。構造一個隊列儲存每次的遊戲狀態。當隊列非空時進行循環。每次循環取出隊頭元素。然後首先判斷船的位置,若船在左邊,那麼再繼續對左邊岸上角色人數進行判斷,通過狀態圖可以直到,這裏會出現以下幾種情況:

牧師數量大於0 魔鬼數量大於0 牧師與魔鬼數都大於0 牧師數量大於1 魔鬼數量大於1
讓一個牧師移到右邊 讓一個魔鬼移到右邊 讓一個牧師和一個魔鬼移到右邊 讓兩個牧師移到右邊 讓兩個魔鬼移到右邊

然後根據相應的情況對兩岸人數進行加減,然後將當前狀態push到隊列中,並記錄父節點狀態。結束條件爲當前狀態爲遊戲結束狀態。
船在右邊的情況與此對稱,判斷右岸角色人數然後進行相應的處理就可以了。

            GameState temp = GameState.BFS(start, end);

            leftPriests = temp.lp;
            leftDevils = temp.ld;
            rightPriests = temp.rp;
            rightDevils = temp.rd;

在UserGUI中通過調用BFS來獲取遊戲狀態,然後將提示信息顯示在界面中就可以實現遊戲的智能提示了。

運行結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章