【C / EasyX】隨機迷宮生成和路徑搜尋(DFS)
阿新 • • 發佈:2019-01-28
==========================================說明=====================================
1.用了寫遊戲的基本流程所以介面是持續重新整理的(可能會改成遊戲)
2.迷宮生成演算法是深度優先遍歷(DFS),為了讓路線不那麼容易走通,是從終點向起點生成的。
PS1:迷宮難度不高,路線完全隨機似乎不是太好,有待改進。
PS2:至於EasyX,是個C語言可用的不錯的圖形庫,下載的話百度一下就有了。
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <graphics.h> //////////////////////////////以下是 巨集定義引數 ////////////////////////////// #define WIN_WIDTH 1200 // 窗體寬度 #define WIN_HEIGHT 680 // 窗體高度 #define MAZE_WIDTH 15*34 // 迷宮寬度 #define MAZE_HEIGHT 15*17 // 迷宮高度 #define B_WIDTH 10 // 格子寬度 #define X 51 // 迷宮列數 #define Y 26 // 迷宮行數 #define N X*2+1 // 迷宮陣列列數 #define M Y*2+1 // 迷宮陣列行數 #define L (N-1)*(M-1) // 迷宮路徑長度 #define LX 80 // 起點座標X #define LY 50 // 起點座標Y #define C_BLOCK RGB(153,102,85) // 牆壁顏色 #define C_GRAY RGB(123,72,55) // 陰影顏色 #define C_GROUND RGB(50,50,50) // 地面顏色 #define C_ROOT RGB(170,170,210) // 路徑顏色 #define C_LINE YELLOW // 路徑線顏色 //////////////////////////////以下是 函式宣告 ////////////////////////////// float GetFPS(); // 計算畫面FPS(每秒幀數) void Delay(DWORD ms); // 絕對延時 bool IsInRect(int x,int y,RECT r); // 是否在矩形內 void InitMaze(); // 初始化迷宮陣列 void OutputMaze(); // 輸出迷宮 void CopyScreenBuffer(); // 複製螢幕到快取 void DrawScreenBuffer(); // 繪製快取到螢幕 void OutPutPath(); // 輸出路徑連線 void DrawFPS(); // 輸出幀數 int Move(int d,int x,int y); // 向指定方向移動,返回是否成功移動 void DFS_CreateMaze(int x,int y); // 深度優先生成迷宮 void FindPath(int i,int j); // 深度優先搜尋迷宮路徑 void CreateMaze(); // 建立迷宮 void DrawMenuItems(); // 繪製選單按鈕 //////////////////////////////以下是全域性變數定義////////////////////////////// DWORD* p_Screen; // 視訊記憶體指標 DWORD buff_Screen[WIN_WIDTH*WIN_HEIGHT]; // 顯示快取 int isShowFPS=1; // 是否顯示幀數 int isShowMazePath=0; // 顯示迷宮路徑 int Maze[M][N]; // 迷宮陣列 0 普通通路 1 牆 2 路徑點 3 起點 4 終點 int path_x[1000][L],path_y[1000][L]; // 路徑座標 int length[L]; // 路徑搜尋長度 int lengtha,count; // 搜尋計數 int mouseX; // 滑鼠位置座標X int mouseY; // 滑鼠位置座標Y bool isMouseDown; // 滑鼠按下 int start_x,start_y,end_x,end_y; // 起點座標 終點座標 int temp_d; // 上一步走的方向 int Move_flag; // 移動標記 //////////////////////////////以下是輔助功能函式////////////////////////////// // 計算畫面FPS(每秒幀數) float GetFPS() { #define FPS_COUNT 8 static int i = 0; static DWORD oldTime = GetTickCount(); static float fps; if (i > FPS_COUNT) { i = 0; int newTime = GetTickCount(); int elapsedTime = newTime - oldTime; fps = FPS_COUNT / (elapsedTime / 1000.0f); oldTime = newTime; } i++; return fps; } // 絕對延時 void Delay(DWORD ms) { static DWORD oldTime = GetTickCount(); while(GetTickCount() - oldTime < ms) Sleep(1); oldTime = GetTickCount(); } // 是否在矩形內 bool IsInRect(int x,int y,RECT r) { if((x>=r.left && x<=r.right) && (y>=r.top && y<=r.bottom)) return true; else return false; } // 初始化迷宮陣列 void InitMaze(){ for(int i=0;i<M;i++){ for(int j=0;j<N;j++){ Maze[i][j]=1; } } // 設定起點和終點 Maze[1][0]=3; Maze[M-2][N-1]=4; } // 輸出迷宮 void OutputMaze(){ // 清屏 setcolor(RGB(255,255,255)); setfillcolor(RGB(255,255,255)); fillrectangle(0,0,WIN_WIDTH,WIN_HEIGHT); for(int i=0;i<M;i++){ for(int j=0;j<N;j++){ int xx=LX+j*B_WIDTH; int yy=LY+i*B_WIDTH; switch(Maze[i][j]){ case 0: setcolor(C_GROUND); setfillcolor(C_GROUND); fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH); break; case 1: setcolor(C_BLOCK); setfillcolor(C_BLOCK); fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH); if(Maze[i][j]==1 && (i==M-1 || Maze[i+1][j]!=1)) { setcolor(C_GRAY); setfillcolor(C_GRAY); fillrectangle(xx,yy+B_WIDTH/2,xx+B_WIDTH,yy+B_WIDTH); } break; case 2: setcolor(C_GROUND); setfillcolor(C_GROUND); fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH); /* setcolor(C_ROOT); setfillcolor(C_ROOT); fillellipse(xx+B_WIDTH/2-1,yy+B_WIDTH/2-1,xx+B_WIDTH/2+1,yy+B_WIDTH/2+1); */ break; case 3: setcolor(C_GROUND); setfillcolor(C_GROUND); fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH); break; case 4: setcolor(C_GROUND); setfillcolor(C_GROUND); fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH); break; } } } } // 複製螢幕到快取 void CopyScreenBuffer() { for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++) { buff_Screen[i] = p_Screen[i]; } } // 繪製快取到螢幕 void DrawScreenBuffer() { for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++) { p_Screen[i]=buff_Screen[i]; } } // 輸出路徑連線 void OutPutPath() { if(isShowMazePath) { for(int i=0;i<length[0]-1;i++) { setcolor(C_LINE); int x1=path_x[0][i]*B_WIDTH + B_WIDTH/2 + LX; int x2=path_x[0][i+1]*B_WIDTH + B_WIDTH/2 + LX; int y1=path_y[0][i]*B_WIDTH + B_WIDTH/2 + LY; int y2=path_y[0][i+1]*B_WIDTH + B_WIDTH/2 + LY; line(x1,y1,x2,y2); } } } // 輸出幀數 void DrawFPS() { //輸出幀數 if(isShowFPS) { setcolor(RED); settextstyle(14, 0, _T("宋體")); TCHAR s[5]; #if _MSC_VER > 1200 _stprintf_s(s, _T("%.1f"), GetFPS()); #else _stprintf(s, _T("%.1f"), GetFPS()); #endif outtextxy(4, 2, _T("FPS:")); outtextxy(34, 2, s); } } // 向指定方向移動,返回是否成功移動 int Move(int d,int x,int y) { switch(d) { // 向上 case 0:{ if(Maze[y-3][x]>0 && Maze[y-2][x-1]>0 && Maze[y-2][x+1]>0 && y-2>0) { Maze[y-2][x]=0; Maze[y-1][x]=0; Move_flag=1; temp_d=0; DFS_CreateMaze(x,y-2); } break; } // 向下 case 1:{ if(Maze[y+3][x]>0 && Maze[y+2][x-1]>0 && Maze[y+2][x+1]>0 && y+2<M) { Maze[y+2][x]=0; Maze[y+1][x]=0; Move_flag=1; temp_d=1; DFS_CreateMaze(x,y+2); } break; } // 向左 case 2:{ if(Maze[y][x-3]>0 && Maze[y-1][x-2]>0 && Maze[y+1][x-2]>0 && x-2>0) { Maze[y][x-1]=0; Maze[y][x-2]=0; Move_flag=1; temp_d=2; DFS_CreateMaze(x-2,y); } break; } // 向右 case 3:{ if(Maze[y][x+3]>0 && Maze[y-1][x+2]>0 && Maze[y+1][x+2]>0 && x+2<N) { Maze[y][x+1]=0; Maze[y][x+2]=0; Move_flag=1; temp_d=3; DFS_CreateMaze(x+2,y); } break; } } return Move_flag; }// 深度優先生成迷宮 // 深度優先生成迷宮 void DFS_CreateMaze(int x,int y) { //置通路 Maze[y][x]=0; //隨機方向 int d=rand()%4; int direct=temp_d; int t[4]={0,0,0,0};// 已走方向標記 Move_flag=0;// 重置移動標記 // 保證隨機性的同時走滿地圖 while(t[0]!=1 || t[1]!=1 || t[2]!=1 || t[3]!=1) { int i=rand()%4; if(t[i]!=1) { t[i]=1; Move(i,x,y); } } } // 深度優先搜尋迷宮路徑 void FindPath(int x,int y) { // 置路徑 Maze[y][x]=2; path_x[count][lengtha]=x; path_y[count][lengtha]=y; lengtha++; // 搜尋到目的地 if(x==end_x && y==end_y) { //success=1; length[count] = lengtha; count++; if(count>0) { // 複製上一次的路徑到下一次待搜尋路徑中 for(int i=0;i<length[count-1];i++) { path_x[count][i]=path_x[count-1][i]; path_y[count][i]=path_y[count-1][i]; } } } else { // 搜尋並回溯 if(Maze[y][x+1]==0) {FindPath(x+1,y); Maze[y][x+1]=0;lengtha--;} if(Maze[y+1][x]==0) {FindPath(x,y+1); Maze[y+1][x]=0;lengtha--;} if(Maze[y][x-1]==0) {FindPath(x-1,y); Maze[y][x-1]=0;lengtha--;} if(Maze[y-1][x]==0) {FindPath(x,y-1); Maze[y-1][x]=0;lengtha--;} } //if(success!=1) //return success; } // 建立迷宮 void CreateMaze() { // 初始化迷宮陣列 InitMaze(); isShowMazePath = 0; // 從終點開始逆向生成迷宮 start_x=1; start_y=1; end_x=N-2; end_y=M-2; DFS_CreateMaze(end_x,end_y); // 搜尋路徑 lengtha=0; count=0; FindPath(start_x,start_y); // 結果路徑複製到地圖 for(int i=0;i<length[0];i++) { Maze[path_y[0][i]][path_x[0][i]]=2; } } // 繪製選單按鈕 void DrawMenuItems() { RECT r; r.left=860; r.top=610; r.right=r.left+90; r.bottom=r.top+38; setcolor(RGB(50,50,50)); if(IsInRect(mouseX,mouseY,r)) { setfillcolor(RGB(220,220,220)); // 生成迷宮按鍵響應 if(isMouseDown) { // 建立迷宮 CreateMaze(); // 繪製一次到螢幕並複製到快取 OutputMaze(); CopyScreenBuffer(); isMouseDown = 0; } } else setfillcolor(RGB(240,240,240)); fillrectangle(r.left,r.top,r.right,r.bottom); settextstyle(25, 9, _T("Verdana")); setcolor(RGB(60,60,60)); outtextxy(r.left+5, r.top+5,_T(" 生成迷宮")); r.left=1020; r.top=610; r.right=r.left+90; r.bottom=r.top+38; setcolor(RGB(50,50,50)); if(IsInRect(mouseX,mouseY,r)) { setfillcolor(RGB(220,220,220)); // 顯示/隱藏路徑 if(isMouseDown) { isShowMazePath = !isShowMazePath; isMouseDown = 0; } } else setfillcolor(RGB(240,240,240)); fillrectangle(r.left,r.top,r.right,r.bottom); settextstyle(25, 9, _T("Verdana")); setcolor(RGB(60,60,60)); if(isShowMazePath) outtextxy(r.left+5, r.top+5,_T(" 隱藏路徑")); else outtextxy(r.left+5, r.top+5,_T(" 顯示路徑")); } // 主函式 void main(){ // 置隨機數種子 srand(unsigned(time(NULL))); // 初始化裝置,載入圖片 initgraph(WIN_WIDTH, WIN_HEIGHT); // 設定視窗標題 SetWindowText(GetHWnd(),_T("Maze v0.9 By:ls9512")); cleardevice(); // 設定黑色背景 setbkmode(TRANSPARENT); settextcolor(BLACK); // 開啟雙緩衝,防止閃屏 BeginBatchDraw(); // 滑鼠訊息變數 MOUSEMSG mmsg; // 獲取視訊記憶體指標 p_Screen = GetImageBuffer(); // 建立迷宮 CreateMaze(); // 繪製一次到螢幕並複製到快取 OutputMaze(); CopyScreenBuffer(); // 主迴圈 while(true) { //處理滑鼠訊息 while(MouseHit()) { mmsg = GetMouseMsg(); switch(mmsg.uMsg) { case WM_MOUSEMOVE: mouseX = mmsg.x; mouseY = mmsg.y; break; case WM_LBUTTONDOWN: isMouseDown=true ;break; case WM_LBUTTONUP: isMouseDown=false;break; } } // 繪製,將快取資料複製到螢幕提速 DrawScreenBuffer(); OutPutPath(); DrawMenuItems(); DrawFPS(); // 顯示快取的繪製內容 FlushBatchDraw(); // 延遲,幀數控制 Delay(13); } // 關閉 EndBatchDraw(); closegraph(); }