一.扫雷游戏的分析和设计
1.扫雷游戏的功能说明
- 使用控制台实现经典的扫雷游戏
- 游戏可以通过菜单实现继续玩或者退出游戏
- 扫雷的棋盘是9*9的格子
- 默认随机布置10个雷
- 可以排查雷
- 如果位置不是雷,就显示周围有几个雷
- 如果位置是雷,就炸死,游戏结束
- 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束
2.数据结构的分析
扫雷过程中,布置的雷和排查出的雷的信息都需要储存,所以我们需要一定的数据结构来存储这些信息。
2.1.文件结构的设计
text.c//写游戏的测试逻辑 game.c//写游戏中函数的实现等 game.h//写游戏需要的数据结构类型和函数声明等
2.2 为什么需要创建11*11的数组?
完成一个9*9的扫雷游戏,首先想到的是创建一个9*9的数组。但这里,还需要创建11*11的数组。理由如下:排查雷时,若排查的位置不是雷,就显示周围一圈(8个位置)有几个雷。而当该位置位于坐标的边界时,则会出现越界的情况。为了防止越界,在设计时,给数组扩大一圈,雷还是布置在9*9的坐标上,周围一圈不去布置雷,这样就解决了越界问题。
2.3 为什么要两个棋盘来存放信息?
在棋盘上布置雷,符号‘1’为雷,符合‘0’为非雷。当我们排查雷时,若某位置不是雷,会返回该位置周围有几个雷,若有一个雷,则返回数字1。此时,若返回的数字1与符号‘1’(雷)在同一个棋盘上,玩家则无法区分它是数字1,还是符号‘1’。因此,需要两个棋盘来存放信息。
因此,需要在game.h文件中,定义行,列和10个雷,来让这3个文件都使用。只有其他两个文件都写上#include"game.h",在game.h文件中声明函数和数据结构类型都通用。
#include<stdio.h>其余两个文件就不用加这个头文件了 #define ROW 9//行 #define COL 9//列 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10//10个雷
二.扫雷游戏的代码实现
设计一个扫雷游戏需要初始化棋盘,打印棋盘,布置雷,排查雷。
首先,建立text.c文件,通过菜单实现继续玩或者退出游戏,使用do while循环实现先选择后判断
(如果不太了解函数传参,可以看博主的这篇文章https://blog.csdn.net/wait___wait/article/details/135047014?spm=1001.2014.3001.5502ss)
(switch语句有些遗忘的,也可以看看博主这篇文章https://blog.csdn.net/wait___wait/article/details/134805824?spm=1001.2014.3001.5501)
main函数
#include"game.h"//自己定义的头文件用“” void menu() { printf("*******************\n"); printf("******1.paly*******\n"); printf("******0.exit*******\n"); printf("*******************\n"); } int main() { int input = 0; do { menu(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case 1: { game(); break; } case 0: { printf("退出游戏\n"); break; } default: { printf("输入不符合规定,请重新选择\n"); break; } } }while(input); return 0; }
上述代码中涉及game()函数,因而在text.c(游戏的测试逻辑)这个文件中要进行定义。
根据对该游戏的分析和设计,知,game()中要创建两个数组来存放布置好的雷,和排查出雷的信息。
char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出雷的信息
初始化棋盘
text.c
数组是11*11,所需棋盘是9*9
//初始化棋盘 ,11*11数组 InitBoard(show, ROWS, COLS, '*');//show数组最开始全是‘*’ InitBoard(mine, ROWS, COLS, '0');//mine数组最开始全是‘0’
game.c
//初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 1; j < cols; j++)//j若初始化为0,后面排查雷时,输入的坐标在棋盘上输出的坐标会不一样 { board[i][j] = set; } } }
game.h
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘
运行示例图
打印棋盘
text.c
show数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9
//打印棋盘 PlayBoard(show, ROW, COL); //PlayBoard(mine, ROW, COL);//检查‘0’是否设置好
game.h
void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘
game.c
//打印棋盘 void PlayBoard(char board[ROWS][COLS], int row, int col) { printf("------扫雷游戏-------\n"); for (int i = 0; i <= col; i++)//打印列号0-9 { printf("%d|", i); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d|", i);//打印行号1-9 for (int j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
运行示例图
布置雷
text.c
由于布置雷时需要产生随机数,就要用到rand函数。又因为调用rand函数之前要先调用srand函数,所以在该文件的main函数中使用srand函数,
srand((unsigned int)time(NULL));
//布置雷 SetMine(mine, ROW, COL); //PlayBoard(mine, ROW, COL);//检查是否成功设置雷
game.h
#include<stdlib.h>//srand,rand的头文件 #include<time.h>//time的头文件 void SetMine(char board[ROWS][COLS], int row, int col);//布置雷
game.c
//布置雷 void SetMine(char board[ROWS][COLS], int row, int col) { int x = 0, y = 0; int count = EASY_COUNT; while (count) { x=rand() % row + 1;//生成1-9的随机数 y=rand() % row + 1;//生成1-9的随机数 if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10 { board[x][y] = '1'; count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环 } } }
运行实例,显示雷的位置需要把text.c文件中的PlayBoard取消注销
排雷
text.c
FindMine(show, mine, ROW, COL);
game.h
void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷
game.c
//排雷 //如果位置不是雷,就显示周围有几个雷 //如果位置是雷,就炸死,游戏结束 //把十个雷之外的所有雷都找出来,排雷成功,游戏结束 void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0, y = 0; int win = 0;//排查次数 while (win < row*col-EASY_COUNT)//确保win=71之后,不再继续,因为之后排查的一定是炸弹 { printf("请输入你要排查的坐标\n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); PlayBoard(mine, row, col); break; } else { int count = GetMineCount(mine, x, y);//统计雷坐标周围雷的个数 show[x][y] = count + '0'; PlayBoard(show, row, col); win ++; } } else { printf("坐标非法,请重新输入\n"); } } if (win == col * row - EASY_COUNT) { printf("恭喜你,排雷成功\n"); PlayBoard(mine, row, col); } printf("是否继续游戏?\n"); }
计算周围雷的个数
//计算周围雷的个数 int GetMineCount(char mine[ROWS][COLS], int x, int y) { int i = 0; int j = 0; int count = 0; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { count += (mine[x][y] - '0');//mine[x][y]-'0'得到的是整数 } } return count; }
mine数组里面的元素是字符型,‘0’也是字符,两个字符相减,得到的是整型数字。
完整代码
text.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() { printf("*******************\n"); printf("******1.paly*******\n"); printf("******0.exit*******\n"); printf("*******************\n"); } void game() { char show[ROWS][COLS] = { 0 };//全部设为‘*’ char mine[ROWS][COLS] = { 0 };//全部设为‘0’ //初始化棋盘 InitBoard(show, ROWS, COLS, '*'); InitBoard(mine, ROWS, COLS, '0'); //打印棋盘 PlayBoard(show, ROW, COL); //PlayBoard(mine, ROW, COL); //布置雷‘1'为雷,‘0’非雷 SetMine(mine, ROW, COL); //PlayBoard(mine, ROW, COL);//检查是否成功设置雷 //排雷 FindMine(show, mine, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>\n"); scanf("%d", &input); switch (input) { case 1: { game(); break; } case 0: { printf("退出游戏\n"); break; } default: { printf("请重新选择\n"); break; } } }while(input); return 0; }
game.h
#include<stdio.h> #include<stdlib.h>//srand,rand的头文件 #include<time.h>//time的头文件 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10//10个雷 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘 void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘 void SetMine(char board[ROWS][COLS], int row, int col);//布置雷 void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷
game.c
#include"game.h" //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = set; } } } //打印棋盘 void PlayBoard(char board[ROWS][COLS], int row, int col) { printf("------扫雷游戏-------\n"); for (int i = 0; i <= col; i++)//打印列号0-10 { printf("%d|", i); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d|", i);//打印行号1-9 for (int j = 1; j <= col; j++)//j若初始化为0,棋盘就是9*10,而非9*9 { printf("%c ", board[i][j]); } printf("\n"); } } //布置雷 void SetMine(char board[ROWS][COLS], int row, int col) { int x = 0, y = 0; int count = EASY_COUNT; while (count) { x=rand() % row + 1;//生成1-9的随机数 y=rand() % row + 1;//生成1-9的随机数 if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10 { board[x][y] = '1'; count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环 } } } //排雷 //如果位置不是雷,就显示周围有几个雷 //如果位置是雷,就炸死,游戏结束 //把十个雷之外的所有雷都找出来,排雷成功,游戏结束 int GetMineCount(char mine[ROWS][COLS], int x, int y) { int i = 0; int j = 0; int count = 0; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { count += (mine[x][y] - '0'); } } return count; } void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { int x = 0, y = 0; int win = 0; while (win < row*col-EASY_COUNT) { printf("请输入你要排查的坐标\n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); PlayBoard(mine, row, col); break; } else { int count = GetMineCount(mine, x, y);//统计雷的坐标 show[x][y] = count + '0'; PlayBoard(show, row, col); win++; } } else { printf("坐标非法,请重新输入\n"); } } if (win == col * row - EASY_COUNT) { printf("恭喜你,排雷成功\n"); PlayBoard(mine, row, col); } printf("是否继续游戏?\n"); }
运行示例
小tip
1
这个是因为打印棋盘时,game.c文件中,第三个for循环里,j初始化为0,导致输入(4,5)坐标,得到(4,6)坐标,所以,要注意自己变量赋值。(并且这个是9*10棋盘,不知道当时怎么没看出来,哈哈)
2
show,mine数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9
3
建议写完一个模块,运行一个,好纠错。
欢迎斧正!!!
(听说公主,王子们都有一个好习惯,那就是, )