在有了制作三子棋的经验后,做起扫雷也更加得心应手了。
大致思路:
首先扫雷全部都不知道雷在哪里,然后选择一个位置(这时候就可以设计二维数组来实现了)后该位置会显示有多少个雷,没有踩到雷游戏继续(再一次进入循环),然后如果踩到雷游戏结束(结束跳出循环),而我们要使得知道玩家选择的位置有没有雷,要先埋雷(随机位置),而且玩家看到的是被蒙着的游戏棋盘,而玩家选择的位置要与我们埋雷的位置对应,所以这时候需要两个棋盘,一个是玩家看到的,一个是我们自己埋雷的(也是用来判断玩家选不选得到雷的棋盘)。
但是两个棋盘里面要放置什么类型的元素呢?
我们可以用0和1来表示有无雷,0就不是雷,1表示有雷,而这个0与1使用字符类型还是int类型?
若用int类型来表示有无雷,在后续我们排查周围一圈,若玩家选择了一个位置,其周围有一个雷,那么玩家选择的位置会显示int型数字1,但是同时我们的雷也是用int型1来表示,我们要怎么来分辨这两个1之中哪一个是雷呢?就如下图情况:
综上,为了避免有分不出来的情况,我们都用char类型来创造数组,以便后续在排查有无雷的时候不会分辨不出是雷还是雷的数量显示。
具体做法:
1.创造三个文件
创建三个文件,分别命名为game.h game.c text.c。
- text.c用来测试游戏的进程。
- game.c用来编写游戏实现所需要的函数。
- game.h用来声明函数名。
这样分开编写能使我们的思路更加清晰,在之后有bug需要修改时也不会难以查找。
2.游戏进程
Ⅰ:打印出游戏开始界面——menu()
Ⅱ:若玩家选择开始游戏,则进入游戏,若选择退出,结束程序
Ⅲ:创造两个棋盘,用char型二维数组表示。
Ⅳ:将埋雷的棋盘初始化为‘0’,玩家看到的棋盘初始化为‘*’——initializedboard() coverboard()
Ⅴ:开始埋雷——set_mine()
Ⅵ:打印coverboard()棋盘——displayboard()
Ⅶ:让玩家选择位置并显示出其周围雷数,并判断是否踩雷以及游戏是否胜利,若踩雷或游戏 胜利,则结束此次游戏。
3.函数的具体实现
前提:在game.h内先设定以下常量:
//game.h
#include <stdio.h>
//这里设定左右上下都多一行是为了让玩家后续选择扫边边的雷也可以判断周围雷数
#define ROWS 11
#define COLS 11
//这里才是真实的棋盘大小
#define row 9
#define col 9
设置以下常量以便在之后调试程序是否成功的时候可以修改棋盘大小,以方便调试。
Ⅰ:制作游戏界面——menu()
只需做出简单的页面即可,如下图:
由于玩家可能会重复游玩,界面可能要重复打印,为了方便使用,我们创造一个void函数来实现,代码如下:
void menu()//在text.c内
{
printf("******************************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("******************************\n");
}
Ⅱ:让玩家选择是否开始游戏
打印出菜单后玩家可输入是否进行游戏,如果选择1,就进入游戏,如果选择0,就退出游戏,这个过程用switch来实现,又因为可能玩家玩完又要再玩一次,在整个过程用循环来实现,直至玩家选择0才跳出循环。
int main()//在text.c内
{
srand((unsigned int)time(NULL));
int input = 0;//创造一个变量,为之后跳出循环做准备
do
{
menu();//打印菜单
scanf("%d", &input);//这个过程让玩家选择,如果选1,打开开关1,进入游戏,若选择0,打印出游戏结束后同时变成while(0),跳出循环
switch (input)
{
case 1:
{
game();//这个是选择1后才游戏实现的过程的函数,之后再实现。
break;
}
case 0:
{
printf("游戏结束\n");
break;
}
default://玩家有可能会输入错误,若输入的数既不是1也不是0就让循环重来一次。
{
printf("输入错误,请重新输入\n");
break;
}
}
} while (input);
return 0;
}
这两步与上一三子棋案例无太多的不同。
Ⅲ:创造两个棋盘
在text.c里创建数组,这里我们创造的数组设置为全局,进入函数后再重新初始化就好,这样在以后如果需要修改数组样式便会十分方便。实现代码如下:
//text.c
#include <stdio.h>//引用头文件,以便之后用printf打印出菜单
#include "game.h"//引用game.h头文件,为了之后可使用常量ROWS COLS row col
char mineboard[ROWS][COLS] = { 0 };//放置雷的数组
char realboard[ROWS][COLS] = { 0 };//玩家看到的数组
Ⅳ:将埋雷的棋盘初始化为‘0’,玩家看到的棋盘初始化为‘*’
设置两个函数,名字分别为initializeboard()与coverboard(),前者实现全部初始化为‘0’,以便后续放置雷与检验雷,后者用来初始化数组为‘*’,让玩家看的一个棋盘。这两个函数都需要放在game()函数里,代表当游戏开始时,我们就开始创造这俩个棋盘。具体代码如下:
//text.c
void game()
{
initializeboard(mineboard,row,col);
coverboard(realboard, row, col);
}
//game.h
void initializeboard(char arr[ROWS][COLS], int rows, int cols);
void coverboard(char arr[ROWS][COLS], int rows, int cols);
//game.c
void initializeboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
//这里用for循环遍历数组里每一个元素,并将其元素替换为字符‘0’
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
arr[i][j] = '0';
}
}
}
void coverboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
//这里用for循环遍历数组里每一个元素,并将其元素替换为字符‘*’
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
arr[i][j] = '*';
}
}
}
Ⅴ:开始埋雷
设置一个void类型函数名为set_mine()的函数,用来随机放置雷(将雷位置换为字符‘1’),这时候就要使用到随机数,随机生成x,y数,并且让其在数组大小范围内,一直埋雷,直到埋雷数达到我们需要的数量(用while实现)。具体代码及分析如下:
//game.h
//用来声明函数先
void set_mine(char arr[ROWS][COLS], int rows, int cols);
//text.c
//这里要注意,生成随机数要引用两个库函数:<stdlib.h>和<time.h>
#include "game.h"
#include <stdlib.h>
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;
}
//game.c
void set_mine(char arr[ROWS][COLS], int rows, int cols)
{
int num = 0;
//设置一个num值初始值为0,每次埋完一个雷,num的数量就加一,直至埋完十个雷
while (num < 10)
{
//设置坐标
int i = rand() % row + 1;
int j = rand() % col + 1;
if (arr[i][j] == '0')
{
arr[i][j] = '1';
num += 1;
}
}
}
Ⅵ:打印coverboard()棋盘
设置一个void类型函数名为displayboard()的棋盘,实现把数组的每一个元素都打印出来,具体代码及解释如下:
//game.h
void displayboard(char arr[ROWS][COLS], int rows, int cols);
//text.c
void game()
{
initializeboard(mineboard,row,col);
coverboard(realboard, row, col);
set_mine(mineboard, row, col);
}
//game.c
void displayboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
//这里打印出行数和列数,为了让玩家更加方便找坐标
int heng = 0;
int shu = 0;
for (heng = 0; heng <= row; heng++)
{
printf("%d ", heng);//注意这里要打印出%d和一个空格,才会有比较整齐的排列效果
}
printf("\n");
for (i = 1; i <= rows; i++)
{
printf("%d ", i);//注意这里要打印出%d和一个空格
for (j = 1; j <= cols; j++)
{
printf("%c ", arr[i][j]);//注意这里要打印出%c和一个空格
}
printf("\n");//每把一行的元素全部打印完后换行再打印下一行
}
}
Ⅶ:让玩家选择位置并显示出其周围雷数,并判断是否踩雷以及游戏是否胜利,若踩雷 或游戏胜利,则结束此次游戏。
如何排查周围雷数:
我们要让玩家重复输入坐标,每一次输入坐标之后,我们要判断玩家输入的坐标会不会超出棋盘的大小,然后通过坐标对应的位置在我们用来放雷数组元素的周围一圈的排查,找出周围有多少个字符‘1’,创建一个int变量用来存放雷数,当检测到周围的一个位置中有一个字符‘1’,我们就让这个变量+1,在排除完一圈后,我们要把最终得出来的周围雷数放入玩家输入的坐标中显示,那么最终要怎么让这个类型为int类型的变量放入char类型数组里呢?
这里有一个想了很久才想到的小方法:
int a= 10; ‘10‘ = 10 + ’0‘ 10 = ’10‘ - ’0‘
其实int类型与char类型本质上没有太大区别,都可以互相转换的,在观察ascll码后我们可以知道任何int类型数字的ascll码值,在加上char类型’0‘的ascll码值之后,最终打印出的字符都会变为其int类型时本身的数字。
这样我们将int类型与char类型之间的转换就完成啦~
如何判断游戏胜利(即扫完所有雷):
制作一个循环,同时创造一个变量,当我们每一次进入循环,这个变量就增加一次,一直到
进入次数 = 棋盘大小-总埋雷数 的时候,就告诉玩家游戏胜利并跳出循环,若过程中踩雷,就告诉玩家游戏失败并跳出循环,然后再一次调用之前敲出的display()函数将我们的埋雷版本棋盘给玩家展示后就结束本次游戏。
下面是Ⅶ部分的代码:
//text.c
game()
{
int time = 0;
//创造一个变量time,每次扫出一个雷(若扫到雷就跳出循环了,time不会增加),time就加一,直至扫出目标数量的雷,游戏结束标志胜利
do
{
k = playeract(mineboard, realboard, row, col);//若有雷返还整形0给循环使循环中止。
displayboard(realboard, row, col);
time += 1;
//判断字符‘0’的数量有没有到达棋盘格子数减去雷的数量,若有达到,则游戏胜利
if (time == row*col-10)
{
printf("恭喜,游戏胜利!\n");
break;
}
else if (k == 0)
{
displayboard(mineboard, row, col);
}
} while (k);//若有雷返还整形0给循环使循环中止。
}
//game.c
int playeract(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int rows, int cols)
{
int x = 0;
int y = 0;
int num = 0;
int m = 0;
printf("请输入坐标:");
while (1)
{
while (1)//这里利用循环,做到让玩家输入正确的坐标,如果不输入正确坐标,就无法跳出循环
{
scanf_s("%d %d", &x, &y);
if ((x > 0) && (x <= row) && (y > 0) && (y <= col))
{
break;
}
else
{
printf("超出范围,请重新输入坐标:");
}
}
if (arr1[x][y] == '0')//判断有没有雷,如果没有雷要把附近一圈的给显示出来
{
if (arr1[x - 1][y - 1] == '1')
{
m += 1;
}
if (arr1[x][y - 1] == '1')
{
m += 1;
}
if (arr1[x + 1][y - 1] == '1')
{
m += 1;
}
if (arr1[x - 1][y ] == '1')
{
m += 1;
}
if (arr1[x + 1][y] == '1')
{
m += 1;
}
if (arr1[x - 1][y + 1] == '1')
{
m += 1;
}
if (arr1[x][y + 1] == '1')
{
m += 1;
}
if (arr1[x + 1][y + 1] == '1')
{
m += 1;
}
arr2[x][y] = m+'0';//这里就将检测出的雷个数(int型)换成周围雷的个数(char型)
num = 1;
return 1;
}
if (arr1[x][y] == '1')
{
arr2[x][y] = '1';
printf("扫到地雷,游戏结束\n");
num = 0;
return 0;
}
}
}
//game.h
int playeract(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int rows, int cols);
那么来看看最终的代码吧
//game.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROWS 11
#define COLS 11
#define row 9
#define col 9
void initializeboard(char arr[ROWS][COLS], int rows, int cols);
void displayboard(char arr[ROWS][COLS], int rows, int cols);
void set_mine(char arr[ROWS][COLS], int rows, int cols);
void coverboard(char arr[ROWS][COLS], int rows, int cols);
int playeract(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int rows, int cols);
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
char mineboard[ROWS][COLS] = { 0 };
char realboard[ROWS][COLS] = { 0 };
void menu()
{
printf("************************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("************************************\n");
}
void game()
{
initializeboard(mineboard,row,col);
coverboard(realboard, row, col);
set_mine(mineboard, row, col);
//displayboard(mineboard, row, col);
//printf("\n");
displayboard(realboard, row, col);
int k = 0;
/*k = playeract(mineboard, realboard, row, col);*/
int time = 0;
do
{
k = playeract(mineboard, realboard, row, col);
displayboard(realboard, row, col);
time += 1;
if (time == row*col-10)
{
printf("恭喜,游戏胜利!\n");
break;
}
else if (k == 0)
{
displayboard(mineboard, row, col);
}
} while (k);
//if (k == 0)
//{
// displayboard(mineboard, 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;
}
//game.c
#include "game.h"
#define _CRT_SECURE_NO_WARNINGS 1
void initializeboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
arr[i][j] = '0';
}
}
}
void coverboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
arr[i][j] = '*';
}
}
}
void displayboard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
int j = 0;
int heng = 0;//
int shu = 0;//
for (heng = 0; heng <= row; heng++)//
{
printf("%d ", heng);
}//
printf("\n");//
for (i = 1; i <= rows; i++)
{
printf("%d ", i);//
for (j = 1; j <= cols; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
int playeract(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int rows, int cols)
{
int x = 0;
int y = 0;
//int num = 0;
int m = 0;
printf("请输入坐标:");
while (1)
{
while (1)
{
scanf_s("%d %d", &x, &y);
if ((x > 0) && (x <= row) && (y > 0) && (y <= col))
{
break;
}
else
{
printf("超出范围,请重新输入坐标:");
}
}
if (arr1[x][y] == '0')//判断有没有雷,如果没有雷要把附近一圈的给显示出来
{
if (arr1[x - 1][y - 1] == '1')
{
m += 1;
}
if (arr1[x][y - 1] == '1')
{
m += 1;
}
if (arr1[x + 1][y - 1] == '1')
{
m += 1;
}
if (arr1[x - 1][y ] == '1')
{
m += 1;
}
if (arr1[x + 1][y] == '1')
{
m += 1;
}
if (arr1[x - 1][y + 1] == '1')
{
m += 1;
}
if (arr1[x][y + 1] == '1')
{
m += 1;
}
if (arr1[x + 1][y + 1] == '1')
{
m += 1;
}
arr2[x][y] = m+'0';//这里的‘0’应该被换成周围雷的个数
//num = 1;
return 1;
}
if (arr1[x][y] == '1')
{
arr2[x][y] = '1';
printf("扫到地雷,游戏结束\n");
//num = 0;
return 0;
}
}
}
void set_mine(char arr[ROWS][COLS], int rows, int cols)
{
int num = 0;
while (num < 10)
{
int i = rand() % row + 1;
int j = rand() % col + 1;
if (arr[i][j] == '0')
{
arr[i][j] = '1';
num += 1;
}
}
}
又是收获满满的一天~