游戏运行效果
初始界面
排雷界面
数据结构的分析
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需一定的数据结构来存储这些信息。
因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们首先想到的就创建⼀个的数组来存放信息。
为了防止越界,我们在设计的时候,给数组扩大一圈,雷还是布置在中间的的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11是比较合适。
为了避免混杂,这里我们采用这样的方案,我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符 ‘*’,为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’
1.char mine[11][11] = {0};//⽤来存放布置好的雷的信息 2.char show[11][11] = {0};//⽤来存放排查出的雷的个数信息
实现过及注意事项
文件结构
1.test.c //⽂件中写游戏的测试逻辑 2.game.c //⽂件中写游戏中函数的实现等 3.game.h //⽂件中写游戏需要的数据类型和函数声明等
主函数
#include "game.h" int main() { int input = 0; srand((unsigned int)time(NULL)); do { manu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
菜单函数
void manu() { printf("********************\n"); printf("*** _1.play_ ***\n"); printf("*** _0.exit_ ***\n"); printf("********************\n"); }
具体的游戏执行流程
void game() { char mine[ROWS][COLS]; // 存放雷信息的棋盘 char show[ROWS][COLS]; //展示给玩家的棋盘 //初始化棋盘 //mine棋盘全部初始化为'0' show棋盘全部初始化为'*' InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 //DisplayBoard(mine, ROWS, COLS); DisplayBoard(show, ROWS, COLS); //布置雷 SetMine(mine, ROWS, COLS); //DisplayBoard(mine, ROWS, COLS); //排查雷 FindMine(mine, show, ROW, COL); }
棋盘初始化函数
我们通过InitBoard()
函数对char mine[ROWS][COLS]
和char show[ROWS][COLS]
进行初始化,前者放'0'
后者放'*'
我们用两个for
循环来遍历数组
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 DisplayBoard(char Board[ROWS][COLS], int rows, int cols) { int i = 0; printf("\n"); for (i = 0; i <= rows - 2; i++) printf("%d ", i); printf("\n"); for (i = 1; i < rows-1; i++) { int j = 0; printf("%d ", i); for (j = 1; j < cols-1; j++) { printf("%c ", Board[i][j]); } printf("\n"); } }
布置雷函数
我们用rand()
来生成随机数,用srand((unsigned int)time(NULL))
确保了游戏的随机性。
void SetMine(char Board[ROWS][COLS], int rows, int cols) { int n = EASY_COUNT;//雷的数量 while (n) { int x = rand() % 9 + 1; int y = rand() % 9 + 1; if (Board[x][y] != '1') { Board[x][y] = '1'; n--; } } }
排查雷及判断输赢函数
void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col) { int count = row * col;//未知坐标的数量 while (count > EASY_COUNT) { int x, y; printf("请输入你想排查的坐标:>"); scanf("%d%d", &x, &y); if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { if (MineBoard[x][y] == '1') { printf("很遗憾你踩到雷了,游戏结束\n"); DisplayBoard(MineBoard, ROWS, COLS); break; } else { //统计周围雷的数量 SearchAround(MineBoard, ShowBoard, x, y); DisplayBoard(ShowBoard, ROWS, COLS); count = JudgeCount(ShowBoard, row, col); } } else { printf("坐标非法,请重新输入!\n"); } } if (count == EASY_COUNT) { printf("恭喜你扫雷成功:>\n"); DisplayBoard(MineBoard, ROWS, COLS); } }
返回周围雷数量函数
这里就体现了我们将数组初始化为'0'
,雷为'1'
的好处。通过周围八格的和再减去8*'0'
就是周围雷的数量了。
int GetMineCount(char Board[ROWS][COLS],int x,int y) { return (Board[x - 1][y - 1] + Board[x - 1][y] + Board[x - 1][y + 1] + Board[x][y - 1] + Board[x][y + 1] + Board[x + 1][y - 1] + Board[x + 1][y] + Board[x + 1][y + 1] - 8 * '0'); }
需要注意的是:这里我们必须减去8*'0'
,否者会出现以下状况
因为这加起来的和是'?'
的ASCII表中的值,减去8*'0'
才是周围雷的数量
雷区展开函数
//如果排查位置没有雷,且周围位置也没有雷,返回周围一片 void SearchAround(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y) { if (ShowBoard[x][y] == '*')//判断当前位置是否已经赋值 { int arounds = GetMineCount(MineBoard, x, y); if (arounds != 0) { ShowBoard[x][y] = arounds + '0'; } else { if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL)) { ShowBoard[x][y] = arounds + ' '; SearchAround(MineBoard, ShowBoard, x - 1, y - 1); SearchAround(MineBoard, ShowBoard, x - 1, y); SearchAround(MineBoard, ShowBoard, x - 1, y + 1); SearchAround(MineBoard, ShowBoard, x, y - 1); SearchAround(MineBoard, ShowBoard, x, y + 1); SearchAround(MineBoard, ShowBoard, x + 1, y - 1); SearchAround(MineBoard, ShowBoard, x + 1, y); SearchAround(MineBoard, ShowBoard, x + 1, y + 1); } } } }
这里我们需要注意两个易错点:
1.这里必须判断一下当前位置是否赋值,否则将在两个相邻坐标间来回进入此函数,造成死循环;
2.当第一次进入SearchAround()
函数时我们知道x
和y
的取值都只是在1~9之间。那么不断递归后x
和y
将越过此范围,造成错误。
未排查坐标数量函数
int JudgeCount(char ShowBoard[ROWS][COLS], int row, int col) { int num = 0; for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (ShowBoard[i][j] == '*') num++; } } return num; }
源代码
头文件game.h
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //初始化棋盘 void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set); //打印棋盘 void DisplayBoard(char Board[ROWS][COLS], int rows, int cols); //布置雷 void SetMine(char Board[ROWS][COLS], int rows, int cols); //排查雷 void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col);
测试文件test.c
#include "game.h" void game() { char mine[ROWS][COLS]; // 存放雷信息的棋盘 char show[ROWS][COLS]; //展示给玩家的棋盘 //初始化棋盘 //mine棋盘全部初始化为'0' show棋盘全部初始化为'*' InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 //DisplayBoard(mine, ROWS, COLS); DisplayBoard(show, ROWS, COLS); //布置雷 SetMine(mine, ROWS, COLS); //DisplayBoard(mine, ROWS, COLS); //排查雷 FindMine(mine, show, ROW, COL); } void manu() { printf("********************\n"); printf("*** _1.play_ ***\n"); printf("*** _0.exit_ ***\n"); printf("********************\n"); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { manu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
函数实现文件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 DisplayBoard(char Board[ROWS][COLS], int rows, int cols) { int i = 0; printf("\n"); for (i = 0; i <= rows - 2; i++) printf("%d ", i); printf("\n"); for (i = 1; i < rows-1; i++) { int j = 0; printf("%d ", i); for (j = 1; j < cols-1; j++) { printf("%c ", Board[i][j]); } printf("\n"); } } //布置雷 void SetMine(char Board[ROWS][COLS], int rows, int cols) { int n = EASY_COUNT;//雷的数量 while (n) { int x = rand() % 9 + 1; int y = rand() % 9 + 1; if (Board[x][y] != '1') { Board[x][y] = '1'; n--; } } } //返回周围雷的数量 int GetMineCount(char Board[ROWS][COLS],int x,int y) { return (Board[x - 1][y - 1] + Board[x - 1][y] + Board[x - 1][y + 1] + Board[x][y - 1] + Board[x][y + 1] + Board[x + 1][y - 1] + Board[x + 1][y] + Board[x + 1][y + 1] - 8 * '0'); } //如果排查位置没有雷,且周围位置也没有雷,返回周围一片 void SearchAround(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y) { if (ShowBoard[x][y] == '*')//判断当前位置是否已经赋值 { int arounds = GetMineCount(MineBoard, x, y); if (arounds != 0) { ShowBoard[x][y] = arounds + '0'; } else { if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL)) { ShowBoard[x][y] = arounds + ' '; SearchAround(MineBoard, ShowBoard, x - 1, y - 1); SearchAround(MineBoard, ShowBoard, x - 1, y); SearchAround(MineBoard, ShowBoard, x - 1, y + 1); SearchAround(MineBoard, ShowBoard, x, y - 1); SearchAround(MineBoard, ShowBoard, x, y + 1); SearchAround(MineBoard, ShowBoard, x + 1, y - 1); SearchAround(MineBoard, ShowBoard, x + 1, y); SearchAround(MineBoard, ShowBoard, x + 1, y + 1); } } } } int JudgeCount(char ShowBoard[ROWS][COLS], int row, int col) { int num = 0; for (int i = 1; i <= row; i++) { for (int j = 1; j <= col; j++) { if (ShowBoard[i][j] == '*') num++; } } return num; } //排查雷 void FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col) { int count = row * col;//未知坐标的数量 while (count > EASY_COUNT) { int x, y; printf("请输入你想排查的坐标:>"); scanf("%d%d", &x, &y); if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { if (MineBoard[x][y] == '1') { printf("很遗憾你踩到雷了,游戏结束\n"); DisplayBoard(MineBoard, ROWS, COLS); break; } else { //统计周围雷的数量 SearchAround(MineBoard, ShowBoard, x, y); DisplayBoard(ShowBoard, ROWS, COLS); count = JudgeCount(ShowBoard, row, col); } } else { printf("坐标非法,请重新输入!\n"); } } if (count == EASY_COUNT) { printf("恭喜你扫雷成功:>\n"); DisplayBoard(MineBoard, ROWS, COLS); } }