A* 尋路過程

首先來一張地圖,在此A點爲起始點,B點爲終點(目的地),中心藍色的爲障礙物,如下圖:

wKioL1l8j2aC3tBqAADj__lCd_8419.png-wh_50



尋路步驟:

   1,從點A開始,並且把它作爲待處理點存入一個“開啓列表”。開啓列表就像一張購物清單。儘管現在列表裏只有一個元素,但以後就會多起來。你的路徑可能會通過它包含的方格,也可能不會。基本上,這是一個待檢查方格的列表。
   
2,尋找起點周圍所有可到達或者可通過的方格,跳過有牆,水,或其他無法通過地形的方格(障礙物)。也把他們加入開啓列表。爲所有這些方格保存點A作爲“父方格”。當我們想描述路徑的時候,父方格的資料是十分重要的。後面會解釋它的具體用途。
   
3,從開啓列表中刪除點A,把它加入到一個“關閉列表”,列表中保存所有不需要再次檢查的方格。

wKiom1l8lKLhHnW9AACLWuaH8Xo196.png-wh_50


從 "開啓列表" 中找出相對最靠譜的方塊, 什麼是最靠譜? 它們通過公式 F=G+H 來計算.

G = 從起點A,沿着產生的路徑,移動到網格上指定方格的距離。
H = 從網格上那個方格移動到終點B的預估移動距離。

我們假設橫向移動一個格子的耗費爲10, 爲了便於計算, 沿斜方向移動一個格子耗費是14. 爲了更直觀的展示如何運算 FGH, 圖中方塊的左上角數字表示 F, 左下角表示 G, 右下角表示 H



爲了繼續搜索,我們簡單的從開啓列表中選擇F值最低的方格。然後,對選中的方格做如下處理:

wKioL1l8nXqSksV7AAFSIkFx378122.png-wh_50   

   4,把它從開啓列表中刪除,然後添加到關閉列表中。
   
5,檢查所有相鄰格子。跳過那些已經在關閉列表中的或者不可通過的(有牆,水的地形,或者其他無法通過的地形),把他們添加進開啓列表,如果他們還不在裏面的話。把選中的方格作爲新的方格的父節點。
   
6,如果某個相鄰格已經在開啓列表裏了,檢查現在的這條路徑是否更好。換句話說,檢查如果我們用新的路徑到達它的話,G值是否會更低一些。如果不是,那就什麼都不做。
      另一方面,如果新的G值更低,那就把相鄰方格的父節點改爲目前選中的方格(在上面的圖表中,把箭頭的方向改爲指向這個方格)。最後,重新計算F和G的值。


圖解


當A被放入開啓列表後 , 按照上面的步驟,尋找其周圍F值最小的格子:

wKiom1l8mFbQT9UoAAFElRxNyGY396.png-wh_50

如上圖, 我們選中了 C 因爲它的 F 值最小, 我們把它從 "開啓列表" 中刪除, 並把它加入 "關閉列表". 它(C點)右邊上下三個都是牆, 所以不考慮它們. 它左邊是起始方塊(A點), 已經加入到 "關閉列表" 了, 也不考慮. 所以它周圍的候選方塊就只剩下 4 個. 讓我們來看看 C 下面的那個格子(D點,早已在A點的視乎已經加入了開啓列表), 它目前的 G 是14, 如果通過 C 到達它的話, G將會是 10 + 10, 這比 14 要大, 因此我們什麼也不做.

wKioL1l8mhrh7RJ_AAE6BqCgTIU080.png-wh_50

然後我們繼續從 "開啓列表" 中找出 F 值最小的, 但我們發現 C 上面的和下面的同時爲 54, 這時怎麼辦呢? 這時隨便取哪一個都行, 比如我們選擇了 C 下面的那個方塊 D.

D 右邊以及右上方的都是牆, 所以不考慮, 但爲什麼右下角的沒有被加進 "開啓列表" 呢? 因爲如果 C 下面的那塊也不可以走, 想要到達 C 右下角的方塊就需要從 "方塊的角" 走了(產生絆腳), 在程序中設置是否允許

這樣走.

絆腳解釋如下圖:

wKiom1l8nFLwuce3AAGHJ2_mx7Y271.png-wh_50

就這樣, 我們從 "開啓列表" 找出 F 值最小的, 將它從 "開啓列表" 中移掉, 添加到 "關閉列表". 再繼續找出它周圍可以到達的方塊, 如此循環下去。

        那麼什麼時候停止呢:當我們發現 "開始列表" 裏出現了目標終點方塊的時候, 說明路徑已經被找到.


我們測試一下A*

這裏需要使用AStarLib類庫 , 會在附件中提供。

using AStarLib;
using AStarLib.com;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AStarTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 0 : 可走 / 1 : 不可走
            int[,] array = {
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
                           { 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1},
                           { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
                           { 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1},
                           { 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
                           { 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
                           { 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1},
                           { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
                           };
            AStarHandler aStar = new AStarHandler(array, 10);
            Console.WriteLine("開始尋路 , 開始點 : {0},{1} 目標點 : {2},{3}", 1, 1, 6, 4);
            List<Node> path = aStar.Get_Map_Path(new Node(1, 1), new Node(6, 4), false);
            if (path != null)
            {
                Console.WriteLine("路徑爲(下標對應的是地圖數組的下標,所以從0開始) : ");
                Console.WriteLine("====================================");
                Node cell = null;
                for (int i = 0, j = path.Count; i < j; i++)
                { 
                    cell = path[i];
                    Console.WriteLine("橫(第一維)下標{0} , 縱(第二維)下標{1}", cell.Row_Index, cell.Col_Index);
                }
            }
            else
            {
                Console.WriteLine("此地無法到達");
            }
            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("開始尋路 , 開始點 : {0},{1} 目標點 : {2},{3}", 1, 1, 7, 7);
            path = aStar.Get_Map_Path(new Node(1, 1), new Node(7, 7), false);
            if (path != null)
            {
                Console.WriteLine("路徑爲(下標對應的是地圖數組的下標,所以從0開始) : ");
                Console.WriteLine("====================================");
                Node cell = null;
                for (int i = 0, j = path.Count; i < j; i++)
                {
                    cell = path[i];
                    Console.WriteLine("橫(第一維)下標{0} , 縱(第二維)下標{1}", cell.Row_Index, cell.Col_Index);
                }
            }
            else
            {
                Console.WriteLine("此地無法到達");
            }
            Console.Read();
        }
    }
}

結果:

wKioL1l9uyziEjLvAABr7jJRQqI947.png-wh_50

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