想了一個尋路算法,用C++實現了一下,界面用MFC完成的很簡單。用20x20的方形區域作為迷宮,為了方便,隨機選取了大約1/3的格子作為路障,禁止通過。規則是在只能想前后左右四個方向移動的前提下找到從入口(默認左上角)到出口(默認右下角)的最短路徑。
源代碼下載:http://files.cnblogs.com/GhostZCH/MFCMaze.rar(如果你下載了,希望你能留下只言片語,哪怕是“+1”也好,謝謝)
說來這個算法也不算難,借鑒了路由器建立路由表的算法,更加簡化一些。熟悉TCP/IP協議的筒子們一定會記得路由表建立的原來,這個算法也一樣,把每一個單元看成一個路由器,在它上下左右的四個格子可以看做與它聯通的四個路由器。每個單元與相鄰的單元交換路由信息,直到穩定下來,這樣就獲得了每個單元到出口的路由信息。所謂的路由信息並不是一條完整地路徑,只保存了到達出口的跳數(距離)和下一跳(下一步)的位置。這樣如果存在從入口到出口的路徑就可以找出來。如果在與相鄰單位交換信息時,只保存最短的路徑,就可以得到最短路徑,同時最短路選擇也避免了繞圈形成死循環的問題。
界面很簡單,進入程序或者點擊建立迷宮時生成一個隨機迷宮,點擊尋找路徑后電腦會執行尋路算法,通過提示框提示尋路是否成功及迭代次數,如果成功顯示路徑和每個格子到出口的距離。黑色為障礙,灰色為可通過區域,綠色為電腦找到的路徑,數字標明該格子到出口(右下角)的最短距離,沒有數字的灰色格子說明這個格子與出口不連通。如上圖中的左下區域。雖然結果只顯示了從左上到右下的最短路徑,事實上算法已經計算出每個格子(與出口聯通的)到達出口的最短路徑和距離。
下面的兩組圖片是生成的迷宮和找到的路徑,運行時間沒有計算,人工觀測都小於1秒。有興趣的筒子可以驗證一下是不是最短的路徑。
尋路的核心代碼如下:
數據用的是“vector<Block *> _blocks”按照行優先的格式存下來的,在之前生成迷宮的時候就已經控制了入口和出口不是障礙,所以一開始先把出口的位置數據初始化了一下,剩下的就是迭代了。括號有十層,確實有點暈了,事實上不建議這樣寫代碼的,超過五層括號就讓人很迷惑了。但是考慮到程序本身比較小,既是不拆分函數也只有50行,不算變態(有一次需要讀懂一個700行的函數,淚奔啊!!),循環體內部的邏輯也比較簡單,沒有太復雜的,所以就一個函數搞定,當然還是不建議大家這么做的。
1 int Grid::InitMap() 2 { 3 Block* target = _blocks.at(_height*_width-1); 4 target->CanReach(true); 5 target->JumpCount(0); 6 7 int times = 0; 8 bool ischanged = true; 9 //迭代到收斂,每個格子有最短個路徑的長度和下一跳地址 10 while(ischanged) 11 { 12 times ++; 13 ischanged = false; 14 15 // 逐格聞訊周圍格子路徑 16 for (int i=0;i<_height;i++) 17 { 18 for (int j =0;j<_width;j++) 19 { 20 //問詢鄰居,選擇最短的路徑更新自己的路由信息 21 Block* block = Get(i,j); 22 if (block->CanPass()) 23 { 24 for (int ii=-1;ii<2;ii++)//-1到1 25 { 26 for (int jj=-1;jj<2;jj++)//-1到1 27 { 28 if(abs(ii)+abs(jj)==1)//只選四鄰域 29 { 30 int x = i+ii; 31 int y = j+jj; 32 if (x>=0&&y>=0&&x<_height&&y<_width)//處於格子中的鄰域 33 { 34 // 鄰接格子中比自己路徑短的更新自己的路徑 35 Block *tmp = Get(x,y);//某個鄰接的格子 36 if (tmp->CanReach()&&(tmp->JumpCount()+1)<block->JumpCount()) 37 { 38 block->CanReach(true);//設置可到達 39 block->JumpCount(tmp->JumpCount()+1);//設置跳數為鄰近格子的跳數+1 40 block->NextJump(x*_width+y);//下一跳地址是鄰接的格子 41 ischanged = true; 42 } 43 }//end if 44 }//end if 45 }//end for jj 46 }//end for ii 47 }//end if 48 }//end for j 49 }//end for i 50 }// end while 51 return times; 52 }
順便多貼幾張結果圖,當然也有尋路失敗的: