当前位置:   article > 正文

小游戏实现----扫雷 (优化 递归展开、标记雷点的坐标)_扫雷怎么在有雷的方块上做标记

扫雷怎么在有雷的方块上做标记

        今天给大家介绍并用C语言实现一款小游戏——扫雷,相信不少人小时候都玩过,是电脑上一个非常经典的游戏。话不多说,先跟我重温一遍游戏的大致规则吧!


目录

一.游戏规则

二.游戏设计思路

三.main()函数的设计

1.menu函数的实现

2.game()函数的设计

.游戏核心函数的具体实现

1.初始化雷盘

2.打印雷盘

3.布置雷

4.排查雷

(1)获取某一点周围雷的数量

(2)安全的展开一片

(3)标记雷点

 五.游戏完整代码

test.c

game.h

game.c


一.游戏规则

如图所示:该雷盘为9行9列,埋有10个雷。

  1. 游戏规则:
  2. 1.以画方框的9宫格为例,该九宫格中心为数字1,表示其周围8个方格有1个雷,而且其周围只有一个方格
  3. 还没点开,所以很容易判断它左边那个方格就是雷,我们把它标记成小旗子表示已查明此处是雷。
  4. 2.我们每标记一出雷的位置,上图中显示的雷的剩余的数量就会减一,虽然减一,但雷盘中剩余的真正的雷
  5. 数不一定是多少,因为我们判断的不一定正确。
  6. 3.当我们点开所有非雷的空格时,游戏结束。

二.游戏设计思路

采用多文件的形式:

1.test.c          //测试游戏,只包含main(),menu(),game()

2.game.h       //包含头文件及游戏所需函数的声明

3.game.c       //游戏所需函数的实现

三.main()函数的设计

1.menu函数的实现

无论我们玩什么游戏,最先出现的都应是选择菜单界面,该游戏也不例外。

我们设计“menu()”函数来模拟实现玩家最初的选择------play或exit。

menu()具有以下功能:
1.play

2,exit

3.非法输入,重新输入。

并且为了不让游戏只进行一次就结束,应将menu()函数放到循环里,当玩家输入0.exit时游戏才结束游戏。

具体代码如下:

  1. void menu()
  2. {
  3. printf("************************************\n");
  4. printf("******** 1. play *********\n");
  5. printf("******** 0. exit *********\n");
  6. printf("************************************\n");
  7. }
  8. int main()
  9. {
  10. srand((unsigned int)time(NULL));
  11. int input = 0;
  12. do
  13. {
  14. menu();
  15. printf("请选择:>");
  16. scanf("%d", &input);
  17. switch (input)
  18. {
  19. case 1:
  20. game();
  21. break;
  22. case 0:
  23. printf("退出游戏\n");
  24. break;
  25. default:
  26. printf("选择错误,重新选择!\n");
  27. break;
  28. }
  29. } while (input);
  30. return 0;
  31. }

具体运行图示:

2.game()函数的设计

在设计游戏时会使用初始化雷盘,打印雷盘,布置雷,排查雷多个细分的函数,而这些函数组成了一个大的game()函数

在game()函数里,我们需要存放布置好的雷的信息,存放排查出的雷的信息,因此我们需要2个二维数组,并且只用给玩家展示排查出雷的信息的数组。

char mine[ROWS][COLS] = { 0 };//布置好的雷的信息
char show[ROWS][COLS] = { 0 };//排查出的雷的信息

由于在后续的设计里,会多次用到行和列,因此只需在game.h中宏定义ROWSCOLS。

排查坐标的时候,当坐标处在边界位置,为了防止坐标越界,我们给数组的行增加2行,列增加了2列,使得每一个坐标都有属于自己的九宫格,这样排查起来更方便。如下图所示:

四.游戏核心函数的具体实现

1.初始化雷盘

在初始化雷盘的时候,我们将存有雷的信息的数组初始化为字符‘0’(下文会讲到初始化为字符‘0’的好处),将玩家用来操作排雷的数组初始化 为‘ * ’。

  1. //初始化雷盘
  2. void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
  3. {
  4. int i = 0;
  5. for (i = 0; i < rows; i++)
  6. {
  7. int j = 0;
  8. for (j = 0; j < cols; j++)
  9. {
  10. board[i][j] = set;
  11. }
  12. }
  13. }

2.打印雷盘

没啥好说的,直接上代码:

  1. void DisplayBoard(char board[ROWS][COLS], int row, int col)
  2. {
  3. int i = 0;
  4. int j = 0;
  5. printf("---------扫雷----------\n");
  6. //控制列号
  7. for (j = 0; j <= col; j++)
  8. {
  9. printf("%d ", j);
  10. }
  11. printf("\n");
  12. for (i = 1; i <= row; i++)
  13. {
  14. printf("%d ", i);
  15. for (j = 1; j <= col; j++)
  16. {
  17. printf("%c ", board[i][j]);
  18. }
  19. printf("\n");
  20. }
  21. printf("---------扫雷----------\n");
  22. }

 3.布置雷

在布置雷的时候有以下两点需要注意:

1.srand()在main()中调用一次即可

2.判断该位置是否放过雷。

  1. //布置雷
  2. void SetMine(char mine[ROWS][COLS], int row, int col)
  3. {
  4. int count = EASY_COUNT;
  5. while (count)
  6. {
  7. //1. 生成随机下标
  8. int x = rand() % row + 1;
  9. int y = rand() % col + 1;
  10. //2. 布置雷
  11. if (mine[x][y] == '0')
  12. {
  13. mine[x][y] = '1';
  14. count--;
  15. }
  16. }
  17. }

4.排查雷

基本思想是玩家先输入一个坐标,然后判断该点是否有雷。

但是有以下几点需要思考:

1.该点是否被排查过

2.该点是否有雷

3.该点的周围有几个雷?

4.如何以该点为中心,进行区域式的判断并展开?

5.玩家如何标记已经确定的雷点

经过上述分析:单单进行判断有没有雷是不对的,我们玩扫雷的时候会出现一点展开一片的现象,游戏中也能用小旗子对雷进行标记,因此在这一步我们再次进行细分:

(1)获取某一点周围雷的数量

在计算周围雷的数量时,我们可以把它周围的字符全部相加,再减去8个字符‘0’,这样得到的就是周围雷的数量,这就体现了将布置雷数组全部初始化为‘0’的好处。

  1. //计算周围雷的数量
  2. int get_mine_count(char mine[ROWS][COLS], int x, int y)
  3. {
  4. return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
  5. mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
  6. mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
  7. }

(2)安全的展开一片

当玩家点下某一点时,如果该点周围都为有雷,那么将会一次性展开一片,这是非常合理的,如果一个一个的点下去,这将非常影响游戏体验。

以某一点画9宫格,该点周围8个点若都没有雷,则再对这8个点进行同样的排查,因此我们采用递归实现该函数:

  1. //安全的爆炸式展开
  2. void boom_broad(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
  3. {
  4. //判断坐标是否越界
  5. if (x == 0 || y == 0 || x == ROWS - 1 || y == COLS - 1)
  6. return;
  7. //判断是否已经被排除
  8. if (show[x][y] != '*')
  9. {
  10. return;
  11. }
  12. int count = get_mine_count(mine, x, y);
  13. if (count > 0)
  14. {
  15. show[x][y] = count + '0';
  16. return;
  17. }
  18. //递归拓展地图
  19. else if (count == 0)
  20. {
  21. show[x][y] = '0';
  22. boom_broad(mine, show, x - 1, y);
  23. boom_broad(mine, show, x - 1, y - 1);
  24. boom_broad(mine, show, x, y - 1);
  25. boom_broad(mine, show, x + 1, y - 1);
  26. boom_broad(mine, show, x + 1, y);
  27. boom_broad(mine, show, x + 1, y + 1);
  28. boom_broad(mine, show, x, y + 1);
  29. boom_broad(mine, show, x - 1, y + 1);
  30. }
  31. }

(3)标记雷点

如果玩家判断出某一点是雷的话,可以选择标记,标记一次,会提示还剩下几个雷,前面也说过,提示的数量不一定是雷盘上剩余雷的真正数量,因为玩家的判断不一定正确。

  1. //标记雷的位置
  2. void MarkMine(char board[ROWS][COLS], int row, int col)
  3. {
  4. int x = 0;
  5. int y = 0;
  6. while (1)
  7. {
  8. printf("请输入你想要标记位置的坐标->");
  9. scanf("%d %d", &x, &y);
  10. if (x >= 1 && x <= row && y >= 1 && y <= col) //判断该坐标是否合法
  11. {
  12. if (board[x][y] == '*') //判断该坐标是否被排查
  13. {
  14. board[x][y] = '!';
  15. break;
  16. }
  17. else
  18. {
  19. printf("该位置不能被标记,请重新输入!\n");
  20. }
  21. }
  22. else
  23. {
  24. printf("输入错误,请重新输入!\n");
  25. }
  26. }
  27. }
  28. //排查雷
  29. void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
  30. {
  31. int x = 0;
  32. int y = 0;
  33. int win = 0;
  34. char ch;
  35. int sum = 0;
  36. while (win < (row * col - EASY_COUNT))
  37. {
  38. printf("请输入要排查的坐标:>");
  39. scanf("%d %d", &x, &y);
  40. if (x >= 1 && x <= row && y >= 1 && y <= col)
  41. {
  42. if (show[x][y] != '*')
  43. {
  44. printf("该坐标被排查过了\n");
  45. continue;
  46. }
  47. if (mine[x][y] == '1')
  48. {
  49. printf("很遗憾,你被炸死了\n");
  50. DisplayBoard(mine, ROW, COL);
  51. break;
  52. }
  53. else
  54. {
  55. boom_broad(mine, show, x, y);
  56. int n = get_mine_count(mine, x, y);
  57. show[x][y] = n + '0';
  58. DisplayBoard(show, ROW, COL);
  59. win++;
  60. while (1) {
  61. printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
  62. while ((ch = getchar()) != '\n'); //清理缓冲区
  63. scanf("%c", &ch);
  64. if (ch == 'Y' || ch == 'y')
  65. {
  66. MarkMine(show, row, col); //标记雷
  67. sum++;
  68. DisplayBoard(show, ROW, COL);
  69. printf("还剩%d个雷!\n", EASY_COUNT - sum);
  70. }
  71. else
  72. {
  73. break;
  74. }
  75. }
  76. }
  77. }
  78. else
  79. {
  80. printf("坐标非法,重新输入\n");
  81. }
  82. }
  83. if (win == (row * col - EASY_COUNT))
  84. {
  85. printf("恭喜你,排雷成功\n");
  86. DisplayBoard(mine, ROW, COL);
  87. }
  88. }

 五.游戏完整代码

test.c

  1. //test.c
  2. #define _CRT_SECURE_NO_WARNINGS 1
  3. #include "game.h"
  4. void menu()
  5. {
  6. printf("************************************\n");
  7. printf("******** 1. play *********\n");
  8. printf("******** 0. exit *********\n");
  9. printf("************************************\n");
  10. }
  11. void game()
  12. {
  13. //1. 需要存放布置好的雷的信息,存放排查出的雷的信息,我们需要2个二维数组
  14. //2. 排查坐标的时候,为了防止坐标越界,我们给数组的行增加2行,列增加了2
  15. char mine[ROWS][COLS] = { 0 };//布置好的雷的信息
  16. char show[ROWS][COLS] = { 0 };//排查出的雷的信息
  17. //初始化雷盘
  18. InitBoard(mine, ROWS, COLS, '0');
  19. InitBoard(show, ROWS, COLS, '*');
  20. //打印雷盘
  21. DisplayBoard(show, ROW, COL);
  22. //布置雷
  23. SetMine(mine, ROW, COL);
  24. //DisplayBoard(mine, ROW, COL);
  25. //排查雷
  26. FindMine(mine, show, ROW, COL);
  27. }
  28. int main()
  29. {
  30. srand((unsigned int)time(NULL));
  31. int input = 0;
  32. do
  33. {
  34. menu();
  35. printf("请选择:>");
  36. scanf("%d", &input);
  37. switch (input)
  38. {
  39. case 1:
  40. game();
  41. break;
  42. case 0:
  43. printf("退出游戏\n");
  44. break;
  45. default:
  46. printf("选择错误,重新选择!\n");
  47. break;
  48. }
  49. } while (input);
  50. return 0;
  51. }

game.h

  1. //game.h
  2. #pragma once
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <time.h>
  6. #define ROW 9
  7. #define COL 9
  8. #define ROWS ROW+2
  9. #define COLS COL+2
  10. #define EASY_COUNT 10
  11. //初始化雷盘
  12. void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
  13. //打印雷盘
  14. void DisplayBoard(char board[ROWS][COLS], int row, int col);
  15. //布置雷
  16. void SetMine(char mine[ROWS][COLS], int row, int col);
  17. //排查雷
  18. void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "game.h"
  3. //初始化雷盘
  4. void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
  5. {
  6. int i = 0;
  7. for (i = 0; i < rows; i++)
  8. {
  9. int j = 0;
  10. for (j = 0; j < cols; j++)
  11. {
  12. board[i][j] = set;
  13. }
  14. }
  15. }
  16. //打印雷盘
  17. void DisplayBoard(char board[ROWS][COLS], int row, int col)
  18. {
  19. int i = 0;
  20. int j = 0;
  21. printf("---------扫雷----------\n");
  22. //控制列号
  23. for (j = 0; j <= col; j++)
  24. {
  25. printf("%d ", j);
  26. }
  27. printf("\n");
  28. for (i = 1; i <= row; i++)
  29. {
  30. printf("%d ", i);
  31. for (j = 1; j <= col; j++)
  32. {
  33. printf("%c ", board[i][j]);
  34. }
  35. printf("\n");
  36. }
  37. printf("---------扫雷----------\n");
  38. }
  39. //布置雷
  40. void SetMine(char mine[ROWS][COLS], int row, int col)
  41. {
  42. int count = EASY_COUNT;
  43. while (count)
  44. {
  45. //1. 生成随机下标
  46. int x = rand() % row + 1;
  47. int y = rand() % col + 1;
  48. //2. 布置雷
  49. if (mine[x][y] == '0')
  50. {
  51. mine[x][y] = '1';
  52. count--;
  53. }
  54. }
  55. }
  56. //标记雷的位置
  57. void MarkMine(char board[ROWS][COLS], int row, int col)
  58. {
  59. int x = 0;
  60. int y = 0;
  61. while (1)
  62. {
  63. printf("请输入你想要标记位置的坐标->");
  64. scanf("%d %d", &x, &y);
  65. if (x >= 1 && x <= row && y >= 1 && y <= col) //判断该坐标是否合法
  66. {
  67. if (board[x][y] == '*') //判断该坐标是否被排查
  68. {
  69. board[x][y] = '!';
  70. break;
  71. }
  72. else
  73. {
  74. printf("该位置不能被标记,请重新输入!\n");
  75. }
  76. }
  77. else
  78. {
  79. printf("输入错误,请重新输入!\n");
  80. }
  81. }
  82. }
  83. //计算周围雷的数量
  84. int get_mine_count(char mine[ROWS][COLS], int x, int y)
  85. {
  86. return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
  87. mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
  88. mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');
  89. }
  90. //安全的爆炸式展开
  91. void boom_broad(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
  92. {
  93. //判断坐标是否越界
  94. if (x == 0 || y == 0 || x == ROWS - 1 || y == COLS - 1)
  95. return;
  96. //判断是否已经被排除
  97. if (show[x][y] != '*')
  98. {
  99. return;
  100. }
  101. int count = get_mine_count(mine, x, y);
  102. if (count > 0)
  103. {
  104. show[x][y] = count + '0';
  105. return;
  106. }
  107. //递归拓展地图
  108. else if (count == 0)
  109. {
  110. show[x][y] = '0';
  111. boom_broad(mine, show, x - 1, y);
  112. boom_broad(mine, show, x - 1, y - 1);
  113. boom_broad(mine, show, x, y - 1);
  114. boom_broad(mine, show, x + 1, y - 1);
  115. boom_broad(mine, show, x + 1, y);
  116. boom_broad(mine, show, x + 1, y + 1);
  117. boom_broad(mine, show, x, y + 1);
  118. boom_broad(mine, show, x - 1, y + 1);
  119. }
  120. }
  121. //排查雷
  122. void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
  123. {
  124. int x = 0;
  125. int y = 0;
  126. int win = 0;
  127. char ch;
  128. int sum = 0;
  129. while (win < (row * col - EASY_COUNT))
  130. {
  131. printf("请输入要排查的坐标:>");
  132. scanf("%d %d", &x, &y);
  133. if (x >= 1 && x <= row && y >= 1 && y <= col)
  134. {
  135. if (show[x][y] != '*')
  136. {
  137. printf("该坐标被排查过了\n");
  138. continue;
  139. }
  140. if (mine[x][y] == '1')
  141. {
  142. printf("很遗憾,你被炸死了\n");
  143. DisplayBoard(mine, ROW, COL);
  144. break;
  145. }
  146. else
  147. {
  148. boom_broad(mine, show, x, y);
  149. int n = get_mine_count(mine, x, y);
  150. show[x][y] = n + '0';
  151. DisplayBoard(show, ROW, COL);
  152. win++;
  153. while (1) {
  154. printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
  155. while ((ch = getchar()) != '\n'); //清理缓冲区
  156. scanf("%c", &ch);
  157. if (ch == 'Y' || ch == 'y')
  158. {
  159. MarkMine(show, row, col); //标记雷
  160. sum++;
  161. DisplayBoard(show, ROW, COL);
  162. printf("还剩%d个雷!\n", EASY_COUNT - sum);
  163. }
  164. else
  165. {
  166. break;
  167. }
  168. }
  169. }
  170. }
  171. else
  172. {
  173. printf("坐标非法,重新输入\n");
  174. }
  175. }
  176. if (win == (row * col - EASY_COUNT))
  177. {
  178. printf("恭喜你,排雷成功\n");
  179. DisplayBoard(mine, ROW, COL);
  180. }
  181. }

以上就是实现扫雷的全部内容了,谢谢您的观看!


码文不易,还请多多支持哦~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/748750
推荐阅读
相关标签
  

闽ICP备14008679号