迷宮生成及深度優先搜索遍歷迷宮(OpenGL)


   其實以前就寫過一個迷宮的程序和DFS遍歷,不過弄丟了,前幾天閑就重寫了一下。歡迎交流和拍磚。有很多不足的地方也希望大家多指正。

  迷宮生成的算法來自《計算機圖形學》,也就是這本書:

  生成迷宮的算法描述如下:

  由於表示牆使用了up_wall和left_wall兩個矩陣,所以格子的數量要比能顯示出來的多一行一列,否則屏幕最下邊和最右邊是沒有牆的。雖然可以后面畫上,不過我選擇這樣。

 

  對於迷宮的遍歷使用DFS,另外由於使用了一個visited矩陣表示每個格子是否已經訪問過,所以即使迷宮里存在環也沒有任何影響,不會死循環。

  

  另外當時不知道glutPostRedisplay()這玩意兒,所以很蠢的把遍歷的過程設成了DisplayFunc來看遍歷的過程。

  生成迷宮及DFS遍歷的代碼如下:

 

  1 #include <iostream>
  2 #include <fstream>
  3 #include <stack>
  4 #include <GL/gl.h>
  5 #include <GL/glu.h>
  6 #include <GL/glut.h>
  7 #include <cstdlib>
  8 using namespace std;
  9 
 10 /*Constant And Structures*/
 11 const int SCREEN_WIDTH = 1200;
 12 const int SCREEN_HEIGHT = 600;
 13 const int BLOCK_SIZE = 10;
 14 
 15 const int BLOCK_VER_NUM = (SCREEN_WIDTH / BLOCK_SIZE) + 1;
 16 const int BLOCK_HOR_NUM = (SCREEN_HEIGHT / BLOCK_SIZE) + 1;
 17 
 18 bool up_wall[BLOCK_VER_NUM][BLOCK_HOR_NUM];
 19 bool left_wall[BLOCK_VER_NUM][BLOCK_HOR_NUM];
 20 bool visited[BLOCK_VER_NUM][BLOCK_HOR_NUM];
 21 
 22 const int wall_size = 1;
 23 
 24 enum Dir
 25 {
 26     _up = 0,
 27     _left = 1,
 28     _right = 2,
 29     _down = 3,
 30     _none = 4
 31 };
 32 const int DIR_SUM = 3;
 33 
 34 struct Pos
 35 {
 36     int x, y;
 37     Pos(int xx, int yy)
 38     {
 39         x = xx;
 40         y = yy;
 41     }
 42     Pos()
 43     {
 44         x = 0;
 45         y = 0;
 46     }
 47     Pos(const Pos& p)
 48     {
 49         x = p.x;
 50         y = p.y;
 51     }
 52     Pos& operator =(const Pos& p)
 53     {
 54         x = p.x;
 55         y = p.y;
 56     }
 57 
 58     bool operator ==(const Pos& p)
 59     {
 60         return (x == p.x) && (y == p.y);
 61     }
 62 };
 63 
 64 class Path : public stack<Pos>
 65 {
 66 public:
 67     Path() : stack<Pos>()
 68     {}
 69     const deque<Pos>& _Get_container()
 70     {
 71         return c;
 72     }
 73 };
 74 
 75 //For Build Puzzle
 76 stack<Pos> pos;
 77 stack<Pos> temp;
 78 
 79 Path path;
 80 Pos entryPuzzle;
 81 Pos entrancePuzzle;
 82 bool arrive;
 83 
 84 
 85 
 86 /*Function*/
 87 void myDisplay()
 88 {
 89     glClear(GL_COLOR_BUFFER_BIT);
 90     glBegin(GL_POINTS);
 91 
 92     for(int i = 0; i < SCREEN_WIDTH; ++i)
 93     {
 94         for(int j = 0; j < SCREEN_HEIGHT; ++j)
 95         {
 96             int bx = i / BLOCK_SIZE;
 97             int by = j / BLOCK_SIZE;
 98           
 99             // //cout << "Opering Cell: " << bx << "," << by << endl;
100             if(up_wall[bx][by])
101             {
102                 if(j % BLOCK_SIZE < wall_size)
103                 {
104                     ////cout << "Put Pix at " << i << "," << j << endl;;
105                     glVertex2d(i, j);
106                 }
107             }
108             if(left_wall[bx][by])
109             {
110                 if(i % BLOCK_SIZE < wall_size)
111                 {
112                     // //cout << "Put Pix at " << i << "," << j << endl;
113                     glVertex2d(i, j);
114                 }
115             }
116         }
117     }
118   
119     glEnd();
120     glFlush();
121     //cout << "Render Over\n";
122 }
123 
124 void DisplayTraverse()
125 {
126     glClear(GL_COLOR_BUFFER_BIT);
127     glBegin(GL_POINTS);
128 
129     glColor3f(0.0f, 0.0f, 0.0f);//Map Color Black
130     //glPointSize(1.0);
131     for(int i = 0; i < SCREEN_WIDTH; ++i)
132     {
133         for(int j = 0; j < SCREEN_HEIGHT; ++j)
134         {
135             int bx = i / BLOCK_SIZE;
136             int by = j / BLOCK_SIZE;
137           
138             // //cout << "Opering Cell: " << bx << "," << by << endl;
139             if(up_wall[bx][by])
140             {
141                 if(j % BLOCK_SIZE < wall_size)
142                 {
143                     ////cout << "Put Pix at " << i << "," << j << endl;;
144                     glVertex2d(i, j);
145                 }
146             }
147             if(left_wall[bx][by])
148             {
149                 if(i % BLOCK_SIZE < wall_size)
150                 {
151                     // //cout << "Put Pix at " << i << "," << j << endl;
152                     glVertex2d(i, j);
153                 }
154             }
155         }
156     }
157     glEnd();
158 
159     glColor3f(0.0f, 0.0f, 1.0f);//Map Color Black
160     //glPointSize(2.0);
161     glBegin(GL_LINE_STRIP);
162 
163     const int off = BLOCK_SIZE / 2;
164 
165     auto dq = path._Get_container();
166     for(auto it : dq)
167     {
168         glVertex2d(it.x * BLOCK_SIZE + off, it.y * BLOCK_SIZE + off);
169     }
170 
171     glEnd();
172   
173     
174     glFlush();
175 }
176 
177 bool canbeDestroy(int x, int y)
178 {
179     //cout << "Detect CanbeDestroy: " << x << "," << y << endl;
180     bool succ = true;
181     if(x < 0 || x >= BLOCK_VER_NUM - 1 || y < 0 || y >= BLOCK_HOR_NUM - 1)
182     {
183         return false;
184     }
185 
186     succ = left_wall[x][y] && up_wall[x][y] && left_wall[x + 1][y] && up_wall[x][y + 1];
187     //if(succ) //cout << "And Succ\n\n";
188     //else //cout << "And False\n\n";
189     return succ;
190 }
191 
192 void Proc(Pos& position)
193 {
194     //cout << "Procing" << position.x << "," << position.y << endl;
195     if(position.x < 0 || position.x >= BLOCK_VER_NUM || position.y < 0 || position.y >= BLOCK_HOR_NUM)
196         return;
197     stack<Pos> temp;
198 
199     //Find Neighbour Who can be access
200     for(int i = 0; i < 4; ++i)
201     {
202         switch(i)
203         {
204         case _up:
205         {
206             if(canbeDestroy(position.x, position.y - 1))
207             {
208                 temp.emplace(position.x, position.y - 1);
209             }
210         }break;
211         case _down:
212         {
213             if(canbeDestroy(position.x, position.y + 1))
214             {
215                 temp.emplace(position.x, position.y + 1);
216             }
217         }break;
218         case _left:
219         {
220             if(canbeDestroy(position.x - 1, position.y))
221             {
222                 temp.emplace(position.x - 1, position.y);
223             }
224         }break;
225         case _right:
226         {
227             if(canbeDestroy(position.x + 1, position.y))
228             {
229                 temp.emplace(position.x + 1, position.y);
230             }
231         }break;
232         default:break;
233         }
234     }
235 
236     if(temp.size() == 0)//No valid target
237         return;
238  
239     //Have valid target, and random choose one
240     int size = temp.size();
241     int tar = rand() % size;
242     //cout << "Select " << tar + 1 << " ops of " << size << endl;
243     Pos next;
244     /*int i = 0;
245     while(!temp.empty())
246     {
247         if(i++ == tar)
248         {
249             next = temp.top();
250             temp.pop();
251         }
252 
253         //if(!visited[temp.top().x][temp.top().y])
254         pos.push(temp.top());
255         temp.pop();
256     }*/
257     for(int i = 0; i < size; ++i)
258     {
259         if(i == tar)
260         {
261             next = temp.top();
262             temp.pop();
263         }
264         else
265         {
266             //if(!visited[temp.top().x][temp.top().y])
267             //pos.push(temp.top());
268             temp.pop();
269         }
270     }
271     pos.push(position);
272     
273     if(next.y == position.y - 1)
274         up_wall[position.x][position.y] = false;
275     else if(next.y == position.y + 1)
276         up_wall[position.x][position.y + 1] = false;
277     else if(next.x == position.x - 1)
278         left_wall[position.x][position.y] = false;
279     else if(next.x == position.x + 1)
280         left_wall[position.x + 1][position.y] = false;
281     else
282     {
283 
284     }
285     
286     //visited[next.x][next.y] = true;
287     //cout << "Move To " << next.x << "," << next.y << endl;
288     //myDisplay();
289     Proc(next);
290 }
291 
292 void MakePuzzle()
293 {
294     while(!pos.empty())
295     {
296         Pos curpos = pos.top();
297         pos.pop();
298         //visited[curpos.x][curpos.y] = true;
299         Proc(curpos);
300     }
301 
302 
303     cout << "MakePuzzle Over.\n";
304 }
305 
306 void myInit()
307 {
308     /*
309     Output Info
310     */
311     //cout << "Screen Size: " << SCREEN_WIDTH << "*" << SCREEN_HEIGHT << endl;
312     //cout << "Block Size: " << BLOCK_SIZE << endl;
313     //cout << "Puzzle Size: " << BLOCK_VER_NUM << "*" << BLOCK_HOR_NUM << endl;
314     //cout << "Wall Size: " << wall_size << endl;
315 
316 
317     glClearColor((float)0x66 / 0x100, (float)0xcc / 0x100, 1.0, 0.0);
318     glColor3f(0.0f, 0.0f, 0.0f);//Map Color Black
319     //glPointSize(1.0);
320     glMatrixMode(GL_PROJECTION);
321     
322     glLoadIdentity();
323     gluOrtho2D(0.0, (GLdouble)SCREEN_WIDTH, 0.0, (GLdouble)SCREEN_HEIGHT);
324     glViewport(0.0, SCREEN_WIDTH, 0.0, SCREEN_HEIGHT);
325   
326     for(int i = 0; i < BLOCK_VER_NUM; ++i)
327     {
328         for(int j = 0; j <  BLOCK_HOR_NUM; ++j)
329         {
330             up_wall[i][j] = true;
331             left_wall[i][j] = true;
332             visited[i][j] = false;
333         }
334     }
335 
336     cout << "Display after init\n";
337     myDisplay();
338 
339     pos.emplace(BLOCK_VER_NUM / 2, BLOCK_HOR_NUM / 2);
340     cout << "myInit Over.\n";
341 }
342 
343 void TraverseDFS(const Pos& p)
344 {
345     cout << "Travering " << p.x << ", " << p.y << endl;
346     path.push(p);
347     visited[p.x][p.y] = true;
348     DisplayTraverse();
349     if(entrancePuzzle == p)
350     {
351         arrive = true;
352         return;
353     }
354 
355     if((p.x > 0) && !left_wall[p.x][p.y] && !visited[p.x - 1][p.y])//left
356     {
357         Pos next(p.x - 1, p.y);
358         TraverseDFS(next);
359         return;
360     }
361     if((p.x < BLOCK_VER_NUM - 1) && !left_wall[p.x + 1][p.y] && !visited[p.x + 1][p.y])//right
362     {
363         Pos next(p.x + 1, p.y);
364         TraverseDFS(next);
365         return;
366     }
367     if((p.y > 0) && !up_wall[p.x][p.y] && !visited[p.x][p.y - 1])//up
368     {
369         Pos next(p.x, p.y - 1);
370         TraverseDFS(next);
371         return;
372     }
373     if((p.y < BLOCK_HOR_NUM - 1) && !up_wall[p.x][p.y + 1] && !visited[p.x][p.y + 1])//down
374     {
375         Pos next(p.x, p.y + 1);
376         TraverseDFS(next);
377         return;
378     }
379 
380     path.pop();
381 }
382 
383 void TraverseInit()
384 {
385     entryPuzzle.x = 0;
386     entryPuzzle.y = rand() % (BLOCK_HOR_NUM - 1);
387     entrancePuzzle.x = BLOCK_VER_NUM - 2;
388     entrancePuzzle.y = rand() % (BLOCK_HOR_NUM - 1);
389 
390     cout << "Generated\nEntry: " << entryPuzzle.x << ", " << entryPuzzle.y << endl;
391     cout << "Entrance: " << entrancePuzzle.x << ", " << entrancePuzzle.y << endl;
392 
393     path.push(entryPuzzle);
394     visited[entryPuzzle.x][entryPuzzle.y] = true;
395     arrive = false;
396 }
397 
398 void Traverse()
399 {
400     while(!path.empty() && !arrive)
401     {
402         Pos& cur = path.top();
403         path.pop();
404         TraverseDFS(cur);
405     }
406     if(arrive)
407         cout << "Arrived.\n";
408     else
409         cout << "Something Wrong, Not arrive.";
410 }
411 
412 int main(int argc, char* argv[])
413 {
414     glutInit(&argc, argv);
415     glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
416     glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
417     glutInitWindowPosition(0, 0);
418     glutCreateWindow("Puzzle");
419     glutDisplayFunc(Traverse);
420   
421     myInit();
422     MakePuzzle();
423     TraverseInit();
424     glutMainLoop();
425 
426     return 0;
427 }

   代碼中myInit函數用來初始化各個矩陣。之后是MakePuzzle構造迷宮。構造迷宮的過程使用了一個棧並且是用Proc函數遞歸調用來生成迷宮的。現在想想其實可以寫成完全的遞歸或者迭代的,不過當時寫着順手就這么寫了。canbeDestroy函數即是判斷一個格子是否有四堵完整的牆,同時考慮了邊界。

  之后就是DFS遍歷了,TraverseInit函數在迷宮最左邊隨機選取一點作為起點,最右邊隨機選擇一點作為終點。然后起點壓棧,進入主循環也就是Traverse函數開始遍歷。Traverse還是使用了遞歸+迭代的方式,不斷遞歸直到進入死路,然后彈棧重新遞歸。

  最后,最上面倆函數myDisplay和DisplayTraverse,前者是用來畫迷宮的,后者則是迷宮+DFS的路徑。另外我畫迷宮是一個像素一個像素去判斷是否在迷宮的牆上,是就畫不是就不畫,這樣其實效率很低,完全可以遍歷每行每列用GL_LINES去畫的。人懶沒葯醫啊。

 

運行情況(由於寬度1200截圖出來顯示效果不好所以我改成了800):

 

  另外至於生成迷宮是叫MakePuzzle,完全是因為我英語不好並且寫這東西的時候完全是在無網絡的情況下所以隨便想了個單詞。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM