小言_互联网的博客

C语言实现一个走迷宫小游戏(深度优先算法)

502人阅读  评论(0)

       转载于我的博客园:https://www.cnblogs.com/xiao-qi-w/p/13031637.html

        接上一篇万年历博文,还是那位朋友的练习题。这次是使用C语言做一个小游戏程序,三选一(2048、8皇后和迷宫游戏),我选择的是迷宫(文章末尾有源码下载链接以及演示视频链接)。个人认为这个程序的难点在于迷宫地图的绘制,也就是怎么建立一个迷宫。如果迷宫地图是在程序里写死的,那可玩性就大大降低了。那么能不能像正常游戏一样生成一个随机地图呢?当然有!在网上查到的结果不外乎这三种:深度优先算法、prim算法和递归分割算法。这三种算法的优劣比较可前往这篇博文一探究竟:

  三大迷宫生成算法 (Maze generation algorithm) -- 深度优先,随机Prim,递归分割

  至于代码实现我参考的是CSDN博主 jjwwwww 的三篇迷宫算法文章的第一篇,全部文章的链接如下:

  1. 随机迷宫生成算法——深度优先算法
  2. 随机迷宫生成算法——prime算法
  3. 随机迷宫生成算法——递归分割算法

  下面来看一下思路和代码:

  维基百科中给出的深度优先(递归回溯)算法思路如下:

  1.将起点作为当前迷宫单元并标记为已访问
  2.当还存在未标记的迷宫单元,进行循环
    1.如果当前迷宫单元有未被访问过的的相邻的迷宫单元
      1.随机选择一个未访问的相邻迷宫单元
      2.将当前迷宫单元入栈
      3.移除当前迷宫单元与相邻迷宫单元的墙
      4.标记相邻迷宫单元并用它作为当前迷宫单元
    2.如果当前迷宫单元不存在未访问的相邻迷宫单元,并且栈不空
      1.栈顶的迷宫单元出栈
      2.令其成为当前迷宫单元

  如果你觉得上面这个表述不太容易理解,那么来看看下面这个思路(还是上面那个博主的,也是我的代码采用的):

  1. 首先假设迷宫只有一条正确的道路。
  2. 假设自己是一只地鼠,要在这个区域不停的挖,直到任何一块区域再挖就会挖穿了为止。
  3. 我们挖的道路就像树结构,树上有很多的分支,分支也有子分支,每个子分支都不能相交,相交了就说明墙被挖穿了,那么此时的迷宫就可能存在多条正确道路,这不是我想看到的。
  4. 那么基于唯一道路的原则,我们向某个方向挖一块新的区域时,要先判断新区域是否有挖穿的可能,如果可能挖穿要立即停止并换个方向再挖。如图:

  

   有了思路,就有了下面的代码,创建迷宫:


  
  1. void CreateMaze(int **maze, int x, int y) { //构建迷宫
  2. maze[x][y] = ROUTE;
  3. //确保四个方向随机,而不再是固定的上下左右这种排列
  4. int direction[ 4][ 2] = { { 1, 0 },{ -1, 0 },{ 0, -1 },{ 0, 1 } };
  5. for ( int i = 0; i < 4; i++) {
  6. int r = rand() % 4;
  7. int temp = direction[ 0][ 0];
  8. direction[ 0][ 0] = direction[r][ 0];
  9. direction[r][ 0] = temp;
  10. temp = direction[ 0][ 1];
  11. direction[ 0][ 1] = direction[r][ 1];
  12. direction[r][ 1] = temp;
  13. }
  14. //向四个方向开挖
  15. for ( int i = 0; i < 4; i++) {
  16. int dx = x;
  17. int dy = y;
  18. //控制挖的距离,由Rank来调整大小
  19. int range = 1 + (Rank == 0 ? 0 : rand() % Rank);
  20. while (range > 0) {
  21. //计算出将要访问到的坐标
  22. dx += direction[i][ 0];
  23. dy += direction[i][ 1];
  24. //排除掉回头路
  25. if (maze[dx][dy] == ROUTE) {
  26. break;
  27. }
  28. //判断是否挖穿路径
  29. int count = 0;
  30. for ( int j = dx - 1; j < dx + 2; j++) {
  31. for ( int k = dy - 1; k < dy + 2; k++) {
  32. //abs(j - dx) + abs(k - dy) == 1 确保只判断九宫格的四个特定位置
  33. if ( abs(j - dx) + abs(k - dy) == 1 && maze[j][k] == ROUTE) {
  34. count++;
  35. }
  36. }
  37. }
  38. //count大于1表明墙体会被挖穿,停止
  39. if (count > 1)
  40. break;
  41. //确保不会挖穿时,前进
  42. range -= 1;
  43. maze[dx][dy] = ROUTE;
  44. }
  45. //没有挖穿危险,以此为节点递归
  46. if (range <= 0) {
  47. CreateMaze(maze, dx, dy);
  48. }
  49. }
  50. }

  当然这样还不够,为了防止挖路时挖出边界,同时为了保护迷宫主体外的一圈墙体被挖穿,我们把最外层全部设为路径。此外还需要设置入口以及寻找出口,如下:


  
  1. int init(int** Maze) { //初始化迷宫
  2. //最外围层设为路径的原因,为了防止挖路时挖出边界,同时为了保护迷宫主体外的一圈墙体被挖穿
  3. for ( int i = 0; i < L; i++) {
  4. Maze[i][ 0] = ROUTE;
  5. Maze[ 0][i] = ROUTE;
  6. Maze[i][L - 1] = ROUTE;
  7. Maze[L - 1][i] = ROUTE;
  8. }
  9. //创造迷宫,(2,2)为起点
  10. CreateMaze(Maze, 2, 2);
  11. //画迷宫的入口和出口,给出玩家初始位置
  12. Maze[ 2][ 1] = PLAYER;
  13. //由于算法随机性,出口有一定概率不在(L-3,L-2)处,此时需要寻找出口
  14. for ( int i = L - 3; i >= 0; i--) {
  15. if (Maze[i][L - 3] == ROUTE) {
  16. Maze[i][L - 2] = ROUTE;
  17. //返回出口所在的纵坐标
  18. return i;
  19. }
  20. }
  21. }

   至此,我们已经能够生成一幅迷宫地图了,下面用函数把它展示出来:


  
  1. void print(int** Maze) { //画迷宫
  2. for ( int i = 0; i < L; i++) {
  3. for ( int j = 0; j < L; j++) {
  4. if (Maze[i][j] == ROUTE)
  5. printf( " "); //表示道路
  6. else if(Maze[i][j] == WALL)
  7. printf( "回"); //表示墙体
  8. else
  9. printf( "十"); //表示玩家
  10. }
  11. printf( "\n");
  12. }
  13. }

  实际绘制效果如下:

   至此我们已经完成80%的工作了,如何互动起来就看我们的输入了。我的想法是利用w,s,a,d四个键作为控制键控制角色 “十” 上下左右移动,当移动到出口处游戏结束。把这个想法转换为代码如下:


  
  1. void start() { //开始一局游戏
  2. char t;
  3. //y,x表示角色横纵坐标, out表示出口的纵坐标
  4. int x = 2, y = 1, out = 0;
  5. //随机数发生器初始化函数
  6. srand(( unsigned)time( NULL));
  7. //申请数组空间
  8. int **Maze = ( int**) malloc(L * sizeof( int *));
  9. for ( int i = 0; i < L; i++) {
  10. Maze[i] = ( int*) calloc(L, sizeof( int));
  11. }
  12. //得到出口纵坐标
  13. out = init(Maze);
  14. //游戏开始
  15. system( "cls");
  16. print(Maze);
  17. while(t = getch()) {
  18. if(t == 27) //如果输入为ESC键,结束游戏回到主菜单
  19. break;
  20. system( "cls"); //清屏
  21. move(Maze, t, x, y); //根据输入t进行移动
  22. print(Maze); //重新绘制迷宫
  23. if(x == out && y == L -2) { //已经到出口,游戏结束
  24. system( "cls");
  25. printf( "=============\n");
  26. printf( "游 戏 胜 利!\n");
  27. printf( "=============\n");
  28. printf( "即将后返回主菜单……");
  29. Sleep( 1500); //执行挂起一段时间,暂停1.5秒后打印
  30. break;
  31. }
  32. }
  33. //一局游戏结束,释放内存
  34. for ( int i = 0; i < L; i++) free(Maze[i]);
  35. free(Maze);
  36. }

  为了游戏体验我使用了getch()这个库函数代替getchar(),好处就是不回显。为了让这个游戏看起来还比较像样,我就把他做成了下面这个样子:

  开始界面:           难度调整界面:

  

  说明界面:

  编辑工具:Dev-C++(版本:5.11.0.0)

  编译器:TDM-GCC 4.9.2 64-bit Release

  源码下载链接:https://download.csdn.net/download/qq_43464624/12489415

  程序演示视频链接:https://www.bilibili.com/video/BV1pz411i7Nz/

  非常感谢您的观看,如果对你有所帮助的话实在是再好不过了。


转载:https://blog.csdn.net/qq_43464624/article/details/106501467
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场