用C语言完成简易扫雷游戏,我做的是棋盘大小8*8,10个雷的扫雷。
说明:
本人还是个新手,可能有些地方写的不是很好,请大家见谅。程序测试的也不够全面,可能存在bug,如果发现有bug或者代码不够规范还麻烦大佬们指正。
此程序比较简单,分享扫雷的大佬也很多,我这篇发出来也可以记录一下我的学习历程,也可以给感兴趣的人提供一些帮助。
另外,本人英语不是很好,所以大部分的函数名和变量名都是汉语拼音。
显示汉字时,有时候会发生俩汉字重叠的奇怪现象。
后续有需要的话我还会完善此文章。
注意:本程序中x为横坐标,代表列,y为纵坐标,代表行。(x,y)代表x列y行。
功能预期:
1.运行程序后让玩家选择开始游戏还是退出游戏。
2.玩家选择开始游戏后,游戏开始。
3.棋盘8行8列10个雷。
4.每次玩家输入坐标(x,y)后,开始扫雷,判断输入坐标,如果已经选择过要有提示,如果选中了雷,显示游戏失败。
5.玩家第一次选择的坐标必不是雷。
6.每局游戏结束后不退出程序,而是询问玩家是否重新游戏。
7.若玩家输入无效信息,要给出提示并让玩家继续输入。
目录
一些准备
一些次要功能
界面:
获取是否开始游戏
获取输入的坐标
主要功能
清空棋盘
初始化
创建棋盘
取随机雷的位置
创建
选择坐标
显示雷
main函数
声明函数
总代码
一些准备
首先,在开始实现功能前,需要准备一些东西。
比如,棋盘每个格子的信息存放的地方。
这里我选择用结构体存储每个格子的数据。
struct GeZi {//格子
char XianShi;//显示
int LeiXing;//类型 1:雷 0:空
int ZiShenShuZi;//自身数字=周围雷数
char ZhuangTai;//状态 0:未选择 1:已选择
};
每个格子的数据包括:
1.显示:这个是在游戏界面显示出的信息。
2.类型:1的话是雷,0的话是空地。
3.自身数字:这个数据代表了周围8个方位的雷数。
4.状态:0代表未被选择,1代表已经被选择。
所有的头文件如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
所有的全局变量如下:
int YiWanCheng = 0; //已完成排除雷的格子数量
struct GeZi Pan[10][10] = {};//棋盘
int CiShu = 0;//即将开始的场次
char s[7][50] = {'\0'};//提示区内容
int Lei[15] = {0};//存储雷的位置
一些次要功能
其次 我对游戏界面有一些个性化的设置。
界面:
╔═══════════════════════╗
║ 1 2 3 4 5 6 7 8 ║
║ ╔═════════════════╗ ║
║ 1║ ║ 1║
║ 2║ ║ 2║
║ 3║ ║ 3║
║ 4║ ║ 4║
║ 5║ ║ 5║
║ 6║ ║ 6║
║ 7║ ║ 7║
║ 8║ ║ 8║
║ ╚═════════════════╝ ║
║ 1 2 3 4 5 6 7 8 ║
╚═══════════════════════╝
╔═══════════════════════╗
║提示: ║
║棋盘大小:8×8 雷数:10 ║
║输入1:开始游戏; ║
║输入0:退出游戏; ║
║ ║
║ ║
║ ║
╚═══════════════════════╝
请输入:
我希望它的程序界面长这样。(有些框在这里对不齐)
我把这个界面分为三部分,上面的游戏区,中间的提示区,以及下面的输入区。
因为界面大体不变,每次只需要更改一些字,为了方便,我尝试把这些打包写入到一个Print函数中。在输出前可以选择清除之前输出的内容,只显示最新的输出。下面是我写的Print函数。
int Print() { //输出基本界面
int i = 0;
int j = 0;
system("CLS");//清屏
printf("╔═══════════════════════╗\n");
printf("║ 1 2 3 4 5 6 7 8 ║\n");
printf("║ ╔═════════════════╗ ║\n");
for (i = 1; i <= 8; i++) {
printf("║ %d║ ", i);
for (j = 1; j <= 8; j++) {
printf("%c ", Pan[i][j].XianShi);
}
printf("║ %d║\n", i);
}
printf("║ ╚═════════════════╝ ║\n");
printf("║ 1 2 3 4 5 6 7 8 ║\n");
printf("╚═══════════════════════╝\n");
printf("╔═══════════════════════╗\n");
printf("║提示: ║\n");
for (i = 1; i <= 6; i++) {
printf("║%s║\n", s[i]); //s[i]每行23显示字符
}
printf("╚═══════════════════════╝\n");
printf("请输入:");
}
说明:system("CLS")可以清空之前输出的内容
s[1]~s[6]是全局变量,记录着要输出的提示信息。
获取是否开始游戏
接下来,是获取有效的输入。一个是每局游戏开始前或者结束后,输入1开始游戏或者重新游戏,还是输入0退出程序。另一个是判断输入的坐标是否有效。
CiShu(次数)是一个全局变量,记录着这是第几局游戏。其主要目的是判断即将进行的是否是第一局游戏。如果是第一局游戏,则提示开始游戏,否则提示重新开始。
No1就是一个标识而已。
int ShiFouKaiShi() { //是否开始;
int No1 = -1;
char a = '\0';
do {
a = '\0';
scanf("%d", &No1);
a = getchar();
while (a != '\n') {//清空缓冲区
if (a == ' ') {
} else {
No1 = -1;//存在多余内容,判定输入无效
}
a = getchar();
}
if (No1 != 0 && No1 != 1) {//输入不是0和1,则判定输入无效
No1 = -1;
}
if (No1 == -1) {//输入数据无效
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[3], "输入0:退出游戏; ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], "请输入有效数据 ");
if (CiShu == 1) {
strcpy(s[2], "输入1:开始游戏; ");
} else {
strcpy(s[2], "输入1:重新开始; ");
}
Print();
}
} while (No1 == -1);//此循环获取是否进入游戏
return No1;
}
获取输入的坐标
int ShuRuZuoBiao(int *px, int *py) {//获取输入的坐标
int x = 0;
int y = 0;
int n = 0; //输入整数个数
char a = '\0'; //获取缓冲区字符
int b = 1; //判断标志 1为有效
do {
b = 1;
n = scanf("%d%d", &x, &y);
a = getchar();
while (a != '\n') {//清空缓冲区
if (a == ' ') {
} else {
b = -1;//有多余内容,判定为输入无效
}
a = getchar();
}
if (n != 2 || x < 1 || x > 8 || y < 1 || y > 8) {//读入的不是2个整数,或读入的坐标不在棋盘范围内,判定为输入无效
b = -1;
}
if (b == -1) {
strcpy(s[2], "请输入选择的坐标x y ");
strcpy(s[3], "(中间用空格或换行分隔) ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], "请输入有效数据 ");
Print();
}
} while (b == -1);
if (b == 1) {
*px = x;
*py = y;
strcpy(s[6], " ");
}
return b;
}
主要功能
接下来,我们就可以开始主要功能的编写了。
首先,在全局变量中创建出棋盘Pan。并且记录一下当前以确定没雷格子的数量YiWanCheng。同时,为了方便,我在全局变量中用一个数组(Lei)记录了雷的位置,第i个雷位置存在Lei[i]里。
struct GeZi Pan[10][10] = {};
int YiWanCheng = 0; //已完成排除雷的格子数量
int Lei[15] = {0};//记录10个雷的位置
清空棋盘
每局游戏开始前,都需要让棋盘中所有格子恢复默认值,同时使已完成排雷的格子的数量清0,让Lei[1]~lei[10]清零。为了方便,我把上述操作写为一个函数,QingKongQiPan
int QingKongQiPan() { //清空棋盘
int i = 0;
int j = 0;
for (i = 1; i <= 8; i++) {//恢复棋盘中格子的信息
for (j = 1; j <= 8; j++) {
Pan[i][j].XianShi = ' ';
Pan[i][j].ZhuangTai = 0;
Pan[i][j].LeiXing = 0;
Pan[i][j].ZiShenShuZi = 0;
}
}
for (i = 1; i <= 10; i++) {//清空雷的位置
Lei[i] = 0;
}
YiWanCheng = 0;//已完成排雷数归零
return 0;
}
初始化
在第一局游戏正式开始前,将游戏初始化一下,包括清空棋盘和初始化全局变量的s数组。
int ChuShiHua() {//初始化
QingKongQiPan();
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[2], "输入1:开始游戏; ");
strcpy(s[3], "输入0:退出游戏; ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], " ");
}
创建棋盘
接着,就该写给棋盘写入初始信息的功能了。
取随机雷的位置
因为雷的位置要随机,所以,需要先取随机数。这里的随机数有点要求,因为棋盘大小是8*8,就是64个格子,一共有10个雷,所以,要在1~64之间随机取10个不同的数,将8*8的棋盘的格子依次编号,每个与随机数大小相等的编号的格子类型设置为雷。因为扫雷要求,第一次选的必不是雷,所以我的思路是等玩家输入了第一对有效坐标后,再创建棋盘。因此,我要完成的功能就是从1~64之间随机选10个不等的数,且每个随机数对应的格子都不是第一个有效坐标对应的格子。
我们需要利用<stdlib.h>中的srand()和rand()函数。注意,如果只用rand(),会发现每局游戏的随机数是一样的。所以,为了雷位置随机,要在rand()前写上srand(time(0));这样才能获得到比较随机的随机数。但这样取得的随机数范围不在1~64之间,因此我们需要让取到的数对64取余数,现在得到的数范围在0~63之间了,只需要将这个数再加1,得到的数范围就在1~64之间了。用n记录已经取到的雷数,将第i个取到的数保存到Lei[i]中,使雷数n=n+1;
下面,还需要判断如果两个数相等,就再取新的数,避免一个格子不止一个雷的情况。完成这个小功能,我用的方法是设置一个int型数组c,c[i]==0表示i编号对应格子没有保存为雷,c[i]==1代表i编号对应的格子已被选择为雷。在每次获得随机数b后,判断c[b]是否为1,为1的话就舍弃当前随机数再取一个。
之前还提到了要避开第一个有效坐标(x,y),(x,y)对应的编号为(8*(y-1)+x),所以只需要在开头让c[8*(y-1)+x]=1即可,使对应编号被假想为雷,但是不保存到Lei数组,也不增加雷的个数n。
n>=10时,即雷数量够了,便停止取随机数。
把上述功能写进取随机数(QuSuiJiShu)函数。
int QuSuiJiShu(int x, int y) { //1-64之间取10个不同随机数,且不为输入的坐标(即(y-1)*8+x)对应值,并存到Lei里
int n = 0;//记录已经取到的雷数
int b = 0; //暂存随机数的变量
int c[70] = {0}; //暂存已得随机数的数组
c[(y - 1) * 8 + x] = 1;//假定坐标位是雷
srand(time(0));//不能删
while (n < 10) {
b = rand();
b = b % 64;//取0~63的随机数
b = b + 1;//调整随机数范围
if (c[b] != 1) {//判断是否可以在新生成的随机数对应格子放置雷
c[b] = 1;//标记此位置是雷
n = n + 1;//雷数+1
Lei[n] = b;//将编号存入Lei
}
}
return 0;
}
创建
取完随机的雷的位置了,就可以创建棋盘了。
保险起见,把Pan中所有格子的类型都变为空地,所有自身数字都变为0
接下来用刚刚写的取随机数函数获取10个雷的编号。将对应编号分别转换为对应的格子,并将其类型改为雷。
自身数字,就是周围雷数。通过刚刚的一些操作,使空地和非游戏区格子的类型为0,雷的类型为1,所以,只需要将每个游戏区格子8个方向的格子的类型加起来,就是自己的自身数字。
把这些功能写进创建(ChuangJian)函数。
int ChuangJian(int x, int y) { //创建一个(x,y)必不是雷的棋盘
int i = 0;
int j = 0;
QuSuiJiShu( x, y);//取10个随机雷
for (i = 0; i <= 9; i++) {//此处要对Pan数组内所有成员操作
for (j = 0; j <= 9; j++) {
Pan[i][j].LeiXing = 0;
Pan[i][j].ZiShenShuZi = 0;
}
}
for (i = 1; i <= 10; i++) {//使10个雷对应格子标记为雷
if (Lei[i] % 8 == 0) {
Pan[Lei[i] / 8 ][8 ].LeiXing = 1;
} else {
Pan[Lei[i] / 8 + 1][Lei[i] % 8 ].LeiXing = 1;
}
}
for (i = 1; i <= 8; i++) {//使每个游戏区格子的自身数字等于其8个周围格子的类型之和
for (j = 1; j <= 8; j++) {
Pan[i][j].ZiShenShuZi = Pan[i - 1][j - 1].LeiXing + Pan[i - 1][j].LeiXing + Pan[i - 1][j + 1].LeiXing ;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i][j - 1].LeiXing + Pan[i][j + 1].LeiXing;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i + 1][j - 1].LeiXing + Pan[i + 1][j].LeiXing ;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i + 1][j + 1].LeiXing;
}
}
return 0;
}
至此,棋盘就创建完成了。
选择坐标
接下来,便是紧张又刺激的扫雷过程了。
接下来,写一个选择(XuanZe)函数,XuanZe(x,y)代表玩家选择了(x,y)
当(x,y)被选择过时,即Pan[y][x].zhuangtai==1时,返回1
当(x,y)是雷时,即Pan[y][x].LeiXing==1时,返回-1
当(x,y)既没被选择过,又不是雷,那么就把(x,y)改为选择状态,已完成排雷格子数+1,并且将Pan[y][x].XianShi改为自身数字,这里要注意,Pan[y][x].XianShi是char型,而Pan[y][x].LeiXing是int型,所以需要将Pan[y][x].LeiXing转换为char型,即Pan[y][x].LeiXing-'0',再赋值给Pan[y][x].XianShi。当自身数字为0时,代表周围8个格子都没有雷,则自动选择周围没有被选择过的游戏区格子。
int XuanZe(int x, int y) {//选择(x,y) 返回0:正常 -1:踩雷,游戏结束 1:已被选择
if (Pan[y][x].ZhuangTai == 1) {//已经被选择过
return 1;
} else {
if (Pan[y][x].LeiXing == 1) {//选择的坐标是雷
return -1;
} else {
YiWanCheng = YiWanCheng + 1;//已完成排雷格子数+1
Pan[y][x].XianShi = Pan[y][x].ZiShenShuZi + '0';//显示出类型转换后的自身数字
Pan[y][x].ZhuangTai = 1;//标记被选择
if (Pan[y][x].ZiShenShuZi == 0) {//分别选择周围8个未被选择的游戏区格子
if (Pan[y - 1][x - 1].ZhuangTai == 0 && y - 1 >= 1 && x - 1 >= 1) {
XuanZe(x - 1, y - 1);
}
if (Pan[y - 1][x].ZhuangTai == 0 && y - 1 >= 1) {
XuanZe(x, y - 1);
}
if (Pan[y - 1][x + 1].ZhuangTai == 0 && y - 1 >= 1 && x + 1 <= 8) {
XuanZe(x + 1, y - 1);
}
if (Pan[y ][x - 1].ZhuangTai == 0 && x - 1 >= 1) {
XuanZe(x - 1, y );
}
if (Pan[y][x + 1 ].ZhuangTai == 0 && x + 1 <= 8) {
XuanZe(x + 1, y );
}
if (Pan[y + 1][x - 1].ZhuangTai == 0 && y + 1 <= 8 && x - 1 >= 1) {
XuanZe(x - 1, y + 1);
}
if (Pan[y + 1][x ].ZhuangTai == 0 && y + 1 <= 8) {
XuanZe(x, y + 1);
}
if (Pan[y + 1][x + 1].ZhuangTai == 0 && y + 1 <= 8 && x + 1 <= 8) {
XuanZe(x + 1, y + 1);
}
}
return 0;
}
}
}
显示雷
如果成功扫雷或者不幸踩雷,将所有雷的位置显示出来。
定义一个显示雷(XianShiLei)函数,把所有雷的格子的.XianShi改为'*',标记出雷的位置。
int XianShiLei() {
int i = 0;
for (i = 1; i <= 10; i++) {
if (Lei[i] % 8 == 0) {
Pan[Lei[i] / 8 ][8 ].XianShi = '*';
} else {
Pan[Lei[i] / 8 + 1][Lei[i] % 8 ].XianShi = '*';
}
}
}
main函数
至此,大部分功能已经完成,现在开始main函数编写。
初始化后,将显示信息打印出来。即将开始第1局游戏,标记CiShu=1。之后判断玩家是否想开始游戏,当选择开始游戏后,让玩家输入第一组坐标。之后通过这个坐标来创建棋盘,接着选择第一组输入的坐标,接下来输出需要显示的信息。接下来,在已完成排雷数小于54(64个格子,10个雷,一共54个空地)时,重复让玩家输入坐标,选择坐标的操作,如果选择过则提示,如果是雷则跳出循环。每次判断后,都需要输出需要显示的内容,用来即使更新信息。当已排除雷数到达54时,证明扫雷已经完成,提示扫雷成功并询问是否重新游戏。
int main() {
int No1 = -1;
int x = 0;//横坐标(列)
int y = 0;//纵坐标(行)
int c = 0;//输入坐标返回标识
ChuShiHua();//第一局游戏开始前,初始化一下一些信息
Print();//输出需要显示的信息
CiShu = 1;//即将开始第1局游戏
No1 = ShiFouKaiShi();//获取是否开始信息
while (No1 == 1) {//玩家确定开始游戏
x = 0;
y = 0;
strcpy(s[2], "请输入选择的坐标x y ");
strcpy(s[3], "(中间用空格或换行分隔) ");
strcpy(s[6], " ");
Print();//提示信息更新并输出
ShuRuZuoBiao (&x, &y);//输入有效(x,y)
ChuangJian(x, y);//创建棋盘
c = XuanZe(x, y);//选择第一个坐标
Print();//输出信息
while (YiWanCheng < 54) {//成功扫雷前,不断扫雷
ShuRuZuoBiao (&x, &y);//输入有效坐标
c = XuanZe(x, y);//选择输入的坐标
if (c == 1) {//坐标被选择过
strcpy(s[6], "您输入的坐标已被选择 ");
} else if (c == -1) {//选择到了雷
strcpy(s[6], "游戏失败 ");
break;//跳出循环,本轮游戏结束
} else {
}
Print();
}
//本轮游戏结束
if (YiWanCheng == 54) {//是否成功找到了54个空地
strcpy(s[6], "成功扫雷 ");
}
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[2], "输入1:重新开始; ");
strcpy(s[3], "输入0:退出游戏; ");
XianShiLei();//将所有雷位置显示出来
Print();//输出需要显示的信息
No1 = ShiFouKaiShi();//询问玩家是否再来一局
QingKongQiPan();//清空棋盘数据
Print();//输出信息
CiShu = CiShu + 1;//即将开始的次数+1
}
return 0;
}
声明函数
最后,别忘了声明一下自己定义的函数
int ChuShiHua();
int ShuRuZuoBiao(int *px, int *py);
int QuSuiJiShu( int x, int y);
int ChuangJian(int x, int y);
int XuanZe(int x, int y) ;
int ShiFouKaiShi();
int QingKongQiPan();
int XianShiLei();
int Print();
总代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
struct GeZi {//格子
char XianShi;//显示
int LeiXing;//类型 1:雷 0:空
int ZiShenShuZi;//自身数字=周围雷数
char ZhuangTai;//状态 0:未选择 1:已选择
};
int YiWanCheng = 0; //已完成排除雷的格子数量
struct GeZi Pan[10][10] = {};//棋盘
int CiShu = 0;//即将开始的场次
char s[7][50] = {'\0'};//提示区内容
int Lei[15] = {0};//存储雷的位置
int ChuShiHua();
int ShuRuZuoBiao(int *px, int *py);
int QuSuiJiShu( int x, int y);
int ChuangJian(int x, int y);
int XuanZe(int x, int y) ;
int ShiFouKaiShi();
int QingKongQiPan();
int XianShiLei();
int Print();
int main() {
int No1 = -1;
int x = 0;//横坐标(列)
int y = 0;//纵坐标(行)
int c = 0;//输入坐标返回标识
ChuShiHua();//第一局游戏开始前,初始化一下一些信息
Print();//输出需要显示的信息
CiShu = 1;//即将开始第1局游戏
No1 = ShiFouKaiShi();//获取是否开始信息
while (No1 == 1) {//玩家确定开始游戏
x = 0;
y = 0;
strcpy(s[2], "请输入选择的坐标x y ");
strcpy(s[3], "(中间用空格或换行分隔) ");
strcpy(s[6], " ");
Print();//提示信息更新并输出
ShuRuZuoBiao (&x, &y);//输入有效(x,y)
ChuangJian(x, y);//创建棋盘
c = XuanZe(x, y);//选择第一个坐标
Print();//输出信息
while (YiWanCheng < 54) {//成功扫雷前,不断扫雷
ShuRuZuoBiao (&x, &y);//输入有效坐标
c = XuanZe(x, y);//选择输入的坐标
if (c == 1) {//坐标被选择过
strcpy(s[6], "您输入的坐标已被选择 ");
} else if (c == -1) {//选择到了雷
strcpy(s[6], "游戏失败 ");
break;//跳出循环,本轮游戏结束
} else {
}
Print();
}
//本轮游戏结束
if (YiWanCheng == 54) {//是否成功找到了54个空地
strcpy(s[6], "成功扫雷 ");
}
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[2], "输入1:重新开始; ");
strcpy(s[3], "输入0:退出游戏; ");
XianShiLei();//将所有雷位置显示出来
Print();//输出需要显示的信息
No1 = ShiFouKaiShi();//询问玩家是否再来一局
QingKongQiPan();//清空棋盘数据
Print();//输出信息
CiShu = CiShu + 1;//即将开始的次数+1
}
return 0;
}
int Print() { //输出基本界面
int i = 0;
int j = 0;
system("CLS");//清屏
printf("╔═══════════════════════╗\n");
printf("║ 1 2 3 4 5 6 7 8 ║\n");
printf("║ ╔═════════════════╗ ║\n");
for (i = 1; i <= 8; i++) {
printf("║ %d║ ", i);
for (j = 1; j <= 8; j++) {
printf("%c ", Pan[i][j].XianShi);
}
printf("║ %d║\n", i);
}
printf("║ ╚═════════════════╝ ║\n");
printf("║ 1 2 3 4 5 6 7 8 ║\n");
printf("╚═══════════════════════╝\n");
printf("╔═══════════════════════╗\n");
printf("║提示: ║\n");
for (i = 1; i <= 6; i++) {
printf("║%s║\n", s[i]); //s[i]每行23显示字符
}
printf("╚═══════════════════════╝\n");
printf("请输入:");
}
int ShiFouKaiShi() { //是否开始;
int No1 = -1;
char a = '\0';
do {
a = '\0';
scanf("%d", &No1);
a = getchar();
while (a != '\n') {//清空缓冲区
if (a == ' ') {
} else {
No1 = -1;//存在多余内容,判定输入无效
}
a = getchar();
}
if (No1 != 0 && No1 != 1) {//输入不是0和1,则判定输入无效
No1 = -1;
}
if (No1 == -1) {//输入数据无效
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[3], "输入0:退出游戏; ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], "请输入有效数据 ");
if (CiShu == 1) {
strcpy(s[2], "输入1:开始游戏; ");
} else {
strcpy(s[2], "输入1:重新开始; ");
}
Print();
}
} while (No1 == -1);//此循环获取是否进入游戏
return No1;
}
int ShuRuZuoBiao(int *px, int *py) {//获取输入的坐标
int x = 0;
int y = 0;
int n = 0; //输入整数个数
char a = '\0'; //获取缓冲区字符
int b = 1; //判断标志 1为有效
do {
b = 1;
n = scanf("%d%d", &x, &y);
a = getchar();
while (a != '\n') {//清空缓冲区
if (a == ' ') {
} else {
b = -1;//有多余内容,判定为输入无效
}
a = getchar();
}
if (n != 2 || x < 1 || x > 8 || y < 1 || y > 8) {//读入的不是2个整数,或读入的坐标不在棋盘范围内,判定为输入无效
b = -1;
}
if (b == -1) {
strcpy(s[2], "请输入选择的坐标x y ");
strcpy(s[3], "(中间用空格或换行分隔) ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], "请输入有效数据 ");
Print();
}
} while (b == -1);
if (b == 1) {
*px = x;
*py = y;
strcpy(s[6], " ");
}
return b;
}
int QingKongQiPan() { //清空棋盘
int i = 0;
int j = 0;
for (i = 1; i <= 8; i++) {//恢复棋盘中格子的信息
for (j = 1; j <= 8; j++) {
Pan[i][j].XianShi = ' ';
Pan[i][j].ZhuangTai = 0;
Pan[i][j].LeiXing = 0;
Pan[i][j].ZiShenShuZi = 0;
}
}
for (i = 1; i <= 10; i++) {//清空雷的位置
Lei[i] = 0;
}
YiWanCheng = 0;//已完成排雷数归零
return 0;
}
int ChuShiHua() {//初始化
QingKongQiPan();
strcpy(s[1], "棋盘大小:8×8 雷数:10 ");
strcpy(s[2], "输入1:开始游戏; ");
strcpy(s[3], "输入0:退出游戏; ");
strcpy(s[4], " ");
strcpy(s[5], " ");
strcpy(s[6], " ");
}
int QuSuiJiShu(int x, int y) { //1-64之间取10个不同随机数,且不为输入的坐标(即(y-1)*8+x)对应值,并存到Lei里
int n = 0;//记录已经取到的雷数
int b = 0; //暂存随机数的变量
int c[70] = {0}; //暂存已得随机数的数组
c[(y - 1) * 8 + x] = 1;//假定坐标位是雷
srand(time(0));//不能删
while (n < 10) {
b = rand();
b = b % 64;//取0~63的随机数
b = b + 1;//调整随机数范围
if (c[b] != 1) {//判断是否可以在新生成的随机数对应格子放置雷
c[b] = 1;//标记此位置是雷
n = n + 1;//雷数+1
Lei[n] = b;//将编号存入Lei
}
}
return 0;
}
int ChuangJian(int x, int y) { //创建一个(x,y)必不是雷的棋盘
int i = 0;
int j = 0;
QuSuiJiShu( x, y);//取10个随机雷
for (i = 0; i <= 9; i++) {//此处要对Pan数组内所有成员操作
for (j = 0; j <= 9; j++) {
Pan[i][j].LeiXing = 0;
Pan[i][j].ZiShenShuZi = 0;
}
}
for (i = 1; i <= 10; i++) {//使10个雷对应格子标记为雷
if (Lei[i] % 8 == 0) {
Pan[Lei[i] / 8 ][8 ].LeiXing = 1;
} else {
Pan[Lei[i] / 8 + 1][Lei[i] % 8 ].LeiXing = 1;
}
}
for (i = 1; i <= 8; i++) {//使每个游戏区格子的自身数字等于其8个周围格子的类型之和
for (j = 1; j <= 8; j++) {
Pan[i][j].ZiShenShuZi = Pan[i - 1][j - 1].LeiXing + Pan[i - 1][j].LeiXing + Pan[i - 1][j + 1].LeiXing ;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i][j - 1].LeiXing + Pan[i][j + 1].LeiXing;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i + 1][j - 1].LeiXing + Pan[i + 1][j].LeiXing ;
Pan[i][j].ZiShenShuZi = Pan[i][j].ZiShenShuZi + Pan[i + 1][j + 1].LeiXing;
}
}
return 0;
}
int XuanZe(int x, int y) {//选择(x,y) 返回0:正常 -1:踩雷,游戏结束 1:已被选择
if (Pan[y][x].ZhuangTai == 1) {//已经被选择过
return 1;
} else {
if (Pan[y][x].LeiXing == 1) {//选择的坐标是雷
return -1;
} else {
YiWanCheng = YiWanCheng + 1;//已完成排雷格子数+1
Pan[y][x].XianShi = Pan[y][x].ZiShenShuZi + '0';//显示出类型转换后的自身数字
Pan[y][x].ZhuangTai = 1;//标记被选择
if (Pan[y][x].ZiShenShuZi == 0) {//分别选择周围8个未被选择的游戏区格子
if (Pan[y - 1][x - 1].ZhuangTai == 0 && y - 1 >= 1 && x - 1 >= 1) {
XuanZe(x - 1, y - 1);
}
if (Pan[y - 1][x].ZhuangTai == 0 && y - 1 >= 1) {
XuanZe(x, y - 1);
}
if (Pan[y - 1][x + 1].ZhuangTai == 0 && y - 1 >= 1 && x + 1 <= 8) {
XuanZe(x + 1, y - 1);
}
if (Pan[y ][x - 1].ZhuangTai == 0 && x - 1 >= 1) {
XuanZe(x - 1, y );
}
if (Pan[y][x + 1 ].ZhuangTai == 0 && x + 1 <= 8) {
XuanZe(x + 1, y );
}
if (Pan[y + 1][x - 1].ZhuangTai == 0 && y + 1 <= 8 && x - 1 >= 1) {
XuanZe(x - 1, y + 1);
}
if (Pan[y + 1][x ].ZhuangTai == 0 && y + 1 <= 8) {
XuanZe(x, y + 1);
}
if (Pan[y + 1][x + 1].ZhuangTai == 0 && y + 1 <= 8 && x + 1 <= 8) {
XuanZe(x + 1, y + 1);
}
}
return 0;
}
}
}
int XianShiLei() {
int i = 0;
for (i = 1; i <= 10; i++) {
if (Lei[i] % 8 == 0) {
Pan[Lei[i] / 8 ][8 ].XianShi = '*';
} else {
Pan[Lei[i] / 8 + 1][Lei[i] % 8 ].XianShi = '*';
}
}
}