深圳幻海软件技术有限公司 欢迎您!

练习时长两年半的扫雷

2023-06-07

目录设计思路游戏运行效果函数的声明头文件game.h游戏主体(源文件)1.game.c2.test.c各文件的阐述各部分设计心得1.打印菜单2.初始化雷池3.打印雷池以及玩家界面打印效果如何改变雷的数量与雷池大小4.生成随机雷5.排雷与对局判断对于越界的看法设计思路1.菜单2.棋盘 需要一

目录

设计思路

游戏运行效果

函数的声明

头文件game.h

游戏主体(源文件)

1.game.c

2.test.c

各文件的阐述

各部分设计心得

1.打印菜单

2.初始化雷池

3.打印雷池以及玩家界面

打印效果

如何改变雷的数量与雷池大小

4.生成随机雷

5.排雷与对局判断

对于越界的看法


设计思路

1.菜单

2.棋盘  需要一个9 * 9 的数组  为了防止越界可以用11 * 11  游戏的实现用11 * 11  玩家看到的则是9 * 9

3.布置雷   非雷 '0'   雷'1'   这里的 零 一 均为字符

4.排查雷  玩家输入坐标  非雷周围一圈的坐标有几个雷就将坐标显示替换为该数

5.判断输赢 全部雷排序除即胜利

游戏运行效果

 

这里就不把全部雷排完了,需要一定时间,待会的心得我会分享一个非常舒服的测试小技巧

函数的声明

头文件game.h

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <stdio.h>
  3. #include<time.h>
  4. #define ROW 9
  5. #define COL 9
  6. #define ROWS ROW+2
  7. #define COLS COL+2
  8. #define EASY_COUNT 10 //雷的个数
  9. //设计思路
  10. //1.选择菜单
  11. //2.棋盘 需要一个9 * 9 的数组 为了越界可以用11 * 11 游戏的实现用11 * 11 玩家看到的则是9 * 9
  12. //3.布置雷 非雷 '0''1' 这里的 零 一 均为字符
  13. //4.排查雷 玩家输入坐标 非雷周围一圈的坐标有几个雷就显示几
  14. //5.判断输赢 全部雷排序除即胜利
  15. //打印菜单
  16. void menu();
  17. //初始化棋盘 1.游戏内部棋盘 2.玩家棋盘
  18. void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch);
  19. //打印棋盘
  20. void DisplayBoard(char board[ROWS][COLS], int row, int col);
  21. //布置雷
  22. void SetMine(char Mine[ROWS][COLS], int row, int col);
  23. //排查雷
  24. void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);
  25. int Find(char Mine[ROWS][COLS], int row, int col);

游戏主体(源文件)

1.game.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "game.h"
  3. void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch)
  4. {
  5. int i = 0;
  6. for (i = 0; i < rows; i++) //注意取值范围 不要越界
  7. {
  8. int j = 0;
  9. for (j = 0; j < cols; j++)
  10. {
  11. board[i][j] = ch;
  12. }
  13. }
  14. }
  15. //注意:需要打印的坐标是x与y的范围是 1-9
  16. //故循环中的 i 与 j 不要初始化为 0 避免后面布置雷的个数一直不对
  17. void DisplayBoard(char board[ROWS][COLS], int row, int col)
  18. {
  19. int x = 0;
  20. printf("------扫雷游戏------\n");
  21. for (x = 0; x <= row; x++)
  22. {
  23. printf("%d ", x);
  24. }
  25. printf("\n");
  26. int i = 1;
  27. int j = 1;
  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. }
  38. //注意:随机数的生成要在循环内部 每次判断前都要生成一次
  39. //否则坐标字符一直为'1' 会出现死循环
  40. void SetMine(char Mine[ROWS][COLS], int row, int col)
  41. {
  42. int count = EASY_COUNT;
  43. while (count)
  44. {
  45. int x = rand() % row + 1;
  46. int y = rand() % row + 1;
  47. if (Mine[x][y] == '0')
  48. {
  49. Mine[x][y] = '1';
  50. count--;
  51. }
  52. }
  53. }
  54. int Find(char Mine[ROWS][COLS], int x, int y)
  55. {
  56. return(Mine[x - 1][y - 1] + Mine[x - 1][y] + Mine[x - 1][y + 1] + Mine[x][y - 1] + Mine[x][y + 1] + Mine[x + 1][y - 1] + Mine[x + 1][y] + Mine[x + 1][y + 1] - 8 * '0');
  57. }
  58. void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
  59. {
  60. int x = 0;
  61. int y = 0;
  62. int win = 0;
  63. char Visited[ROWS][COLS]; //用来存放已经输入过的坐标
  64. while (win < row * col - EASY_COUNT)
  65. {
  66. printf("请输入坐标:>");
  67. scanf("%d %d", &x, &y);
  68. if (x >= 1 && x <= row && y >= 1 && y <= col)
  69. {
  70. if (Mine[x][y] == '1')
  71. {
  72. printf("很遗憾,你被炸死了\n");
  73. DisplayBoard(Mine, ROW, COL);
  74. break;
  75. }
  76. if (Visited[x][y] == '1')
  77. {
  78. printf("该坐标已排查过,请重新输入\n");
  79. continue;
  80. }
  81. else
  82. {
  83. int count = Find(Mine, x, y);
  84. Show[x][y] = count + '0';
  85. DisplayBoard(Show, ROW, COL);
  86. win++;
  87. Visited[x][y] = '1';
  88. }
  89. }
  90. else
  91. {
  92. printf("坐标非法,请重新输入\n");
  93. }
  94. }
  95. if (win == row * col - EASY_COUNT)
  96. {
  97. printf("恭喜你,排雷成功\n");
  98. DisplayBoard(Mine, ROW, COL);
  99. }
  100. }

2.test.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "game.h"
  3. void menu()
  4. {
  5. printf("****************************\n");
  6. printf("******** 1. play ********\n");
  7. printf("******** 0. exit ********\n");
  8. printf("****************************\n");
  9. }
  10. void game()
  11. {
  12. char Mine[ROWS][COLS];//存放布置好的雷
  13. char Show[ROWS][COLS];//存放排查出的雷的信息 即玩家页面
  14. //初始化棋盘
  15. InitBoard(Mine, ROWS, COLS, '0');
  16. InitBoard(Show, ROWS, COLS, '*');
  17. //打印棋盘
  18. DisplayBoard(Show, ROW, COL);
  19. //布置雷
  20. SetMine(Mine, ROW, COL);
  21. DisplayBoard(Mine, ROW, COL);
  22. //排查雷
  23. FindMine(Mine, Show, ROW, COL);
  24. }
  25. int main()
  26. {
  27. int input = 0;
  28. srand((unsigned int)time(NULL));
  29. do
  30. {
  31. menu();
  32. printf("请选择:>");
  33. scanf("%d", &input);
  34. switch (input)
  35. {
  36. case 1:
  37. game();
  38. break;
  39. case 0:
  40. printf("退出游戏\n");
  41. break;
  42. default:
  43. printf("输入错误,请重新输入\n");
  44. break;
  45. }
  46. } while (input);
  47. return 0;
  48. }

各文件的阐述

game.h头文件用来声明函数
game.c游戏功能的实现
test调用game.c的函数,用于运行测试

分文件写代码可以很好的避免被开源

把game.c改为静态库  使用时导入静态库即可  

各部分设计心得

1.打印菜单

封装一个专门打印菜单的函数,后期想要美化菜单直接在函数修改即可,不用在海量的主体代码寻找菜单部分的代码,提高工作效率

2.初始化雷池

玩家所看到的棋盘是 9 * 9 的  为了防止越界 设计时调用的是 11 * 11的二维数组

需要两个雷池  一个是设计游戏的 雷池  一个是玩家所看到的雷池 

即      一个存放雷的信息         一个提供给玩家进行游玩

存放雷池的数组全部初始化为  '0'

(这里的'0'为字符   数组为字符数组)

玩家游玩数组全部初始化为 '*'   

这里的初始化排雷的时候有妙用

  1. void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch)
  2. {
  3. int i = 0;
  4. for (i = 0; i < rows; i++) //注意取值范围 不要越界
  5. {
  6. int j = 0;
  7. for (j = 0; j < cols; j++)
  8. {
  9. board[i][j] = ch;
  10. }
  11. }
  12. }

由于对两个数组进行初始化的符号不同,故可以多增加一个形参接收初始化的字符

3.打印雷池以及玩家界面

封装一个打印函数,需要打印哪个数组就传哪个数组即可

为了方便玩家排雷   在雷池的周围标注行和列  优化游戏体验

  1. void DisplayBoard(char board[ROWS][COLS], int row, int col)
  2. {
  3. int x = 0;
  4. printf("------扫雷游戏------\n");
  5. for (x = 0; x <= row; x++)
  6. {
  7. printf("%d ", x);
  8. }
  9. printf("\n");
  10. int i = 1;
  11. int j = 1;
  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. }

列的打印很简单 用一个循环先打印出来即可

行的打印则需要稍稍注意 在每次打印雷池数据时打印一个数即可

打印效果

如何改变雷的数量与雷池大小

这是就体现了定义的好处了

可以通过修改定义来扩展雷池

//玩家页面

#define ROW 9       
#define COL 9        

//雷池信息

#define ROWS ROW+2
#define COLS COL+2

//雷的个数
#define EASY_COUNT 10     

 改变雷的数量和雷池大小直接从定义更改即可     

上一篇与坤对弈也有提到    性感坤坤在线邀请老铁对弈 (>_<)

4.生成随机雷

这是我们所需要的雷池数组   下标从0开始

画图方便理解   很容易可以看出 

生成的随机雷的坐标范围x,y只需要在1 - 9 

即图中红框内的范围

  1. void SetMine(char Mine[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() % row + 1;
  8. if (Mine[x][y] == '0')
  9. {
  10. Mine[x][y] = '1';
  11. count--;
  12. }
  13. }
  14. }

随机数的生成用rand()函数  需配合srand()使用  调用头文件#include <stdio.h>  

坐标范围是 1 - 9  故 rand() % row + 1 即  0 - 8  左右加一  故为 1 - 9

rand % row =   0   ~  row -1

5.排雷与对局判断

通过分析已知需要排查  row * col - EASY_COUNT  次

//可建立一个新数组来存放已经排查过的坐标
char Visited[ROWS][COLS]
 
 
  1. int Find(char Mine[ROWS][COLS], int x, int y)
  2. {
  3. return(Mine[x - 1][y - 1] + Mine[x - 1][y] + Mine[x - 1][y + 1] + Mine[x][y - 1] + Mine[x][y + 1] + Mine[x + 1][y - 1] + Mine[x + 1][y] + Mine[x + 1][y + 1] - 8 * '0');
  4. }

这个函数计算了输入坐标周围的雷的个数 并返回了字符

数字 3 想变成 字符 '3'  只需减去一个'0' 字符零

ASCALL码    51  -   48  = 3

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

对于越界的看法

数组越界可能不会立即导致程序崩溃或报错。这取决于您使用的编程语言和运行时环境。有些语言和运行时环境会在数组越界时立即报错,而有些则不会。如果您的程序在数组越界时仍能正常运行,但在程序结束时报错,那么这可能是因为您使用的编程语言或运行时环境在程序结束时才检测到并报告了错误。

         ⠰⢷⢿⠄
⠀⠀⠀⠀⠀⣼⣷⣄
⠀⠀⣤⣿⣇⣿⣿⣧⣿⡄
⢴⠾⠋⠀⠀⠻⣿⣷⣿⣿⡀
🏀 ⠀⢀⣿⣿⡿⢿ ⠈⣿
⠀⠀⠀⢠⣿⡿⠁⠀⡊⠀ ⠙
⠀⠀⠀⢿⣿⠀⠀⠹⣿
⠀⠀⠀⠀⠹⣷⡀⠀⣿⡄
⠀⠀⠀⠀⣀⣼⣿⠀⢈⣧                                              我能在这打篮球,你能吗?

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览47725 人正在系统学习中