【C语言/入门游戏设计】简单扫雷游戏的设计和实现

目录

扫雷游戏简述:

效果图

1.选择目录界面

2.初始界面

3.游戏界面

4.失败界面

基本编写逻辑:

编写初始界面及运行逻辑

扫雷的基本逻辑

扫雷界面的创建

随机布雷

玩家排雷

排雷的逻辑判断

完整代码:

game.h

game.c

test.c


扫雷游戏简述:

“《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。”

在一个9×9(初级)、16×16(中级)、16×30(高级)或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个),再由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第1张图片

首先扫雷的规则很简单,就是不能点到雷,每一局扫雷的新游戏里面玩家第一次都不会点到雷(注意如果重复上局布局的雷就有可能踩到雷)。

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第2张图片

我们随便点一颗格子,就是出现随机情况,圆圈标记的那个3,代表以3为中心,周围八个格子里就会有三个雷。同理所有的数字都是这个意思,用这种方法我们就可以确定周围雷的数量,点击右键就可封雷,最后找出所有的雷即为胜利。

用C语言实现扫雷游戏并不困难,需要用到以下知识:

  • 自定义函数及实现;
  • 二维数组的定义和使用;
  • 循环和判断语句的使用等;

效果图

首先放一张VS2019运行的效果图:

1.选择目录界面

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第3张图片

 2.初始界面

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第4张图片

 3.游戏界面

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第5张图片

4.失败界面

【C语言/入门游戏设计】简单扫雷游戏的设计和实现_第6张图片

基本编写逻辑:

编写初始界面及运行逻辑

 首先我们编写选择界面,假定输入1可以进行游戏,输入0退出游戏,这是一个switch语句。又因为无论输入几,都需要执行一次输出目录,而且玩完一次后可以继续玩游戏,故使用do--while循环语句

int main()
{
	srand((unsigned int)time(NULL)); //生成随机数,一会儿会说到
	int input = 0;
	do
   //用do while循环保证菜单至少被打开一次
	{
		menu();
        //需要定义menu()函数
		printf("Please input(1/0):>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//printf("game\n");
			game();
			break;
		case 0:
			printf("exit the game\n");
			break;
		default:
			printf("unknown input,please retry");
			break;
		}

	} while (input);
	return 0;
}

//如果选择0,会判定为假,跳出循环,结束游戏;选择其他则不会,保证游戏可以重复游玩
//这是基本的游戏界面的编写程序,需要掌握。

 menu()函数,只需要简单的阐述清楚,应该输入 1 / 0 ,1为开始游戏, 0为退出游戏。

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

扫雷的基本逻辑

 首先我们思考一个问题,如果我们需要创建9*9的初级难度扫雷棋盘,我们需要先随机布雷,在上一篇井字棋中我们已经讲到过,几×几的棋盘可以使用二维数组去创建。我们可以通过输入9*9棋盘上的坐标的方法进行探雷,然后系统打印输出带我们刚刚输入点周围雷的信息的棋盘出来。直至我们输入的坐标是雷点(游戏失败)或者我们挑选出了所有的雷区(游戏成功)为止。

这就是我们的基本逻辑,但是:如果我们只设置一个棋盘的话,打印的时候会把雷的信息都打印出来,游戏可玩性为0,所以我们需要设置两个棋盘,一个为布雷棋盘(程序中用mine表示),一个是玩家可以看到的玩家棋盘(程序中用show表示)

为了代码的可编辑性,容易修改,我们在game.h头文件中先定义宏:

#define ROW 9 //行
#define COL 9 //列
#define NUM_BOOM 20  //炸弹数目
#define ROWS ROW+2
#define COLS COL+2

 定义好宏之后,我们可以随时根据需要调整行和列和炸弹的值,方便快捷地对全局进行修改。

不知道大家对定义ROWS是否有疑问?这是因为如果我们定义一个9*9的数组,在计算输入坐标值周围的地雷数时容易出现越界的情况,所以我们直接定义ROWS为ROW+2,不会出现越界的情况。

扫雷界面的创建

按上述的方法,我们创建两个棋盘分别作为雷盘和玩家盘。并对这两个棋盘都进行初始化。

	char mine[ROWS][COLS] = { 0 };// = { '0' }错误,不完全初始化//存放布置雷的信息
	char show[ROWS][COLS] = { 0 };// = { '*' };//存放排查雷的信息

对其进行初始化,初始化时注意一个问题,比如我们想把它们两个分别初始化为 ‘0’ 和 ‘*’ ,那么如果用一个函数难以实现,除非在函数的参数中设置好需要传过去的字符,如此:

//主函数中进行初始化传参
InitBoard(mine, ROW, COL,'0');
InitBoard(show, ROW, COL,'*');
void InitBoard(char arr[ROWS][COLS], int x, int y,char set)
{
	for (int i = 0; i <= x ; i++)
	{
		for (int j = 0; j <= y; j++)
		{
			arr[i][j] = set;
		}
	}
}

 这样就对二维数组的每个元素进行了赋值,全部都是相同的字符元素。

对棋盘进行初始化之后,需要打印,也就是摆放出数组中的每个元素。编写函数:

void DisplayBoard(char arr[ROWS][COLS], int x, int y)
{
	printf("----------GAME ZONE----------\n");
	printf(" ");
	for (int j = 1; j <= y; j++)
	{

		printf(" %d ", j);
	}
	printf("\n");
	for (int i = 1; i <= x; i++)
	{
		printf("%d", i);
		for (int j = 1; j <= y; j++)
		{
			printf(" %c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("----------GAME ZONE----------\n");
}

但是我们还是缺少非常关键的一步,随机摆放地雷。

随机布雷

随机值的生成方法我们在井字棋已经学习过,是使用

srand((unsigned int)time(NULL));

编写函数:

void Setboom(char arr[ROWS][COLS], int x, int y,int num)
{
	// 1 - 9 的数字 随机生成
	// 不能重复放置在一个位置
	int ret = 0;
	while(ret < num)
	{
		int a = rand() % x + 1;
		int b = rand() % y + 1;
		if (arr[a][b] != '1')
		{
			arr[a][b] = '1';
			ret++;
		}
	}
}

// rand() % x可以生成0~x-1 的数字, rand() % x + 1 就可以生成 1 ~ x
// 如果用for循环,不太方便,因为可以重复放置在同一位置。
// 用while 可以编写成只有放一颗雷才加一个数值,比较切合应用。

 玩家排雷

玩家的排雷首先要输入棋盘中的一个坐标,输入后首先检查在布雷的棋盘上是否有雷,有雷直接结束,无雷则在玩家棋盘上该坐标位置上输出周围雷的个数。

int Checkboom(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int x, int y)
{
	int a = 0;
	int b = 0;
	int ret = 0;
	while (1)
	{
		printf("Please choose an address to check:>");
		scanf_s("%d %d", &a, &b);
		if (arr2[a][b] != '*')
			printf("You have checked this address,please input again!\n");
		else
		{
			if (a <= x && a > 0 && b <= y && b > 0)
			{
				//炸弹爆炸
				if (arr1[a][b] == '1')
				{
					ret = 1;
					return ret;
				}
				//检查一周有几个炸弹
				else
				{
					char num = NUM(arr1, a, b);
					arr2[a][b] = num;
					//还得打印一遍show
					break;
				}
			}
			else
				printf("unknown address,please input again!\n");

		}
	}
	int flag = 0;
	int amount = ROW * COL - num_boom;
	for (int i = 1; i <= x; i++)
	{
		for (int j = 1; j <= y; j++)
		{
			if (arr2[i][j] != '*')
				flag++;
		}
	}
	if (flag == amount)
		return 2;
	return 0;
}
//假定,输出0则继续重复check阶段,
//输出1则爆炸游戏结束
//输出2则玩家胜利

 其中用到了NUM作为计算输入点的周围雷总数的函数,其运行逻辑为挨个计算,有则加1,但是因为棋盘本身都为char类型,我们需要char类型的计数方法,需要掌握!!!

char NUM(char arr[ROWS][COLS],int a, int b)
{
	char first = '0';
	char num = '0';
	for (int x = a - 1; x <= a + 1; x++)
	{
		for (int y = b - 1; y <= b + 1; y++)
		{
			if (arr[x][y] == '1')
			{
				num = first + 1;
				first = num;
			}
		}
	}
	return num;
}

其实就是初始化了一个变量并赋值为'0',每符合条件一次则加1,进行计数,只适合10以内的计数。

 排雷的逻辑判断

使用CheckBoom函数后,该函数的返回值对应着不同的情况,这时候我们需要对该函数的返回值进行判断,进而判定后续程序该如何运行。

while(1)
	{
		排查雷,
		int c = Checkboom(mine, show, ROW, COL);
		if (c == 1)
		{
			printf("BOOM! GAME OVER!\n");
			printf("5Second later you will choose to play again or not\n");
			Sleep(5000);
			system("cls");
			break;
		}
		else if (c == 2)
		{
			printf("Congratulations! You win the game!\n");
			printf("5Second later you will choose to play again or not\n");
			Sleep(5000);
			system("cls");
			break;
		}
		else
		{
			打印show
			system("cls");
			DisplayBoard(show, ROW, COL);
		}

	}
}

按上述的顺序,我们的扫雷游戏的每个部分就全部都写好啦。下面我们贴上完整代码:

完整代码:

game.h

#pragma once
#include
#include
#include
#define ROW 9
#define COL 9
#define num_boom 20
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char arr[ROWS][COLS], int x, int y,char set);
void DisplayBoard(char arr[ROWS][COLS], int x, int y);
void Setboom(char arr[ROWS][COLS], int x, int y,int num);
int Checkboom(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int x,int y);
char NUM(char arr[ROWS][COLS], int a, int b);

game.c

#include"game.h"
#define _CRT_SECURE_NO_WARNINGS
void InitBoard(char arr[ROWS][COLS], int x, int y,char set)
{
	for (int i = 0; i <= x ; i++)
	{
		for (int j = 0; j <= y; j++)
		{
			arr[i][j] = set;
		}
	}

}
void DisplayBoard(char arr[ROWS][COLS], int x, int y)
{
	printf("----------GAME ZONE----------\n");
	printf(" ");
	for (int j = 1; j <= y; j++)
	{

		printf(" %d ", j);
	}
	printf("\n");
	for (int i = 1; i <= x; i++)
	{
		printf("%d", i);
		for (int j = 1; j <= y; j++)
		{
			printf(" %c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("----------GAME ZONE----------\n");
}
void Setboom(char arr[ROWS][COLS], int x, int y,int num)
{
	int ret = 0;
	while(ret < num)
	{
		int a = rand() % x + 1;
		int b = rand() % y + 1;
		if (arr[a][b] != '1')
		{
			arr[a][b] = '1';
			ret++;
		}
	}
}
char NUM(char arr[ROWS][COLS],int a, int b)
{
	char first = '0';
	char num = '0';
	for (int x = a - 1; x <= a + 1; x++)
	{
		for (int y = b - 1; y <= b + 1; y++)
		{
			if (arr[x][y] == '1')
			{
				num = first + 1;
				first = num;
			}
		}
	}
	return num;
}

int Checkboom(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int x, int y)
{
	int a = 0;
	int b = 0;
	int ret = 0;
	while (1)
	{
		printf("Please choose an address to check:>");
		scanf_s("%d %d", &a, &b);
		if (arr2[a][b] != '*')
			printf("You have checked this address,please input again!\n");
		else
		{
			if (a <= x && a > 0 && b <= y && b > 0)
			{
				if (arr1[a][b] == '1')
				{
					ret = 1;
					return ret;
				}
				else
				{
					char num = NUM(arr1, a, b);
					arr2[a][b] = num;
					break;
				}
			}
			else
				printf("unknown address,please input again!\n");

		}
	}
	int flag = 0;
	int amount = ROW * COL - num_boom;
	for (int i = 1; i <= x; i++)
	{
		for (int j = 1; j <= y; j++)
		{
			if (arr2[i][j] != '*')
				flag++;
		}
	}
	if (flag == amount)
		return 2;
	return 0;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

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

game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	InitBoard(mine, ROW, COL,'0');
	InitBoard(show, ROW, COL,'*');
	Setboom(mine, ROW, COL, num_boom);
	system("cls");
	DisplayBoard(show, ROW, COL);
	while(1)
	{

		int c = Checkboom(mine, show, ROW, COL);
		if (c == 1)
		{
			printf("BOOM! GAME OVER!\n");
			printf("5Second later you will choose to play again or not\n");
			Sleep(5000);
			system("cls");
			break;
		}
		else if (c == 2)
		{
			printf("Congratulations! You win the game!\n");
			printf("5Second later you will choose to play again or not\n");
			Sleep(5000);
			system("cls");
			break;
		}
		else
		{
			system("cls");
			DisplayBoard(show, ROW, COL);
		}

	}
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("Please input(1/0):>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//printf("game\n");
			game();
			break;
		case 0:
			printf("exit the game\n");
			break;
		default:
			printf("unknown input,please retry");
			break;
		}

	} while (input);
	return 0;
}

以上就是扫雷游戏的编码的全部内容啦。本人本来希望继续完善程序,可以进行输入选择难度(9*9,16*16,16*25),但是因为本人系C语言初学者,仍未搞清楚如何在程序中修改宏(因为最一开始在game.h中定义了行和列的宏),或者其他的方法,仍求大佬教教!

最后,码字不易,希望大家的赞和关注的支持 谢谢大家!

你可能感兴趣的:(学习感悟,游戏,c语言,开发语言)