• 扫雷 | C语言 | 简单易懂 | 扫雷相关知识点总结


    扫雷思路

    相信大家都有玩过扫雷吧!其实在我们学习完C语言中函数数组之后,我们就有能力制作一个简单的扫雷小游戏了。

    先考虑扫雷游戏的思路:

    扫雷游戏我们需要利用二维数组将其进行初始化以及赋值“雷”,就以9*9个雷盘来分析,我们要制作一个9*9的雷盘,其中放有10个雷,然后显示“加密”之后的雷盘,接着用户输入想要排查雷的坐标,根据这个坐标:

    如果这个坐标上是雷,那么游戏失败结束游戏;

    如果这个坐标不是雷,那么就显示这个坐标附近的八个坐标有几个雷。

    因为在这个坐标不是雷的情况下,我们要向用户显示,周围雷的个数,如果在同一个雷盘的情况下,我们就不得不将这个周围雷的个数重新赋值给那么坐标上,这样做的话会严重影响这个程序的复杂度,并且很容易出错!

    所以建议大家在写扫雷的程序中,利用两个二维数组,使其中一个是存放雷,而另一个是为玩家展示并且显示相对应的坐标周围有多少个雷。

    在排查雷的过程中,我们要注意的是,排查的是输入坐标的周围八个坐标内一共有几个雷。但是,如果排查的雷是四边的边缘坐标时,访问周围8个坐标,则会产生越界访问。所以为了避免这种情况的发生,我们需要将其行列都应该多设置2个,这样就可以解决越界访问的问题了。

    我们来看一下程序: 

    我们可以得知以下思路

    • 程序打印菜单
    • 玩家选择玩游戏or退出游戏
    • 雷盘初始化
    • 布置雷
    • 排查雷

    我们接下来就根据上面这几个功能进行编写代码。

    代码梳理

    主函数

    我们可以看出在游戏刚刚开始时,也就是程序刚刚开始运行时,他会有一个菜单来显示,询问玩家要进行哪一步程序,那么我们来理一下思路:

    如果用户选择“1”,那么用户就开始游戏

    如果用户选择“0”,那么就是退出程序

    如果用户选择的不是“1”or“0”,那么就是非法输入

    而在这一思路中,有三种情况并且这个选择得一直做到输入正确的数字之后才开始游戏,所以我们可以利用do...while循环语句以及switch分支语句来进行编写程序:

    1. int main()
    2. {
    3. int input = 0;
    4. menu();
    5. do
    6. {
    7. scanf("%d", &input);
    8. switch(input)
    9. {
    10. case 1:
    11. //game();
    12. break;
    13. case 0:
    14. printf("退出游戏!\n");
    15. break;
    16. default:
    17. printf("非法输入!请重新输入!\n");
    18. break;
    19. }
    20. } while (input);
    21. return 0;
    22. }

    do...while语句的使用方法:

    1. do
    2. 循环语句;
    3. while(表达式);

    switch语句的使用方法:

    1. switch(整型表达式)
    2. {
    3. 语句项;
    4. }

    菜单函数

     这个菜单我们可以通过printf函数简单的输出,当然我们为了主函数更加简单,我们可以利用函数(假设这个函数名字是menu),使其主函数更加的便于理解。而我们需要注意的是menu函数只是利用printf函数打印菜单,在这段程序中函数menu并没有返回任何数,也没有使用任何数。其中代码是:

    1. void menu()
    2. {
    3. printf("******************************\n");
    4. printf("*********** 1.play ***********\n");
    5. printf("*********** 0.exit ***********\n");
    6. printf("******************************\n");
    7. }

    数组初始化

    在这之前,我们需要定义一下行列的常量。

    1. #define ROW 9
    2. #define COL 9
    3. #define ROWS ROW+2
    4. #define COLS COL+2

    在雷盘以及显示盘的初始化过程中,两个数组初始化其实代码都是一样的,所以在这里,我们将放置雷的信息的数组命名为MineBoard,将给玩家展示的数组命名为ShowBoard,而这个初始化的函数命名为InitBoard。

    1. void InitBoard(char Board[ROWS][COLS], int rows, int cols,char set)
    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] = set;
    10. }
    11. }
    12. }

    这里参数传入了一个set的作用就是,使其初始化的代码一个就可以完成。

    1. InitBoard(MineBoard,ROWS,COLS,'0');
    2. //雷盘刚刚开始的时候:初始化都为零;最后将雷排为1,将周围八个坐标的值相加,就是雷的个数
    3. InitBoard(ShowBoard,ROWS,COLS,'*');

    布置雷

    时间戳的使用

    1. #include
    2. srand((unsigned)time(NULL));//强制类型转化
    3. int x = 0;
    4. int y = 0;
    5. x = rand() % x索取的范围;
    6. y = rand() % y索取的范围;

    让随机布置10个雷,可以利用时间函数时间戳进行强制类型转换。并且一直随机布置雷,直到10个雷都成功布置,所以我们需要一个while(count)循环,并且定义一个常量EASY_COUNT的值为10。

    #define EASY_COUNT 10

    而在这里我们要注意的有:

    • 数组访问的是11*11的格子,但是在布置雷的过程中,只能是在9*9的范围之内。所以x与y的取值必须是在1~9之间,所以在随机取模之后的结果值再加1。
    • 在布置雷的过程中,必须确保成功布置10个雷,所以要进行判断一下是否这个坐标之前没有被布置过,要确保不能重复布置同一个坐标。
    1. void SetMine(char MineBoard[ROWS][COLS], int row, int col)
    2. {
    3. int count = EASY_COUNT;
    4. while (count)
    5. {
    6. int x = rand() % row + 1;
    7. int y = rand() % col + 1;
    8. if (MineBoard[x][y] == '0')
    9. {
    10. MineBoard[x][y] = '1';
    11. count--;
    12. }
    13. }
    14. }

    排查雷

    在排查雷的过程中,有这么几种情况:

    • 输入的坐标是雷,被“炸死”,游戏输了,游戏结束;
    • 输入的坐标不是雷,显示周围八个坐标一共有几个雷;
    • 输入的坐标重复排查,提醒玩家重新输入;
    • 输入的坐标越界,提醒玩家重新输入;
    • 将所有的雷都排查出来,没有被“炸死”,游戏胜利,游戏结束。

    在这几种情况之中,可以写出以下代码:

    1. FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)
    2. {
    3. int x = 0;
    4. int y = 0;
    5. int win = 0;
    6. while (win < ROW * COL - EASY_COUNT)
    7. {
    8. printf("请输入您想要排查的坐标:\n");
    9. scanf("%d %d", &x, &y);
    10. if (x >= 1 && x <= row && y >= 1 && y <= col)
    11. {
    12. if (ShowBoard[x][y] != '*')
    13. {
    14. printf("该坐标已被查询!请重新输入坐标!");
    15. }
    16. if (MineBoard[x][y] == '1')
    17. {
    18. printf("很遗憾!你被炸死了!\n");
    19. Show(MineBoard, ROW, COL);
    20. break;
    21. }
    22. else
    23. {
    24. int ret = GetMine(MineBoard, x, y);
    25. ShowBoard[x][y] = ret+'0';
    26. Show(ShowBoard, ROW, COL);
    27. win++;
    28. }
    29. }
    30. else
    31. {
    32. printf("非法输入!请重新输入!\n");
    33. }
    34. }
    35. if (win == ROW * COL - EASY_COUNT)
    36. {
    37. printf("恭喜您!排查成功!\n");
    38. Show(MineBoard, ROW, COL);
    39. }
    40. }

    其实这里最难也是最易懂的就是,在排查的坐标不是雷并且显示周围坐标雷的个数的情况。我们在初始化雷盘的时候,我们将不是雷都设置为字符‘0’,把是雷都设置为字符‘1’,而这里排查雷就会有一个简单的小方法,将周围八个坐标的值都加起来,利用ASCII码值,其中字符‘0’的ASCII码值是0,将刚刚加起来的数减掉8个字符‘0’,就可以得到周围雷的个数了。

    1. int GetMine(char MineBoard[ROWS][COLS], int x, int y)
    2. {
    3. return MineBoard[x - 1][y - 1]
    4. + MineBoard[x - 1][y]
    5. + MineBoard[x - 1][y + 1]
    6. + MineBoard[x][y - 1]
    7. + MineBoard[x][y + 1]
    8. + MineBoard[x + 1][y - 1]
    9. + MineBoard[x + 1][y]
    10. + MineBoard[x + 1][y + 1]
    11. - 8*'0';
    12. }

    完整代码

    上面已经游戏所有的思路和代码了,为了方面大家检查复习,我将完整的代码在这附上,以便大家熟悉:

    1. #define ROW 9
    2. #define COL 9
    3. #define ROWS ROW+2
    4. #define COLS COL+2
    5. #define EASY_COUNT 10
    6. #include
    7. #include
    8. #include
    9. void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);
    10. void Show(char Board[ROWS][COLS], int row, int col);
    11. void SetMine(char MineBoard[ROWS][COLS], int row, int col);
    12. FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col);
    13. void InitBoard(char Board[ROWS][COLS], int rows, int cols,char set)
    14. {
    15. int i = 0;
    16. int j = 0;
    17. for (i = 0; i < rows; i++)
    18. {
    19. for (j = 0; j < cols; j++)
    20. {
    21. Board[i][j] = set;
    22. }
    23. }
    24. }
    25. void Show(char Board[ROWS][COLS], int row, int col)
    26. {
    27. int i = 0;
    28. int j = 0;
    29. for (i = 0; i <= col; i++)
    30. {
    31. printf("%d ", i);
    32. }
    33. printf("\n");
    34. for (i = 1; i <= row; i++)
    35. {
    36. printf("%d ",i);
    37. for (j = 1; j <= col; j++)
    38. {
    39. printf("%c ", Board[i][j]);
    40. }
    41. printf("\n");
    42. }
    43. printf("————————扫雷————————\n");
    44. }
    45. void SetMine(char MineBoard[ROWS][COLS], int row, int col)
    46. {
    47. int count = EASY_COUNT;
    48. while (count)
    49. {
    50. int x = rand() % row + 1;
    51. int y = rand() % col + 1;
    52. if (MineBoard[x][y] == '0')
    53. {
    54. MineBoard[x][y] = '1';
    55. count--;
    56. }
    57. }
    58. }
    59. int GetMine(char MineBoard[ROWS][COLS], int x, int y)
    60. {
    61. return MineBoard[x - 1][y - 1]
    62. + MineBoard[x - 1][y]
    63. + MineBoard[x - 1][y + 1]
    64. + MineBoard[x][y - 1]
    65. + MineBoard[x][y + 1]
    66. + MineBoard[x + 1][y - 1]
    67. + MineBoard[x + 1][y]
    68. + MineBoard[x + 1][y + 1]
    69. - 8*'0';
    70. }
    71. FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col)
    72. {
    73. int x = 0;
    74. int y = 0;
    75. int win = 0;
    76. while (win < ROW * COL - EASY_COUNT)
    77. {
    78. printf("请输入您想要排查的坐标:\n");
    79. scanf("%d %d", &x, &y);
    80. if (x >= 1 && x <= row && y >= 1 && y <= col)
    81. {
    82. if (ShowBoard[x][y] != '*')
    83. {
    84. printf("该坐标已被查询!请重新输入坐标!");
    85. }
    86. if (MineBoard[x][y] == '1')
    87. {
    88. printf("很遗憾!你被炸死了!\n");
    89. Show(MineBoard, ROW, COL);
    90. break;
    91. }
    92. else
    93. {
    94. int ret = GetMine(MineBoard, x, y);
    95. ShowBoard[x][y] = ret+'0';
    96. Show(ShowBoard, ROW, COL);
    97. win++;
    98. }
    99. }
    100. else
    101. {
    102. printf("非法输入!请重新输入!\n");
    103. }
    104. }
    105. if (win == ROW * COL - EASY_COUNT)
    106. {
    107. printf("恭喜您!排查成功!\n");
    108. Show(MineBoard, ROW, COL);
    109. }
    110. }
    111. void menu()
    112. {
    113. printf("*********************************\n");
    114. printf("************ 1.play ***********\n");
    115. printf("************ 0.exit ***********\n");
    116. printf("*********************************\n");
    117. }
    118. void play()
    119. {
    120. char MineBoard[ROWS][COLS] = { 0 };
    121. char ShowBoard[ROWS][COLS] = { 0 };
    122. InitBoard(MineBoard,ROWS,COLS,'0');
    123. InitBoard(ShowBoard,ROWS,COLS,'*');
    124. Show(ShowBoard, ROW, COL);
    125. SetMine(MineBoard, ROW, COL);
    126. FindMine(MineBoard, ShowBoard, ROW,COL);
    127. }
    128. int main()
    129. {
    130. srand((unsigned int)time(NULL));
    131. int input = 0;
    132. do
    133. {
    134. menu();
    135. printf("请选择:>\n");
    136. scanf("%d", &input);
    137. switch (input)
    138. {
    139. case 1:
    140. printf("游戏开始!\n");
    141. play();
    142. break;
    143. case 0:
    144. printf("退出游戏!\n");
    145. break;
    146. default:
    147. printf("非法输入!请重新输入!\n");
    148. }
    149. } while (input);
    150. return 0;
    151. }

  • 相关阅读:
    系列四、Nginx的常用命令和配置文件
    Redis哨兵(Sentinel)
    移动NB-IoT卡设置ATU和Active Time缓解无法访问服务器问题
    SpringCloud无介绍快使用,nacos配置中心的基本使用(十九)
    python-opencv 培训课程笔记(1)
    LeetCode刷题笔记之 94. 二叉树的中序遍历(史上最全的二叉树遍历的算法-Java)
    Python 框架学习 Django篇 (十) Redis 缓存
    正确使用 Unicode 和 MBCS 字符集
    低代码助力生产管理:车间管理系统
    Linux系统运维脚本:shell脚本查看一定网段范围在线网络设备的ip地址和不在线的网络设备的数量(查看在线和不在线网络设备)
  • 原文地址:https://blog.csdn.net/2301_78131481/article/details/133546910