1. 程式人生 > 其它 >計算機軟體實習(三)A*演算法迷宮實現

計算機軟體實習(三)A*演算法迷宮實現

技術標籤:c++

A*演算法迷宮實現
實驗內容:
(1) 迷宮遊戲是非常經典的遊戲,在該題中要求隨機生成一個迷宮,並求解迷宮;
(2)要求查詢並理解迷宮生成的演算法,並嘗試用兩種不同的演算法來生成隨機的迷宮。
(3)要求遊戲支援玩家走迷宮,和系統走迷宮路徑兩種模式。玩家走迷宮,通過鍵盤方向鍵控制,並在行走路徑上留下痕跡;系統走迷宮路徑要求基於 A演算法實現,輸出走迷宮的最優路徑並顯示。設計互動友好的遊戲圖形介面。
(4) 選做:① 基於 A
演算法的八數碼問題

簡單思路:
1,把起始格新增到開啟列表。
2,重複如下的工作:
a) 尋找開啟列表中F值最低的格子。我們稱它為當前格。
b) 把它切換到關閉列表。

c) 對相鄰的格中的每一個?
* 如果它不可通過或者已經在關閉列表中,略過它。反之如下。
* 如果它不在開啟列表中,把它新增進去。把當前格作為這一格的父節點。記錄這一格的F,G,和H值。
* 如果它已經在開啟列表中,用G值為參考檢查新的路徑是否更好。更低的G值意味著更好的路徑。如果是這樣,就把這一格的父節點改成當前格,並且重新計算這一格的G和F值。如果你保持你的開啟列表按F值排序,改變之後你可能需要重新對開啟列表排序。
d) 停止,當你
* 把目標格新增進了關閉列表(註解),這時候路徑被找到,或者
* 沒有找到目標格,開啟列表已經空了。這時候,路徑不存在。
3.儲存路徑。從目標格開始,沿著每一格的父節點移動直到回到起始格。這就是你的路徑。

程式碼實現:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#define N 6 // 棋盤/迷宮 的階數 
using namespace std;

class Node
{
	public:
		int x, y; // 節點所在位置	
		int F, G, H; // G:從起點開始,沿著產的路徑,移動到網格上指定方格的移動耗費。
					 // H:從網格上那個方格移動到終點B的預估移動耗費,使用曼哈頓距離。 
// F = G + H Node(int a, int b):x(a), y(b){} // 過載操作符,使優先佇列以F值大小為標準維持堆 bool operator < (const Node &a) const { return F > a.F; } }; // 定義八個方向 int dir[8][2] = {{-1,-1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}; // 優先佇列,就相當於open表 priority_queue<Node>que; // 棋盤 int qp[N][N] = { {0,0,0,0,0,0}, {0,1,1,0,1,1}, {0,0,1,0,0,0}, {0,0,1,1,1,0}, {0,1,1,0,0,0}, {1,1,0,0,0,0} }; bool visit[N][N]; // 訪問情況記錄,close表 int valF[N][N]; // 記錄每個節點對應的F值 int path[N][N][2]; // 儲存每個節點的父節點 int Manhuattan(int x, int y, int x1, int y1); // 計算曼哈頓距離 bool NodeIsLegal(int x, int y, int xx, int yy); // 判斷位置合法性 void A_start(int x0, int y0, int x1, int y1); // A*演算法 void PrintPath(int x1, int y1); // 列印路徑 /* ----------------主函式------------------- */ int main() { fill(visit[0], visit[0]+N*N, false); // 將visit陣列賦初值false fill(valF[0], valF[0]+N*N, 0); // 初始化F全為0 fill(path[0][0], path[0][0]+N*N*2, -1); // 路徑同樣賦初值-1 // // 起點 // 終點 int x0, y0, x1, y1; cout<<"輸入起點:"; cin>>x0>>y0; cout<<"輸入終點:"; cin>>x1>>y1; x0--; y0--; x1--; y1--; if(!NodeIsLegal(x0, y0, x0, y0)) { cout<<"非法起點!"<<endl; return 0; } A_start(x0, y0, x1, y1); // A*演算法 PrintPath(x1, y1); // 列印路徑 } /* ----------------自定義函式------------------ */ void A_start(int x0, int y0, int x1, int y1) { // 初始化起點 Node node(x0, y0); node.G = 0; node.H = Manhuattan(x0, y0, x1, y1); node.F = node.G + node.H; valF[x0][y0] = node.F; // 起點加入open表 que.push(node); while(!que.empty()) { Node node_top = que.top(); que.pop(); visit[node_top.x][node_top.y] = true; // 訪問該點,加入closed表 if(node_top.x == x1 && node_top.y == y1) // 到達終點 break; // 遍歷node_top周圍的8個位置 for(int i=0; i<8; i++) { Node node_next(node_top.x + dir[i][0], node_top.y + dir[i][1]); // 建立一個node_top周圍的節點 // 該節點座標合法 且 未加入close表 if(NodeIsLegal(node_next.x, node_next.y, node_top.x, node_top.y) && !visit[node_next.x][node_next.y]) { // 計算從起點並經過node_top節點到達該節點所花費的代價 node_next.G = node_top.G + int(sqrt(pow(dir[i][0],2)+pow(dir[i][1],2))*10); // 計算該節點到終點的曼哈頓距離 node_next.H = Manhuattan(node_next.x, node_next.y, x1, y1); // 從起點經過node_top和該節點到達終點的估計代價 node_next.F = node_next.G + node_next.H; // node_next.F < valF[node_next.x][node_next.y] 說明找到了更優的路徑,則進行更新 // valF[node_next.x][node_next.y] == 0 說明該節點還未加入open表中,則加入 if(node_next.F < valF[node_next.x][node_next.y] || valF[node_next.x][node_next.y] == 0) { // 儲存該節點的父節點 path[node_next.x][node_next.y][0] = node_top.x; path[node_next.x][node_next.y][1] = node_top.y; valF[node_next.x][node_next.y] = node_next.F; // 修改該節點對應的valF值 que.push(node_next); // 加入open表 } } } } } void PrintPath(int x1, int y1) { if(path[x1][y1][0] == -1 || path[x1][y1][1] == -1) { cout<<"沒有可行路徑!"<<endl; return; } int x = x1, y = y1; int a, b; while(x != -1 || y != -1) { qp[x][y] = 2; // 將可行路徑上的節點賦值為2 a = path[x][y][0]; b = path[x][y][1]; x = a; y = b; } // □表示未經過的節點, █表示障礙物, ☆表示可行節點 string s[3] = {"□", "█", "☆"}; for(int i=0; i<N; i++) { for(int j=0; j<N; j++) cout<<s[qp[i][j]]; cout<<endl; } } int Manhuattan(int x, int y, int x1, int y1) { return (abs(x - x1) + abs(y - y1)) * 10; } bool NodeIsLegal(int x, int y, int xx, int yy) { if(x < 0 || x >= N || y < 0 || y >= N) return false; // 判斷邊界 if(qp[x][y] == 1) return false; // 判斷障礙物 // 兩節點成對角型且它們的公共相鄰節點存在障礙物 if(x != xx && y != yy && (qp[x][yy] == 1 || qp[xx][y] == 1)) return false; return true; }

開發心得:
說真的,此次迷宮開發並不順利,還有許多地方沒有理解,需要完善與補充。