游戏设计思想:
在一个row * col的棋盘上进行游戏,从键盘获取(x,y)坐标选定需要排查的位置,如果该坐标上没有雷,则显示其坐标周围一圈的雷的个数,如果该坐标上有雷,则被炸死,游戏结束。当排除掉了所有非雷的坐标后则游戏胜利。
(PS:如果第一次输入的坐标上就有雷,不被炸死,标记该坐标为雷。)
这里我们将使用二维数组来存放棋盘。
游戏内容:
游戏界面如下
首先游戏开始前,我们需要一个菜单让用户选择是否开始或退出游戏
我们在main函数里面完成菜单的主逻辑,代码如下:
//菜单界面
void menu() {
printf("************************************\n");
printf("************* 1.PLAY ************\n");
printf("************* 0.EXIT ************\n");
printf("************************************\n");
}
int main(){
srand((unsigned int)time(NULL));
int input = 0;//用户菜单输入
//使用循环能让用户反复进行游戏,直到用户选择退出游戏.
do {
//输出菜单
menu();
printf("请选择:");
scanf("%d", &input);
switch (input) {
case 1:
//printf("开始游戏\n");
game();
break;
case 0:
printf("游戏结束。\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
棋盘设计:
我们需要创建两个二维数组,一个用来存放所有雷的坐标,另一个显示用户的已排雷信息。
数组的行和列设置为棋盘的行+2和列+2,因为当用户输入的坐标在棋盘边缘上时,如果访问其周围一圈的元素则会导致数组下标越界,所以扩展棋盘边缘外围一圈。
在存放雷的数组中,‘1’表示雷,‘0’表示非雷;在显示已排雷信息数组中 ‘*’ 表示待排,‘0’-‘8’表示周围一圈的雷的个数,‘m’ 表示首次排雷时遇到雷
game函数:
void game() {
int ret = 0;//首次排雷返回值
//雷的坐标信息
char mine[ROWS][COLS] = { 0 };
//排雷显示信息
char show[ROWS][COLS] = { 0 };
//初始化棋盘
initBoard(mine, ROWS, COLS, '0'); //通过字符参数初始化目标二维数组所有元素
initBoard(show, ROWS, COLS, '*');
//打印棋盘
printBoard(show, ROW, COL);
//设置雷坐标
setMine(mine, ROW, COL,MINE_COUNT);//通过整型参数设置雷的个数
//第一次排雷
ret = firstCheck(mine, show, ROW, COL);
//排雷
findMine(mine, show, ROW, COL,ret);
}
initBoard函数:
通过字符参数set初始化目标数组的所有元素
/*
初始化棋盘函数
board:目标二维数组
rows:行
cols:列
set:初始化内容
*/
void initBoard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0, j = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
printBoard函数:
打印行列标和数组内容
/*
打印棋盘函数
board:目标二维数组
row:行
col:列
*/
void printBoard(char board[ROWS][COLS], int row, int col) {
int i = 0, j = 0;
printf("+------扫雷游戏------+\n");
//输出列标
for (i = 0; i <= col; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
//输出行标
printf("%d ", i);
//输出数组元素
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("+--------------------+\n");
}
setMine函数:
使用随机函数生成的x和y坐标来放置雷
/*
设置棋盘雷坐标
mine:存放雷的数组
row:行
col:列
count:雷的个数
*/
void setMine(char mine[ROWS][COLS], int row, int col,int count) {
while (count) {
//随机生成范围1到row或col的x和y坐标
int x = rand() % row + 1; //假设row = 9,col = 9 即生成 1-9的数
int y = rand() % col + 1;
//判断该位置是否为空
if (mine[x][y] == '0') {
mine[x][y] = '1';
count--;
}
}
}
firstCheck()函数:
处理用户首次输入坐标,如果是雷,则显示该坐标为m,不是雷则显示周围一圈雷的个数。函数返回值0代表此次排查的是雷的坐标,不算作非雷待排坐标数,返回值1代表排查了非雷坐标。此返回值用于后面findMine()函数计算待排雷坐标数的计算。
/*
首次排查雷函数
mine:存放雷的数组
show:排雷显示数组
row:行
col:列
return 首次排雷如遇雷返回0 如空则返回1
*/
int firstCheck(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
while (1) {
//输入x,y坐标
int x, y;
printf("请输入你要排雷的坐标:\n");
scanf("%d%d", &x, &y);
//检查坐标是否合法
if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {
//首次踩雷
if (mine[x][y] == '1') {
printf("oh!当前坐标是雷。\n首次排雷时遇雷不炸死,游戏继续!\n");
show[x][y] = 'm';
printBoard(show, row, col);
return 0;
}
else {
show[x][y] = countMine(mine, x, y) + '0';
printBoard(show, row, col);
return 1;
}
}
else {
printf("坐标非法,请重新输入!\n");
}
}
}
countMine()函数:
计算传入坐标周围一圈的雷的个数,因为数组类型为char,所以直接取周围一圈元素值(‘0’ 或 ‘1’)之和再减去周围个数 × ‘0’ 即可得到个数值(为int型,涉及整型提升)。
/*
计算坐标周围一圈的雷的个数
return 个数值
*/
int countMine(char mine[ROWS][COLS], int x, int 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';
}
findMine函数:
当玩家输入的坐标上有雷时,游戏结束,如果没有雷,则此坐标显示周围一圈雷的个数,如果排除掉了所有非雷的剩余坐标则获胜。
/*
排查雷函数
mine:雷坐标数组
show:已排雷信息数组
row:行
col:列
ret:首次排雷的返回值
*/
void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int ret) {
int remains = row * row - MINE_COUNT - ret; //待排雷坐标数
while (remains) {
//输入x,y坐标
int x, y;
printf("请输入你要排雷的坐标:\n");
scanf("%d%d", &x, &y);
//检查坐标是否合法
if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {
//踩雷
if (mine[x][y] == '1') {
printf("很抱歉,你踩雷了!\n游戏结束。\n");
printBoard(mine, row, col);
return;
}
else {
show[x][y] = countMine(mine, x, y) + '0';
printBoard(show, row, col);
remains--;
}
}
else {
printf("坐标非法,请重新输入:\n");
}
}
if (remains == 0) {
printf("恭喜你!排雷成功!\n");
printBoard(mine, row, col);
}
}
头文件信息:
#include
#include
#include
//棋盘行列数
#define ROW 9
#define COL 9
//扩展棋盘的行列数
#define ROWS ROW+2
#define COLS COL+2
//雷数
#define MINE_COUNT 10
//初始化棋盘函数声明
void initBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘函数声明
void printBoard(char board[ROWS][COLS], int row, int col);
//设置雷坐标函数声明
void setMine(char mine[ROWS][COLS], int row, int col,int count);
//第一次排雷检查
int firstCheck(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//排雷函数声明
void findMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int ret);
补充:
对于firstCheck()函数,也可以写到findMine()函数逻辑里。
对于首次排雷,其实当第一次输入坐标为雷时,可以将该坐标置为非雷(即‘0’),然后resetMine重新设置雷坐标且不包含本次输入的坐标,这样能够起到首次遇雷保护作用,但是当雷的个数偏多时,程序会停留在计算重新设置雷坐标函数里(应该是我写的逻辑有问题,所以暂不展示该函数)。当坐标周围没雷,可以实现展开功能(暂未习得,哈哈)。