• C语言:扫雷小游戏


    文接上一篇博文C语言:三子棋小游戏。本篇博文是使用C语言来实现扫雷小游戏的。这里不对扫雷的规则进行赘述。玩家通过键盘输入坐标来探雷。博主在实现扫雷之前从未看过扫雷实现的相关视频,所以这里实现的扫雷完全是博主的原生思路,具有逻辑性。下面详细介绍一下如何实现扫雷。

    (建议在阅读过上一篇博文再阅读本文,因为再本文中对重复的问题不会再次解读。)

    步骤一:制定框架

    框架是什么?如何制定框架?这些问题在三子棋的实现中就已经解答了,这里也不多讲。这里的框架与三子棋的框架完全相同。

    1. #include"detect.h"
    2. int main()
    3. {
    4. srand((unsigned)time(NULL));//这里设置了随机种子,为了之后随机生成雷
    5. int n = 0;
    6. do
    7. {
    8. menu();
    9. printf("your choice:\n");
    10. scanf("%d", &n);
    11. switch (n)
    12. {
    13. case 1:
    14. system("cls");
    15. game();
    16. break;
    17. case 0:
    18. system("cls");
    19. printf("exit game\n");
    20. break;
    21. default:
    22. printf("input error,again:\n");
    23. Sleep(1000);
    24. system("cls");
    25. }
    26. } while (n);
    27. return 0;
    28. }

    menu是什么在三子棋实现中也提过,这里使用了Sleep函数(程序暂停运行多少毫秒)和system("cls")(清屏)是为了更好的游戏体验。这里的detect.h代码如下:

    1. #pragma once
    2. #define ROW 10
    3. #define COL 10
    4. #define _CRT_SECURE_NO_WARNINGS
    5. #include
    6. #include
    7. #include
    8. #include
    9. void menu();
    10. void game();

    步骤二:实现game函数

    跟三子棋的game函数一样,我们需要事先想好整个game的流程。

    分析过程:既然有雷,我们需要将雷的位置记录下来,那我们就需要一个字符二维数组,雷用大写字符'O'表示,雷的位置就初始化为'O',其他位置为空格。另外,开局时我们一个地方都没探测,我们难道要将带雷的数组直接打印上去吗?显然这样雷就直接被打印出来了。我们可以想到用第二个字符二维数组,里面初始时全是空格字符,这个数组是用来打印的。我们在这个数组中进行探测。

    结束条件:如果探的是雷,那么直接宣告失败,结束本次游戏;如果这一次避开了所有的雷,那么直接宣告成功,结束本次游戏;如果这一次没探到雷而且没有结束,那么显示此处旁边雷的个数

    那我们怎么知道探出的是不是雷?其实很简单,在数组一中此处如果是雷,说明这次探到雷了

    下面是game的代码

    1. void game()
    2. {
    3. char map1[ROW][COL] = { 0 };
    4. char map2[ROW][COL] = { 0 };
    5. mapInit(map1, ROW, COL);//真正存储炸弹
    6. mapInit(map2, ROW, COL);//用来打印
    7. BoomInit(map1, ROW, COL);
    8. system("cls");
    9. showMap(map2, ROW, COL);
    10. while (1)
    11. {
    12. int ret=playerMove(map1, map2, ROW, COL,ROW*COL/8);
    13. if (ret==1)//炸了
    14. {
    15. system("cls");
    16. showMap(map1, ROW, COL);
    17. printf("game over\n");
    18. system("pause");
    19. break;
    20. }
    21. else if (ret== 2)//排除了所有炸弹
    22. {
    23. system("cls");
    24. showMap(map1, ROW, COL);//展示所有炸弹位置
    25. printf("detect successfully\n");
    26. Sleep(2000);
    27. break;
    28. }
    29. system("cls");
    30. showMap(map2, ROW, COL);
    31. }
    32. }

    当然只是代码还是很模糊的,下面依然需要对game中的各个部分进行讲解。

    map1:即数组一,用来存放炸弹的

    map2:即数组二,用来打印的

    ROW,COL:宏定义,这个宏定义在"detect.h"中,之前已经给出

    mapInit:用来初始化两个数组,将两个数组的每一个元素变成空格字符

    BoomInit:用来将map1的随机位置放上炸弹

    showMap:将map2打印出来(连带格子的线条,之后会详细实现)

    playerMove:返回值为int类型,玩家在map2中的一个位置进行探测,如果在map1中相应位置是炸弹就返回1,避开所有炸弹就返回2,其他情况返回0;

     步骤三:实现game中的函数

    1. void mapInit(char map[ROW][COL], int row, int col)
    2. {
    3. for (int i = 0; i < row; i++)
    4. {
    5. for (int j = 0; j < col; j++)
    6. {
    7. map[i][j] = ' ';
    8. }
    9. }
    10. }//这个函数很简单,不作讲解
    11. void BoomInit(char map[ROW][COL], int row, int col)
    12. {
    13. int boomNum = row * col / 6;//6分之一是炸弹
    14. //设置炸弹进map
    15. int curNum = 0;
    16. int x = 0;
    17. int y = 0;
    18. while (curNum != boomNum)
    19. {
    20. x = rand() % row;
    21. y = rand() % col;
    22. if (map[x][y] == ' ')//如果是空格才能正常放入,如果已经是炸弹就不放,重新生成一个坐标
    23. {
    24. map[x][y] = 'O';
    25. curNum++;
    26. }
    27. }
    28. }
    29. void showMap(char map[ROW][COL], int row, int col)
    30. {
    31. for (int i = 0; i < row; i++)
    32. {
    33. for (int j = 0; j < col; j++)
    34. {
    35. printf(" %c ", map[i][j]);
    36. if (j < col - 1)
    37. printf("|");
    38. }
    39. printf("\n");
    40. if (i < row - 1)
    41. {
    42. for (int x = 0; x < col; x++)
    43. {
    44. printf("---");
    45. if (x < col - 1)
    46. printf("|");
    47. }
    48. printf("\n");
    49. }//这一段其实是将画格子线和棋子一并画出,
    50. //需要自己动手操作一下才能明白这段代码每一句是在做什么
    51. //不动手再怎么讲都不会弄明白
    52. }
    53. }
    54. //warning函数是为了完成playerMove函数而写的,请先看playerMove函数
    55. char warning(char map[ROW][COL], int row, int col, int x, int y)
    56. {
    57. int countBoom = 0;//最大为8,加上'0'变成字符
    58. for (int i = x - 1; i <= x + 1;i++)
    59. {
    60. for (int j = y - 1; j <= y + 1; j++)
    61. {
    62. if (i >= 0 && i <= row && j >= 0 && j <= col)//越界了就不判断是不是雷
    63. {
    64. if (map[i][j] == 'O')
    65. countBoom++;
    66. }
    67. }
    68. }
    69. return countBoom +'0';
    70. }
    71. int playerMove(char map1[ROW][COL],char map2[ROW][COL], int row, int col,int boomNum)
    72. {
    73. int x = 0;
    74. int y = 0;
    75. static count = 0;//静态局部变量,记录用户一共开了几个格子
    76. while (1)
    77. {
    78. printf("input x:\n");
    79. scanf("%d", &x);
    80. printf("input y:\n");
    81. scanf("%d", &y);
    82. x--;
    83. y--;//用户输入的1就是第一个位置,数组下标就是0
    84. if (x < 0 || x >= row || y < 0 || y >= col || map2[x][y] != ' ')
    85. {
    86. printf("wrong place,again\n");//非法坐标,重新输入
    87. }
    88. else
    89. {
    90. break;//合法,退出循环
    91. }
    92. }
    93. //如果探到雷了,map2的该位置就改成'O',没探到雷就显示旁边有几个雷
    94. //这里的warning函数就是用来给出map1的相应位置旁边有几个雷,返回值是char类型
    95. map2[x][y] = map1[x][y]=='O'?'O':warning(map1,ROW,COL,x,y);
    96. if (map2[x][y] == 'O')
    97. return 1;//探到雷了,返回1
    98. else//判断是否排除完
    99. {
    100. count++;
    101. if (count == col * row - boomNum)
    102. {
    103. count = 0;//归零,下一次进行游戏count还是从0开始计数,否则count还是之前的值
    104. return 2;//避开了所有的雷,返回2
    105. }
    106. }
    107. return 0;//正常进行下一次探测,返回0
    108. }

     到这里为止,所有的工作就完成了,我们来看看效果

    测试:

     配合清屏和睡眠函数效果还是不错的,只是和原版的扫雷少了一个功能。我们知道,原版的扫雷在探到一个格子旁边没有一个炸弹时,也就这里实现的扫雷显示'0'时,会自动将旁边的格子显示出来,只是这个功能实现起来有点困难。当时想过用递归解决这个问题,就是说如果map2中探到一个0雷格,将旁边八个格子打开,如果这八个格子还有0雷格,就会进行递归,继续将旁边的格子打开,只是这样会有一个问题,0雷格挨在一起的话会陷入死递归。个人感觉这个问题使用递归是最好解决的,之后想到了解决方案会更新博文。

  • 相关阅读:
    多线程进阶:常见的锁策略、CAS
    Jmeter二次开发实现rsa加密
    Python接口自动化测试自学路线
    选择KV数据库最重要的是什么
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    【面试题】JS第七种数据类型Symbol详解
    护士人文修养
    链表经典面试题之一讲
    centos虚拟机无法接受消息(防火墙)
    《lwip学习6》-- ARP协议
  • 原文地址:https://blog.csdn.net/2201_75404212/article/details/132691240