扫雷游戏(命令行界面模式)使用C语言实现

游戏设计思想:
在一个row * col的棋盘上进行游戏,从键盘获取(x,y)坐标选定需要排查的位置,如果该坐标上没有雷,则显示其坐标周围一圈的雷的个数,如果该坐标上有雷,则被炸死,游戏结束。当排除掉了所有非雷的坐标后则游戏胜利。
(PS:如果第一次输入的坐标上就有雷,不被炸死,标记该坐标为雷。)

这里我们将使用二维数组来存放棋盘。

游戏内容:

游戏界面如下

扫雷游戏(命令行界面模式)使用C语言实现_第1张图片

首先游戏开始前,我们需要一个菜单让用户选择是否开始或退出游戏

我们在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’ 表示首次排雷时遇到雷

扫雷游戏(命令行界面模式)使用C语言实现_第2张图片扫雷游戏(命令行界面模式)使用C语言实现_第3张图片

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重新设置雷坐标且不包含本次输入的坐标,这样能够起到首次遇雷保护作用,但是当雷的个数偏多时,程序会停留在计算重新设置雷坐标函数里(应该是我写的逻辑有问题,所以暂不展示该函数)。当坐标周围没雷,可以实现展开功能(暂未习得,哈哈)。

你可能感兴趣的:(C语言基础语法学习笔记,命令行小游戏,游戏,c语言,c++)