目录
🌕 2.由上,我们定义了两个二维字符数组,所以接下来我们要考虑初始化的问题。
🌕 4.因为此处我们再次会应用到数组的行和列,所以再次用define宏定义行列,一个9*9,一个11*11,以及雷的个数,方便分开使用,如下:
今天又学习到一个有趣的东西,用C语言实现扫雷游戏,下面就让小编带领大家一步一步入手吧。
🍁(一)扫雷游戏原理
(1).首先给定一个雷盘
(2).在雷盘上任意点击一个格子,若点到雷,则游戏结束,若不是雷则继续选择
(3).注意棋盘上面的数字,如下:
表示以此位置为中心,周围八个格子中有两个雷
(4).还有一个规则:如果所选择的该位置周围的格子的周围的八个格子中没有雷,则会自动显示,以此类推,直到遇到周围八个格子中至少有一个雷则停止显示。
(5).当雷盘上面只有雷未显示时,游戏胜利。
🍁(二)、 根据以上规则,给出以下思路
(1).对于雷盘,很容想到用一个二维数组来表示,(这里我们以9*9表示),并且我们看到没点击一个位置,就会有相应雷盘显示,所以我们想到用两个二维字符数组。
数组一mine:用于布置雷的信息。
数组二show:用于显示排查出的雷的信息。
(2).棋盘设置好后就该存放雷,因为位置随机,所以采用随机数的知识。
(3).排查雷:由玩家输入数组坐标,然后进行判断,直到游戏结束。
🍁(三)、具体实现
🌕1.此次我们依旧采用多文件的方式进行实现,如下:
(1).main函数内容放入文件test.c,并且考虑到玩游戏可以重复进行,所以采用do while()循环套用switch case语句作为主体结构,如下:
#include"game.h" void menu() { printf("***************************\n"); printf("******* 1.play *******\n"); printf("******* 2.exit *******\n"); printf("***************************\n"); } void game() { char mine[ROWS][COLS]; char show[ROWS][COLS]; //初始化棋盘 InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 //Display(mine, ROW, COL); Display(show, ROW, COL); //布置雷 SetMine(mine, ROW, COL); //Display(mine, ROW, COL); //Display(show, ROW, COL); //排查雷 FineMine(mine, show, ROW, COL); } int main() { srand((unsigned int)time(NULL)); int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出成功\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (input); return 0; }
(2).各函数的声明放入文件game.h,考虑到各.c文件中引用此.h文件,所以对于一些库函数,define定义的常量,我们可以放入此game.h文件,这样就不用重复使用了,如下:
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<time.h> #include<stdlib.h> #define Mine_count 10 #define ROWS 11 #define COLS 11 #define ROW 9 #define COL 9 void InitBoard(char board[ROWS][COLS], int row, int col, char ret);//初始化 void Display(char board[ROWS][COLS], int row, int col);//打印 void SetMine(char board[ROWS][COLS], int row, int col);//布置雷 void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); int GetMineCount(char mine[ROWS][COLS],int x, int y);//返回坐标(x,y)周围的雷数
(3).各函数的定义,我们放入game.c文件,如下:
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化 { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { board[i][j] = ret; } } } void Display(char board[ROWS][COLS], int row, int col)//打印 { int i = 0; printf("------扫雷游戏------\n"); for (i = 0; i < col + 1; i++)//列号 { printf("%d ", i); } printf("\n"); for (i = 1; i < row+1; i++) { printf("%d ", i);//行号 int j = 0; for (j = 1; j < col+1; j++) { printf("%c ", board[i][j]); } printf("\n"); } } void SetMine(char board[ROWS][COLS], int row, int col)//布置雷 { int count = Mine_count; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } } void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查 { int x = 0; int y = 0; int count1 = 0; while (1) { printf("请输入坐标:>"); scanf("%d %d", &x, &y); if (x > 0 && x < row + 1 && y>0 && y < col + 1) { if (mine[x][y] != '1') { int count = GetMineCount(mine,x,y); show[x][y] = count+'0'; Display(show, ROW, COL); count1++; } else { printf("很遗憾,你被炸死了!\n"); system("pause"); system("cls"); break; } } else { printf("输入错误,请重新输入\n"); } if (count1 >= row * col - Mine_count) { printf("恭喜你,游戏胜利!\n"); Display(show, ROW, COL); system("pause"); system("cls"); break; } } } int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,y)周围的雷数 { return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y-1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0'; }
🌕 2.由上,我们定义了两个二维字符数组,所以接下来我们要考虑初始化的问题。
(1).数组一mine:用来存放雷的信息。
我们想到此数组中用‘0’表示非雷,‘1’表示雷,(这样的做的好处将在之后介绍),所以刚开始mine数组初始化为‘0’。
(2).数组二show:用来存放排查雷的信息。
我们想到刚开始初始化为‘*’,作用就是单纯为了美观与显眼,并在玩家选择位置之后显示相应的雷的信息。
🌕3.值得注意的是:
对于以上这种情况,处于边缘的位置的格子,只需要考虑雷盘以内的地方,而盘外的地方是没有雷的,所以我们对于9*9的雷盘,我们想到将其长宽扩大一格,即用一个11*11的数组来表示,而多出来的格子默认为非雷,即初始化为‘0’ / ‘*’,并且在显示雷盘时,只显示中间9*9的区域。这样做的目的是为了后续方便计算边缘的位置周围的雷的个数。
🌕 4.因为此处我们再次会应用到数组的行和列,所以再次用define宏定义行列,一个9*9,一个11*11,以及雷的个数,方便分开使用,如下:
#define Mine_count 10 #define ROWS 11 #define COLS 11 #define ROW 9 #define COL 9
🌕5.开始初始化雷盘
这里有个小细节,mine和show数组是同行同列的,所以两者只需要使用同一个初始化函数,将自己的内容也作为一个参数,即可分开初始化,即创建一个这样的函数(void InitBoard(char board[ROWS][COLS], int row, int col, char ret)),对于二维数组的初始化,我们很容易想到双重for循环,代码实现如下:
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化 { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { board[i][j] = ret; } } }
🌕6.雷盘初始化后
我们还需要打印雷盘(二维数组的打印也是用到双重for循环),由上我们说明到mine和show数组的作用,所以我们在选择位置时所看到的的应该是show函数,并且为了美观与方便,我们可以将行列号也打印出来,代码实现如下:
void Display(char board[ROWS][COLS], int row, int col)//打印 { int i = 0; printf("------扫雷游戏------\n"); for (i = 0; i < col + 1; i++)//列号 { printf("%d ", i); } printf("\n"); for (i = 1; i < row+1; i++) { printf("%d ", i);//行号 int j = 0; for (j = 1; j < col+1; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
🌕效果如图:
7.雷盘打印实现后我们就开始布置雷,创建SetMine函数实现。据上,此时我们操作的是mine数组
(1).因为雷的位置不定,所以我们使用到随机数,随机产生一些坐标布置雷,并且雷的个数为自定义的
(2).坐标产生后还应该判断一下此处是否已经布置过雷了,若布置过了,则重新生成坐标,否则将此处的‘0’换成‘1’,以表示布置雷,成功布置一次,计数器减1,用于最后出控制循环
代码实现如下:
void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{
int count = Mine_count;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
8.雷布置好后,玩家就可以选择坐标了,所以创建FineMIne函数用于实现,并且操作的是show函数用于展示雷的信息。
(1).首先是玩家输入坐标(x,y)
(2).然后我们应该判断该坐标合不合法
1).因为我们要操作的是数组中间的9*9的区域,所以若x>0且x<ROW+1,则合法,然后显示雷盘信息,否则则不合法,提示重新输入。
2).当坐标合法后,我们就该判断该坐标处是否是雷,而判断是否是雷就应该在mine数组里面判断,此处为'1'则为雷,游戏结束;此处'0’,则玩家继续输入。
3).当不是雷时,我们应该显示雷盘信息,即此位置有多少个雷,(操作的是show数组),这时前面提到的用‘0’ / ‘1’的好处就来了,如下图:
所以这就是运用'1' / '0'的好处所在,所以现在我们只需要想办法求(x,y)周围的八个数之和。
因此我们创建GetMineCount函数来求和,实参:(mine,x,y),如下:
很显然我们很容发现(x,y)与其周围八个坐标的关系:
所以我们只需返回这八个数之和,这里有个小细节,因为'1' / '0'都是字符,所以我们应该转化为数字进行求和:
一般情况数字与字符数字之间的转化公式为 : 数字+‘0’=字符数字,所以代码实现如下:
int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,y)周围的雷数
{
return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y-1] + mine[x + 1][y - 1] +
mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
🍁(四)、以上操作效果如下:
🍁 结尾:
细心的小伙伴应该发现,以此坐标进行展开功能还没有实现,下面给出几点注意事项:
(1).该坐标不是雷
(2).该坐标周围八个位置没有雷
(3).该坐标没有被排查过
满足以上三点即可展开,以加快游戏进度 ,这里涉及到函数的递归等相关知识,小编不多以叙述,感兴趣的小伙伴可以自行思考。
此次知识到此就结束了,拜拜!