0
点赞
收藏
分享

微信扫一扫

用C语言实现小游戏之——【扫雷】

SDKB英文 2022-01-24 阅读 45

用C语言实现一个简易的扫雷小游戏!

【游戏要求】


【代码实现】

※与上一篇博客中类似,这个游戏的代码也将分模块完成!即分成游戏的函数实现(game.c)、函数声明和符号定义(game.h)、测试(test.c)三个部分

首先是在test.c中编写一个简易的开始菜单。用do...while循环获取正确的输入,用switch...case判断开始游戏、退出游戏或者是重新选择。

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

}
int main()
{
	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;
}

接着我们要布置雷。我们如何存储数据呢?我们可以用数组(9*9 二维数组)来存储,将数组中雷的地方设置为1,非雷的地方设置为0即可,但这就会与点开之后表示周围有一个雷的1混淆,有歧义。我们可以更换雷和非雷的标记为*和#来解决这一问题,但这样打印的时候就需要经过筛选,雷的地方不能打印为雷,挺麻烦的。于是我们采用【两个数组】的方法来解决。我们再单独搞一个数组,两个数组一样大,坐标也相对应。一个是专门用来打印的(排查出的雷的信息,未排查出的用*表示)数组char show[][],另一个是专门用来存放雷(‘1’)或非雷(‘0’)的(布置好的雷的信息)数组char mine[][]。现在我们需要解决另外一个问题。因为要显示周围一圈的雷的个数,所以要遍历周围八个位置,但如果是边界上的格子的话就会有越界的问题,对于数组来说是越界访问。那么我们该如何解决这一问题呢?既然害怕边界的越界,那就不如【多开辟一圈】数组,即在原先的基础上多了两行两列。9*9->11*11,真正用的是中间9*9的,检测雷的数量的数量时就不会越界了。那么我们就可以在game.h中define ROW和COL为9,define ROWS和COLS为11。

然后就可以重点写game()函数了。首先要定义两个数组,然后将数组【初始化】。我们可以用编写一个函数来实现数组的初始化。因为show数组(初始化为‘0’)和mine数组(初始化为‘*’)的初始化内容不一样,所以我们在编写函数的时候可以把初始化内容作为函数的一个参数来使用,定义void init_board(char arr[ROWS][COLS], int rows, int cols, char set)。

void init_board(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

然后我们需要【打印棋盘】,我们定义另外一个函数来打印棋盘(只打印9*9)。为了增强游戏的可玩性,我们将在打印部分的第一行和第一列加上行列的序号,这样可以更清楚。

void show_board(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int 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 ", arr[i][j]);
		}
		printf("\n");
	}
	printf("--------扫雷--------\n");
}

可以实现这样的效果:

 


接下来我们就要【布置雷】了,我们将用一个set_mine函数来实现,只需要在中间9*9的区域布置雷即可。我们可以先在game.h中定义符号常量EASY_COUNT为10,表示这个扫雷游戏将设置十个雷。我们选择用随机数来生成电脑放置雷的位置。随机数使用了rand()和srand()和时间戳,具体原理可见我的上上篇博客中关于随机数的介绍哦~首先要在我们的test函数里的do...while的循环之前用srand((unsigned int)time(NULL)),并在game.h中添加相关头文件(stdlib.h 和 time.h)然后就可以在布置雷的函数中用rand()函数生成随机数来布置雷了~

void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	int x = 0;
	int y = 0;
	while (count)
	{
		x = rand()%row+1;
		y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

可以将mine数组打印出来检查一下:

 


接下来我们就要开始【排查雷】了,可以写一个find_mine(mine, show, ROW, COL)函数来排查雷。首先是要获取要排查的雷的坐标,然后判断其是否合法,不合法的话要重新输入,所以这里需要一个循环,循环跳出的条件为将所有非雷的地方全部打开,因此我们用一个变量win来计数。输入正确坐标之后排查这个地方,若这个地方是雷,则被炸死游戏结束!如果不是雷,则遍历周围八个坐标统计雷的个数(我们可以另外定义一个函数来计算这个值),然后将数值放在show数组中,并打印目前的show数组,变量win+1,最后判断是否全部扫完继续或跳出循环。

代码实现如下:

int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] 
		+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] 
		+ mine[x + 1][y] + mine[x + 1][y + 1]-8*'0';
}

void find_mine(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 (mine[x][y] == '1')
			{
				printf("很遗憾,被炸死了!\n");
				show_board(mine, ROW, COL);
				break;
			}
			else
			{
				int count = get_mine_count(mine, x, y);
				show[x][y] = count + '0';
				show_board(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入!\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		show_board(mine, ROW, COL);
	}
}

【小结】

我们这个小游戏用到的符号常量、定义的函数和头文件大致有这些

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

#define ROW 9
#define COL 9
#define EASY_COUNT 10

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

//初始化
void init_board(char arr[ROWS][COLS], int rows, int cols, char set);

//打印
void show_board(char arr[ROWS][COLS], int row, int col);

//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);

//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

test.c中则是游戏执行的框架

#define _CRT_SECURE_NO_WARNINGS 1
#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 };//存放排查出的雷的信息
	//初始化棋盘
	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');
	//打印棋盘
	show_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	//排查雷
	find_mine(mine, show, ROW, COL);
}

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

具体的函数的代码都在上文中啦~祝大家阅读愉快~

另:游戏有两个部分可以进行优化升级:第一是关于【展开一片】的问题,可用递归实现(只要满足该坐标不是雷且周围八个都不是雷则可展开,被展开的坐标又可作为新的起点继续展开。但被展开过的应该做个标记,下次递归到的时候不再递归。也就是满足不是雷且周围八个不是雷且没被排查过即可展开一片。)第二是关于【标记雷】的实现。这些都是该代码可进行的优化~

举报

相关推荐

0 条评论