扫雷游戏(基础版)

发布于:2024-12-21 ⋅ 阅读:(38) ⋅ 点赞:(0)

我们用C 语言代码实现了一个简单的控制台版扫雷游戏,代码分为三个部分,分别是头文件中定义的函数声明以及两个源文件game.htest.cgame.c。

1.头文件(game.h)部分

首先包含了<stdio.h>(用于标准输入输出操作,像printfscanf函数的使用)、<time.h>(获取时间相关信息,用于初始化随机数生成器)和<stdlib.h>(包含了如randsrand等函数,用于生成随机数以及内存分配等相关操作)这几个常用的标准库头文件。

接着通过#define定义了一些宏常量:ROWCOL分别定义了棋盘的行数和列数(这里是 9x9 的棋盘,不包含边缘扩充部分)ROWSCOLS则是在原棋盘行数和列数基础上各加 2,用于在棋盘周边扩充一圈,方便后续判断某个格子周围雷的数量等操作,避免边界判断的复杂情况。EASY_COUNT定义了简单难度下雷的数量,这里设定为 10 个雷。

最后声明了四个函数,这些函数分别用于初始化棋盘、打印棋盘、布置雷以及排查雷,它们的具体实现在game.c文件中。

#include<stdio.h>
#include<time.h>
#include<stdlib.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS],int row,int col);
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);


2.源文件(game.c)部分

(InitBoard函数)

这个函数用于初始化棋盘,它通过两层嵌套的for循环遍历给定的二维数组(棋盘)的每一个元素,将每个元素都设置为传入的参数set指定的值。例如,初始化雷的棋盘时可以传入'0'表示初始无雷,初始化显示给玩家的棋盘时可以传入'*'表示初始未排查的格子。

(DisplayBoard函数)

该函数用于在控制台打印出棋盘的样子。首先打印出列号作为表头,然后逐行打印行号以及对应行上每个格子的状态(用相应的字符表示,比如'*'表示未排查、数字字符表示周围雷的数量等)。

(SetMine函数)

此函数的功能是在棋盘上随机布置雷。它先记录还需要布置的雷的数量(初始化为EASY_COUNT),然后通过while循环不断生成随机的坐标(xy,范围是棋盘内部有效的行列范围,通过rand函数结合取余和加 1 操作来实现),判断该坐标位置是否还没有布置雷(是否为'0'),如果是,则将该位置设置为'1'表示有雷,并将剩余雷的数量减 1,直到布置完规定数量的雷为止。

(get_mine_count函数)

这个函数用于计算给定坐标(xy)周围 8 个格子中雷的数量。它通过将周围 8 个格子对应的字符值('0''1')相加,然后减去 8 倍的'0'字符的 ASCII 值(因为字符'0''9'的 ASCII 值是连续的,相减后可以得到对应的整数值),从而得到周围雷的实际数量。

(FindMine函数)

该函数实现了雷的排查逻辑,是游戏的核心交互部分。它通过一个while循环不断让玩家输入要排查的坐标,先判断输入坐标是否合法(在棋盘范围内),接着检查该坐标对应的格子在显示棋盘show中是否已经排查过(判断是否不为'*'),如果没排查过,再看该坐标在雷的棋盘mine中是否是雷,如果是雷则游戏结束,显示雷的棋盘并提示被炸死;如果不是雷,则将找到非雷的个数win加 1,计算该坐标周围雷的数量并更新显示棋盘show中对应位置为相应的数字字符,然后重新显示棋盘,循环继续,直到找到的非雷个数达到总格子数减去雷的数量(即所有非雷格子都被排查完),此时游戏胜利,显示雷的棋盘并提示排雷成功。

#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
	int i = 0;
	int j = 0;
	for (i = 0;i < rows; i++)
	{
		for (j = 0;j < cols;j++)
		{
			board[i][j] = set;
		}
	}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("------扫雷游戏------\n");
	for (j = 0;j <= col;j++)
	{
		printf("%d ",j);
	}
	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");

}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;

	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}
//计算周围雷的数量
int get_mine_count(char board[ROWS][COLS], int x, int y)
{
	// 通过将周围坐标对应的字符值('0'或'1')转换为数字(减去字符'0'的ASCII值)并求和,来统计雷的数量
	// 例如字符'1'减去'0'得到数字1,代表有雷,然后把8个方向的值累加起来
	return (board[x - 1][y] + board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] + board[x][y + 1] + board[x - 1][y + 1] - 8 * '0');

}
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//找到非雷的个数

	while (win<row*col-EASY_COUNT)
	{
		printf("请输入要排查的坐标:>");

		scanf("%d%d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标被排查过了,不能重复排查\n");
				continue;
			}
			else
			{
				//如果是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else//如果不是雷
				{
					win++;
					//统计mine数组中x,y坐标周围有几个雷
					int count = get_mine_count(mine, x, y);
					show[x][y] = count + '0';//转换成数字字符
					DisplayBoard(show, ROW, COL);

				}
			}
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

3.源文件(test.c)部分

(menu函数

这个函数很简单,就是在控制台打印出游戏的菜单,显示了两个选项,分别是1.play用于开始游戏和0.exit用于退出游戏。

(game函数)

game函数实现了一局游戏的完整流程。首先定义了两个二维字符数组,mine数组用于存放雷的信息(初始化为全'0'),show数组用于存放展示给玩家看到的棋盘信息(初始化为全'*'),接着调用InitBoard函数初始化这两个数组,然后通过SetMine函数在mine数组中布置雷,再用DisplayBoard函数显示初始的玩家棋盘(全是'*'的样子),最后调用FindMine函数开始排查雷,进入游戏的交互环节。

(main函数)

main函数是整个程序的入口点。首先初始化随机数生成器(通过调用srand函数并传入当前时间作为种子,确保每次游戏雷的布置是随机的),然后进入一个do-while循环,在循环中先调用menu函数显示游戏菜单,接着让用户输入选择,根据用户输入的不同选项(通过switch语句判断),如果输入1则调用game函数开始一局游戏,如果输入0则退出游戏,其他输入则提示选择错误,循环继续,直到用户选择退出游戏为止,最后返回0表示程序正常结束。

#include"game.h"

void menu()
{
	printf("*****************************\n");
	printf("*********   1.play   ********\n");
	printf("*********   0.exit   ********\n");
	printf("*****************************\n");

}

void game()
{
	char mine[ROWS][COLS] = { 0 };//存放布置好雷的信息
	char show[ROWS][COLS] = { 0 };//存放排查到雷的信息
	//初始化数组内容为指定内容
	//mine 数组在没有布置雷的时候,都是‘0’
	InitBoard(mine, ROWS, COLS,'0');
	//show 数组在没有排查到雷的时候,都是‘*’
	InitBoard(show, ROWS, COLS,'*');

	//设置雷
	SetMine(mine,ROW,COL);
	//显示初始棋盘(给玩家看的)
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	//排查雷
	FindMine(mine,show,ROW,COL);
	
	// 游戏结束后询问是否再来一局
	char choice;
	printf("是否再来一局?(y/n)");
	scanf(" %c", &choice);  // 注意前面的空格,用于跳过输入缓冲区可能存在的换行符等空白字符
	if (choice == 'y' || choice == 'Y')
	{
		game();
	}
}

int main()
{
	int input = 0;
	//设置随机数的生成起点
	srand((unsigned int)time(NULL));

	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;
}