经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
c语言实现含递归清场版扫雷游戏
来源:jb51  时间:2021/11/22 8:34:28  对本文有异议

一,设计思路

想必大家都玩过扫雷

?这便是一个标准的扫雷,换做代码实现,我们需要考虑以下几点:

1.棋盘的设计与初始化

2.在棋盘中放入雷

3.统计雷数

4.如何实现“一片”的效果

5.输赢的判断

接下来我们进行具体操作。

二.实现方式

1.菜单的打印

对任意一个游戏,菜单是必不可少的,也是最简单的部分,直接上代码

  1. void menu()
  2. {
  3. printf("------------------扫雷------------------\n");
  4. printf("---------------1.开始游戏---------------\n");
  5. printf("---------------0.退出游戏---------------\n");
  6. }
  7. int main()
  8. {
  9. srand((unsigned int)time(NULL));
  10. int a = 0;
  11. do
  12. {
  13. menu();
  14. scanf("%d", &a);
  15. if (a == 0)
  16. {
  17. break;
  18. }
  19. game();
  20. } while (a);
  21. return 0;
  22. }

其中srand是为了求随机值,用来布置雷

2.game函数

主菜单完后进入game函数,在game函数里我们就要开始主要的游戏部分,如棋盘初始化与打印等,当然这些步骤都依靠函数完成,game函数只相当于集合了一系列的游戏模块

  1. void game()
  2. {
  3. char mine[ROWS][COLS];
  4. char show[ROWS][COLS];
  5. initeboard(mine, ROWS, COLS, '0');
  6. initeboard(show, ROWS, COLS, '*');//初始棋盘
  7. displayboard(show, ROW, COL);//打印棋盘
  8. mine_make(mine, ROW, COL);//设置雷
  9. //displayboard(mine, ROW, COL);
  10. find_mine(mine, show, ROWS, COLS);//排查雷
  11. }

3.棋盘的初始化与打印

可以看到,我在game函数里初始了两个棋盘,为什么要两个呢?

其实,一个是为了放置了雷,一个为了展示给玩家,为什么不用一个呢,我们要在放雷处用'1',表示不放雷处用‘0',这样计算一个坐标周围雷的数量就会更简单,接下来看一段代码:

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #define ROW 10
  6. #define COL 10
  7. #define ROWS ROW+2
  8. #define COLS COL+2

这里我们看到定义了一个ROWS和COLS这又是为何呢?

回归棋盘

?扫雷时当你确认一个点时,它会对此点周围的八个点进行排查看是否有泪,当坐标位于红线处时没有无法判断八个,因此有了ROWS与COLS,这时,有人就会问了:那埋雷的地方用ROW COL就可以了,还不用担心雷跑到外边我们加的框,想法很好,但我们有两个棋盘,必须对应,而且也不存在跑出雷的情况,往下分析你就会知道

  1. void initeboard(char board[ROWS][COLS], int cols, int rows,char s)//棋盘初始化,此处用了个节省步骤的方法,不用两个棋盘依次初始化
  2. {
  3. int i = 0;
  4. int j = 0;
  5. for (i = 0; i < rows; i++)
  6. {
  7. for (j = 0; j < cols; j++)
  8. {
  9. board[i][j] = s;
  10. }
  11. }
  12. }
  13. void displayboard(char board[ROWS][COLS], int row, int col)//棋盘打印
  14. {
  15. int i = 0;
  16. int j = 0;
  17. printf(" ");//与行中打印的0%d对应
  18. for (j = 0; j <=col; j++)
  19. {
  20. printf(" %d ", j);
  21. }
  22. printf("\n");
  23. printf("-");
  24. for (j = 1; j <= col + 1; j++)
  25. {
  26. printf("---");
  27. printf("|");
  28. }
  29. printf("\n");//列对应打印完成
  30. for (i = 1; i <= row; i++)
  31. {
  32. if (i <= 9)
  33. {
  34. printf(" 0%d ", i);
  35. }
  36. else
  37. printf(" %d ", i);
  38. printf("|");
  39. for (j = 1; j <= col; j++)
  40. {
  41. printf(" %c ", board[i][j]);
  42. printf("|");
  43. }
  44. printf("\n");
  45. printf("-");
  46. for (j = 1; j <= col+1; j++)
  47. {
  48. printf("---");
  49. printf("|");
  50. }
  51. printf("\n");//行对应嵌套在内部 打印的数前加0是为了让当行数大于9时能够对应 如09与10
  52. }
  53. }

棋盘的打印做了行列对应,所以可能有点不明显

?4.雷的放置,雷的个数

雷的放置很简单,在棋盘‘0',处放置就行,因为我们对随机数的算式使随机数只会出现在0-9没有跑出的情况

  1. mine_make(char mine[ROWS][COLS], int row, int col)//设置雷
  2. {
  3. int count = 10;
  4. while (count)
  5. {
  6. int x = rand() % 10;
  7. int y = rand() % 10;
  8. if (mine[x][y] == '0')
  9. {
  10. mine[x][y] = '1';
  11. count--;
  12. }
  13. }
  14. }

雷的个数就需要一定考虑了:

首先,我们需要把周围八个位置是否有雷,有多少个雷判断出来,这时我们设计‘0' ‘1'类型就排上用场了,但是注意,这是字符,不是数字

ASCII码表

?一张表 让我们知道如何转型

当然返回的数字记得转成字符类型

  1. int get_mine(char mine[ROWS][COLS], int i, int j)//得到一个坐标附近雷的个数
  2. {
  3. return mine[i - 1][j - 1] + mine[i - 1][j] + mine[i - 1][j + 1] +
  4. mine[i][j - 1] + mine[i][j + 1] +
  5. mine[i + 1][j - 1] + mine[i +1 ][j] + mine[i + 1][j + 1] - 8 * '0';//此处我们的数字其实是字符,用此方法可以转为数字整形
  6. }

5.递归实现一片效果

当不为雷是会展开附近一片不为雷,提高游戏效率

  1. void spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)//递归方法实现一大片
  2. {
  3. show[x][y] = ' ';//先让输入金的坐标处变为空格,因为已经判定过雷所以可以直接转空格
  4. int i = 0;
  5. int j = 0;
  6. int ret = 0;
  7. for (i = x - 1; i <= x + 1; i++)
  8. {
  9. for (j = y - 1; j <= y + 1; j++)//嵌套for循环表示输入坐标包括自生及周围八个
  10. {
  11. if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')//防止出现负坐标,避免有雷,避免输入重复
  12. {
  13. ret = get_mine(mine, i, j);//判断ret是0或非0,并得出周围雷数
  14. if (!ret)//如果ret=0,!ret便是非0;为真
  15. {
  16. spread(show, mine, i, j);//递归
  17. }
  18. if (ret)//ret!=0时为真,便打印数
  19. {
  20. show[i][j] = ret + '0';//使数字转成对应字符
  21. }
  22. }
  23. }
  24. }
  25. }

效果展示:

?6.排查雷

负责判断是否被炸死及游戏胜利

  1. void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查雷
  2. {
  3. printf("请输入坐标\n");
  4. int i = 0;
  5. int j = 0;
  6. int win = 0;
  7. while (row*col-10)//因为是十个雷,所以是—10,因为下总数减10就赢了,所以可以以此跳出循环,当然要是雷部位10,定一个变量就行,此处就不改了
  8. {
  9. scanf("%d %d", &i, &j);
  10. if (mine[i][j] == '1')
  11. {
  12. printf("你挂了\n");
  13. displayboard(mine, ROW, COL);
  14. break;
  15. }
  16. else
  17. {
  18. show[i][j] = get_mine(mine, i, j)+'0';
  19. spread(show,mine, i, j);
  20. displayboard(show, ROW, COL);
  21. win++;
  22. }
  23. }
  24. if (win ==row * col - 10)
  25. {
  26. printf("恭喜你,成功了\n");
  27. displayboard(mine, ROW, COL);
  28. }
  29. }

具体注释都在代码里了

正常情况把打印雷盘注释就行,就可以正常游戏了

?三.完整代码

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <time.h>
  5. #define ROW 10
  6. #define COL 10
  7. #define ROWS ROW+2
  8. #define COLS COL+2
  9. void menu()
  10. {
  11. printf("------------------扫雷------------------\n");
  12. printf("---------------1.开始游戏---------------\n");
  13. printf("---------------0.退出游戏---------------\n");
  14. }
  15. void initeboard(char board[ROWS][COLS], int cols, int rows,char s)//棋盘初始化,此处用了个节省步骤的方法,不用两个棋盘依次初始化
  16. {
  17. int i = 0;
  18. int j = 0;
  19. for (i = 0; i < rows; i++)
  20. {
  21. for (j = 0; j < cols; j++)
  22. {
  23. board[i][j] = s;
  24. }
  25. }
  26. }
  27. void displayboard(char board[ROWS][COLS], int row, int col)//棋盘打印
  28. {
  29. int i = 0;
  30. int j = 0;
  31. printf(" ");//与行中打印的0%d对应
  32. for (j = 0; j <=col; j++)
  33. {
  34. printf(" %d ", j);
  35. }
  36. printf("\n");
  37. printf("-");
  38. for (j = 1; j <= col + 1; j++)
  39. {
  40. printf("---");
  41. printf("|");
  42. }
  43. printf("\n");//列对应打印完成
  44. for (i = 1; i <= row; i++)
  45. {
  46. if (i <= 9)
  47. {
  48. printf(" 0%d ", i);
  49. }
  50. else
  51. printf(" %d ", i);
  52. printf("|");
  53. for (j = 1; j <= col; j++)
  54. {
  55. printf(" %c ", board[i][j]);
  56. printf("|");
  57. }
  58. printf("\n");
  59. printf("-");
  60. for (j = 1; j <= col+1; j++)
  61. {
  62. printf("---");
  63. printf("|");
  64. }
  65. printf("\n");//行对应嵌套在内部 打印的数前加0是为了让当行数大于9时能够对应 如09与10
  66. }
  67. }
  68. mine_make(char mine[ROWS][COLS], int row, int col)//设置雷
  69. {
  70. int count = 10;
  71. while (count)
  72. {
  73. int x = rand() % 10;
  74. int y = rand() % 10;
  75. if (mine[x][y] == '0')
  76. {
  77. mine[x][y] = '1';
  78. count--;
  79. }
  80. }
  81. }
  82. int get_mine(char mine[ROWS][COLS], int i, int j)//得到一个坐标附近雷的个数
  83. {
  84. return mine[i - 1][j - 1] + mine[i - 1][j] + mine[i - 1][j + 1] +
  85. mine[i][j - 1] + mine[i][j + 1] +
  86. mine[i + 1][j - 1] + mine[i +1 ][j] + mine[i + 1][j + 1] - 8 * '0';//此处我们的数字其实是字符,用此方法可以转为数字整形
  87. }
  88. void spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)//递归方法实现一大片
  89. {
  90. show[x][y] = ' ';//先让输入金的坐标处变为空格,因为已经判定过雷所以可以直接转空格
  91. int i = 0;
  92. int j = 0;
  93. int ret = 0;
  94. for (i = x - 1; i <= x + 1; i++)
  95. {
  96. for (j = y - 1; j <= y + 1; j++)//嵌套for循环表示输入坐标包括自生及周围八个
  97. {
  98. if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')//防止出现负坐标,避免有雷,避免输入重复
  99. {
  100. ret = get_mine(mine, i, j);//判断ret是0或非0,并得出周围雷数
  101. if (!ret)//如果ret=0,!ret便是非0;为真
  102. {
  103. spread(show, mine, i, j);//递归
  104. }
  105. if (ret)//ret!=0时为真,便打印数
  106. {
  107. show[i][j] = ret + '0';//使数字转成对应字符
  108. }
  109. }
  110. }
  111. }
  112. }
  113. void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查雷
  114. {
  115. printf("请输入坐标\n");
  116. int i = 0;
  117. int j = 0;
  118. int win = 0;
  119. while (row*col-10)//因为是十个雷,所以是—10,因为下总数减10就赢了,所以可以以此跳出循环,当然要是雷部位10,定一个变量就行,此处就不改了
  120. {
  121. scanf("%d %d", &i, &j);
  122. if (mine[i][j] == '1')
  123. {
  124. printf("你挂了\n");
  125. displayboard(mine, ROW, COL);
  126. break;
  127. }
  128. else
  129. {
  130. show[i][j] = get_mine(mine, i, j)+'0';
  131. spread(show,mine, i, j);
  132. displayboard(show, ROW, COL);
  133. win++;
  134. }
  135. }
  136. if (win ==row * col - 10)
  137. {
  138. printf("恭喜你,成功了\n");
  139. displayboard(mine, ROW, COL);
  140. }
  141. }
  142. void game()
  143. {
  144. char mine[ROWS][COLS];
  145. char show[ROWS][COLS];
  146. initeboard(mine, ROWS, COLS, '0');
  147. initeboard(show, ROWS, COLS, '*');//初始棋盘
  148. displayboard(show, ROW, COL);//打印棋盘
  149. mine_make(mine, ROW, COL);//设置雷
  150. displayboard(mine, ROW, COL);
  151. find_mine(mine, show, ROWS, COLS);//排查雷
  152. }
  153. int main()
  154. {
  155. srand((unsigned int)time(NULL));
  156. int a = 0;
  157. do
  158. {
  159. menu();
  160. scanf("%d", &a);
  161. if (a == 0)
  162. {
  163. break;
  164. }
  165. game();
  166. } while (a);
  167. return 0;
  168. }

总结

到此这篇关于c语言实现含递归清场版扫雷游戏的文章就介绍到这了,更多相关c语言实现扫雷内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号