🥰扫雷游戏的功能说明
- 使用控制台来模拟扫雷游戏
- 控制台中棋盘的大小为 9*9(后续也可以扩大)
- 默认棋盘中有10个雷
- 如果排雷位置不是雷,就显示周围有几个雷
- 如果排雷位置是雷,就被炸死结束游戏
- 把除了10个雷以外的位置排查完,则排雷成功,游戏结束
🥰扫雷游戏的功能分析
- 游戏的菜单设计
- 游戏可能不止玩一次,是否进行游戏,这里我们使用switch通过用户的输入来判定是否继续
- 在扫雷的过程中,布置好的雷的棋盘和打印在控制台上排雷的棋盘都需要存储,所以这里我们使用两个数组分别进行存储
- 棋盘是 9*9 的大小,我们用二维数组来实现
- 考虑到排查的雷如果在99的边缘的某一个,在计算周围雷的数量就会发生越界,所以实际上的棋盘应该是 1111,显示在控制台的大小是 9*9
- 在棋盘中,没有雷的位置存放0,有雷的位置存放1,雷的生成通过
rand和srand来实现 - 通过字符的形式表示1和0,为了表示神秘感,打印在控制台排雷时,棋盘所有的位置都是*,如果是用整形,有可能会有冲突,为了避免这些情况,我们统一使用字符来表示
🥰实现扫雷游戏必备的知识
- 头文件的作用
- 判断
- 循环
- 数组
- 函数
🥰游戏的框架
为了养成良好的代码习惯,我们将使用三个文件分别来实现扫雷游戏
test.c:所有的在进入游戏逻辑之前的代码
game.c: 所有游戏的功能
game.h:所有函数的声明,包含的头文件
🥰游戏的实现
❤️游戏的启动
- 我们使用
input这个整形变量来判断游戏是否进行 - 使用
do...while循环来包含整个游戏的启动逻辑,需要保证就算游戏不进行第二次,也要有至少一次启动 do...while条件通过input来判断,用户输入 1 就表示继续游戏,输入 0 表示退出游戏,当input为 0 时,条件值为假,do...while就不会执行函数体了- 使用
srand来生成rand函数的种子,srand中使用time函数来计算当下的时间戳,保证每次生成的种子都不一样,srand 函数中参数要求unsigned int类型 - 游戏启动打印菜单,菜单等后续继续介绍
- 打印完菜单后,让用户输入
input的值 - 使用
switch语句来判断游戏是退出还是继续 - 这里使用 game() 函数,所有函数的功能都将放在这个函数中实现
c
test.c
int main() {
int input = 0;
srand((unsigned int)time(NULL));
do{
menu();
printf("请输入:> ");
scanf("%d", &input);
switch (input) {
case 0:
printf("退出游戏\n");
break;
case 1:
game();
break;
default:
printf("选择错误,请重新输入\n");
break;
}
}while(input);
return 0;
}
❤️菜单的设计
- 菜单使用
menu()函数来实现
c
test.c
void menu() {
printf("------------------------\n");
printf("-------- 1. play -------\n");
printf("-------- 0. exit--------\n");
printf("------------------------\n");
}
❤️游戏逻辑的设计
- 在整个游戏逻辑的设计中,调用各个游戏的功能来实现游戏
- 在 game.h 头文件中包含对应的常量和库文件
- 初始化棋盘:将棋盘初始化为 11*11 的大小,所以使用
ROWS和COLS,将数组还有初始化成的字符传入函数 - 打印棋盘:初始化好后,将棋盘打印,因为只需要打印 9*9 大小的棋盘,所以是
ROW和COL - 布置雷:传入的参数与上同理,具体实现看下
- 排查雷:传入的参数与上同理,具体实现看下
c
game.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define EASY_COUNT 10 // 雷的个数
#define ROW 9 // 数组的行
#define COL 9 // 数组的列
#define ROWS ROW+2
#define COLS COL+2
c
test.c
void game() {
char mine[ROWS][COLS]; // 存放布置好的雷
char show[ROWS][COLS]; // 存放排查出来的雷
// 初始化棋盘
// 1.mine数组最开始全是 '0'
// 2.show数组最开始全是 '*'
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
// 打印棋盘
//DisplayBoard(mine,ROW,COL);
DisplayBoard(show,ROW,COL);
// 布置雷
SetMine(mine,ROW,COL);
//DisplayBoard(mine,ROW,COL);
// 排查雷
FindMine(mine,show,ROW,COL);
}
❤️初始化棋盘
- 使用嵌套循环来控制数组,分别使用 row、col 来控制循环,set 控制存放的字符
c
void InitBoard(char board[ROWS][COLS],int row,int col,char set) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = set;
}
}
}
❤️打印棋盘
- 将
row和col参数传进函数,使用循环来打印棋盘 - 并且在打印棋盘的基础上,让棋盘有序号来标识
- 第一次循环,先使用循环,打印
0~9,来表示每列的序号 - 第二次循环,每行打印棋盘前,先打印行号,因为第一次循环,已经打印了 0,并且,打印棋盘从 1 开始打印,如果从 0 开始打印,打印的是棋盘的边界,所以变量
i和j都从 1 开始打印

c
void DisplayBoard(char board[ROWS][COLS],int row,int col) {
printf("------扫雷游戏------\n");
for (int i = 0 ; i <= row; i++) {
printf("%d ",i);
}
printf("\n");
for (int i = 1; i <= row; i++) {
printf("%d ",i);
for (int j = 1; j <= col; j++) {
printf("%c ",board[i][j]);
}
printf("\n");
}
}
❤️排查雷
- 定义变量
x和y,让rand函数随机生辰1~9的随机数 - 再使用
while循环以count来作为直接条件 - 判断数组中,如果是 0,也就是没有雷,就变成 1,表示布置雷,每变一次,
count值就少一个,就像雷,放一个就少一个,当雷放置完了,count等于 0,while循环体就不执行了
c
void SetMine(char board[ROWS][COLS],int row,int col) {
// 布置 10 个雷
// 生成随机的坐标,布置雷
int count = EASY_COUNT;
while (count) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0') {
board[x][y] = '1';
count--;
}
}
}
❤️排查雷
- 依旧定义变量
x和y,来表示棋盘坐标 - 定义变量
win,来表示排雷的次数,循环的条件,当 win 的值等于棋盘上所有格子的个数减去雷的个数,就是没有雷的个数,当 win 大于这个值,就表示所有坐标位置都排查完了,排雷成功 - 首先先判断用户输入的坐标是不是合法的,如果不合法,直接结束循环,重新开始
- 如果坐标合法,开始判断输入的坐标位置是不是雷,如果是雷就炸死,游戏结束,重新开始
- 如果不是雷,就显示当前坐标周围的雷的个数,排雷次数
win+1,具体实现看下
c
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col) {
// 初始化坐标
int x = 0 ;
int y = 0 ;
int win = 0; // 排雷的过程,成功排一次 win+1
while (win < row * col - EASY_COUNT) { // 排雷的次数应该小于棋盘的总数-雷的个数
printf("请输入要排查的坐标:> ");
scanf("%d %d", &x,&y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine,ROW,COL);
break;
}
else {
// 如果这个位置不是雷,就要统计周围有几个雷
int count = GetMineCount(mine,x,y);
show[x][y] = count + '0';
DisplayBoard(show,ROW,COL);
win++;
}
}
else {
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功\n");
DisplayBoard(mine,ROW,COL);
}
}
❤️排查坐标周围的雷的个数
- 计算周围雷的个数我们单独用一个函数来计算
- 当输入合法坐标,并且不是雷时,判断周围坐标中累的个数
x是横坐标,y是纵坐标- 图中是坐标周围的坐标,基于图片的这些坐标,来判断雷的个数
- 因为我们存储的都是字符,
1的 ascll 是49,0的 ascll 是48 - 假设周围有 2 个雷,周围的情况就是
6个0,2个1,6*48+2*49-8*48=386-284=2

c
int GetMineCount(char mine[ROWS][COLS],int x,int y) {
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');
}
到这,游戏的功能全都实现了,扩展功能后续再分享
🥰游戏的代码
test.c
c
#include "game.h"
void menu() {
printf("------------------------\n");
printf("-------- 1. play -------\n");
printf("-------- 0. exit--------\n");
printf("------------------------\n");
}
void game() {
char mine[ROWS][COLS]; // 存放布置好的雷
char show[ROWS][COLS]; // 存放排查出来的雷
// 初始化棋盘
// 1.mine数组最开始全是 '0'
// 2.show数组最开始全是 '*'
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
// 打印棋盘
//DisplayBoard(mine,ROW,COL);
DisplayBoard(show,ROW,COL);
// 布置雷
SetMine(mine,ROW,COL);
//DisplayBoard(mine,ROW,COL);
// 排查雷
FindMine(mine,show,ROW,COL);
}
int main() {
int input = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请输入:> ");
scanf("%d", &input);
switch (input) {
case 0:
printf("退出游戏\n");
break;
case 1:
game();
break;
default:
printf("选择错误,请重新输入\n");
break;
}
}while (input);
return 0;
}
game.h
c
#ifndef GAME_H
#define GAME_H
#include <stdio.h>
#include <stdlib.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 row,int col,char set) ;
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS],int row,int col) ;
// 布置雷
void SetMine(char board[ROWS][COLS],int row,int col) ;
// 排查雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col) ;
#endif //GAME_H
game.c
c
#include "game.h"
// 初始化棋盘
void InitBoard(char board[ROWS][COLS],int row,int col,char set) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = set;
}
}
}
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS],int row,int col) {
printf("------扫雷游戏------\n");
for (int i = 0 ; i <= row; i++) {
printf("%d ",i);
}
printf("\n");
for (int i = 1; i <= row; i++) {
printf("%d ",i);
for (int j = 1; j <= col; j++) {
printf("%c ",board[i][j]);
}
printf("\n");
}
}
// 布置雷
void SetMine(char board[ROWS][COLS],int row,int col) {
// 布置 10 个雷
// 生成随机的坐标,布置雷
int count = EASY_COUNT;
while (count) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0') {
board[x][y] = '1';
count--;
}
}
}
// 排查坐标周围的雷
int GetMineCount(char mine[ROWS][COLS],int x,int y) {
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');
}
// 排查雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col) {
// 初始化坐标
int x = 0 ;
int y = 0 ;
int win = 0; // 排雷的过程,成功排一次 win+1
while (win < row * col - EASY_COUNT) { // 排雷的次数应该小于棋盘的总数-雷的个数
printf("请输入要排查的坐标:> ");
scanf("%d %d", &x,&y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (mine[x][y] == '1') {
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine,ROW,COL);
break;
}
else {
// 如果这个位置不是雷,就要统计周围有几个雷
int count = GetMineCount(mine,x,y);
show[x][y] = count + '0';
DisplayBoard(show,ROW,COL);
win++;
}
}
else {
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功\n");
DisplayBoard(mine,ROW,COL);
}
}