游戏设计思想:
在一个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初始化目标数组的所有元素
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函数:
打印行列标和数组内容
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坐标来放置雷
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()函数计算待排雷坐标数的计算。
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型,涉及整型提升)。
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函数:
当玩家输入的坐标上有雷时,游戏结束,如果没有雷,则此坐标显示周围一圈雷的个数,如果排除掉了所有非雷的剩余坐标则获胜。
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重新设置雷坐标且不包含本次输入的坐标,这样能够起到首次遇雷保护作用,但是当雷的个数偏多时,程序会停留在计算重新设置雷坐标函数里(应该是我写的逻辑有问题,所以暂不展示该函数)。当坐标周围没雷,可以实现展开功能(暂未习得,哈哈)。



