迷宫生成及深度优先搜索遍历迷宫(OpenGL)

安稳与你 提交于 2020-03-02 06:43:32

   其实以前就写过一个迷宫的程序和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,完全是因为我英语不好并且写这东西的时候完全是在无网络的情况下所以随便想了个单词。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!