C语言初阶——扫雷小游戏

扫雷这款小游戏想必大家都接触到过,但是用C语言写的扫雷你见过吗?其实,学完之前我写的那几篇文章,我们完全可以自己写出来。仅仅200多行代码,我们就可以写出我们自己的扫雷小游戏!赶紧来看看吧!!!

1.游戏攻略

1.1游戏介绍

首先电脑会给我们展示一个棋盘,棋盘由一定数量的方格子组成,一定数量的地雷会被随机安置在方格子底下。我们通过鼠标来点击方格子。如果方格子下面是地雷,则游戏失败。如果不是地雷,就会显示一个数字。数字代表的意义就是,这个格子周围地雷的数量。游戏的难度可以通过改变棋盘的大小和地雷的数量,进行调整。
请添加图片描述

当我们把所有的没有埋置地雷的格子排除后,则游戏胜利。就像这样:
请添加图片描述
哈哈,大家不必被我的战绩震惊,因为我发现了网页版扫雷的bug,可以透视雷阵,感兴趣的小伙伴可以私信我。下边是网页版扫雷游戏的链接,大家可以去尝试一下。添加链接描述

1.2 游戏技巧

能够快速取得游戏胜利的关键,就是要学会快速推演。能够将相邻或相近格子显示的数字信息相联系,然后推测出哪个格子必定没有埋置地雷。这样就可以一步步拓宽我们的版图。当然一开始建议从边角的格子出开始,因为边角处的格子,与之相邻的格子数较少,更有助于我们排查。

2.制作流程

正所谓,谋定而后动。在正式写代码之前,我们必须先思考,设计好游戏的流程和框架。然后在我们写代码的时候思路才会更加清晰,速度会更快,写出的代码问题也更少。

2.1开始菜单

当我们随便打开一个游戏,往往就会出现一个菜单供我们选择。我们可以选择游戏的模式,还有玩家的数量等等,比如:
请添加图片描述
铁子们激动不?别着急,一会儿我们就可以写出我们的,C语言版扫雷的游戏的开始界面了(虽然比较捡漏)。
那既然提到了游戏菜单,肯定是要让我们进行选择,这里大家就不比担心了。因为C语言中有分支语句,就可以实现选择的功能。如果大家忘记的话,现在可以去看一看我之前写过的分支和循环语句那一篇文章。

2.2创建雷阵

扫雷游戏怎么着也得有雷阵吧,要不然咱们将地雷布置到天上?提到雷阵,在C语言中可以用什么工具来实现它呢?我想二维数组或许就可以吧。我们可以通过二维数组的行数和列数来控制雷阵的大小,大家如果忘记了数组的知识可以去看看我之前写过的数组那篇文章。
扫雷扫雷,怎么着也得有雷吧,那我们用什么来代表雷呢?大家知道,每一次选择只有两种情况,要么是地雷,要么就是代表地雷个数的数字。这里我们用字符1来代表雷,字符0代表无雷。接下来我们埋置地雷。

2.3布置地雷

首先地雷肯定不能是我们手工一个个埋置,如果这样那这就不算一个好游戏了,地雷必须是随机布置。提到随机,大家肯定能想到我在之前文章里曾经用时间戳,srand和rand函数来生成随机数吧,这次咱们就通过这个来布置地雷!

2.4操作界面

我们布置好了雷阵,那接下来该怎么玩呢?我们必须将雷阵的信息进行隐藏,然后玩家每扫一次,我还要将需要提供给玩家的信息,也就是周围地雷的数量展示给玩家。这里我们可以创建一个和雷阵一样大小的数组,不同的是,我初始化的时候将所有的数组元素初始化为星号:* ,这样既美观又可以很好的隐藏雷阵的信息。然后当玩家玩的时候,我们再将必要的信息通过这个数组反馈给玩家。

2.5排雷

玩家玩的时候该怎样排雷呢?我们又没有图形化界面。
这里我们通过输入坐标的方式,玩家输入想要排查的坐标即可。关于坐标我们在创建二维数组的时候,就可以提前设置好。

2.6游戏结局

如果玩家赢了,结果只能是他把所有没有地雷的地方都扫过了。这里我们自然可以想到,通过地雷的数量和玩家已经扫过的坐标数来判断,玩家是否胜利。玩家胜利电脑要宣告玩家并退回游戏开局选择界面,玩家踩到地雷也要退回那个界面。

2.7相应补充

大家在玩扫雷游戏的时候有没有注意到,当我们点击一个格子,如果那个格子周围的格子要是没有地雷的话,就会一下子炸开一片区域,如下:
请添加图片描述
我点击最左下角的格子,相应的就会显示一片。这个功能呢实现起来比较复杂,还要用到递归的知识,为了防止把大家弄晕(其实是把我弄晕),这篇文章就不再讲解,大家感兴趣可以自己琢磨一下。
好了,关于游戏的设计思路我们就先讲到这里,接下来就让我们一步步将它实现吧!
这里提前声明一点:为了使代码更加简洁,可读性和逻辑性更强,往往代码要用函数进行封装。我们计划创建3个文件。test.c里面放相应的逻辑和框架,game.c专门放函数,然后相应的头文件,常量,函数的声明等均在game.h里面引用和声明,然后其他两个文件只需要#include"game.h"即可。

3.游戏菜单

我们通过menu函数来打印游戏菜单,然后通过switch分支语句来接收玩家的选择,相应的代码如下:

void menu()
{
	printf("***********************************\n");
	printf("***********   1. play   ***********\n");
	printf("***********   0. exit   ***********\n");
	printf("***********************************\n");
}
void test()
{
	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);
}
int main()
{
	test();
	return 0;
}

请添加图片描述
这样当我们选择1,就可以玩游戏了。

4.初始化雷阵

我们通过二维数组的方式来创建雷阵,然后将所有的数组元素初始化为字符0。我们应该创建一个怎样的二维数组呢?我想我们的二维数组应该比雷阵大出一周,这样会方便计算边上坐标其四周地雷的数量。同时,多出来的行和列正好可以用来打印游戏里面需要的横纵坐标。
但是雷阵有大小,,为了方便改动,这里我们通过下面的方式来创建雷阵。我们在game.h头文件里:

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

日后我们就可以在这里改动游戏的参数!
我们的设计思路是创建两个二维数组,一个放雷阵,另一个创建我们的操作界面,因此我们可以常见一个初始化函数,来实现这个效果,代码如下

//test.c
void game()
{
	//创建数组
	char mine[ROWS][COLS] = { 0 };//布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//排查出的雷的信息
	//初始化mine数组为全字符‘0’
	InitBoard(mine, ROWS, COLS, '0');
	//初始化show数组为全‘*’
	InitBoard(show, ROWS, COLS, '*');
}


//game.c
	void InitBoard(char board[ROW][COL], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 1; i <= rows; i++)
	{
		for (j = 1; j <= cols; j++)
		{
			board[i][j] = set;
		}
	}
}

接下来我们在创建一个打印函数,通过打印二维数组的方式,打印我们已经初始化完毕的两个数组:

//game.c
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//列号的打印
	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");
	}
}
//test.c
void game()
{
	//打印棋盘
	DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
}

看看我们打印出来的结果如何吧!
请添加图片描述
没有任何的问题。那接下来就让我们一起布置雷阵吧。

5.布置雷阵

同样,为了能够方便改动地雷的数量,我们在game.h里

#define COUNT 10

这里,9*9的雷阵我们就先放10课地雷吧。当然,日后可以随时改动。
然后,我们同样创建布置地雷的函数,代码如下:

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int x = rand() % row + 1;//这个套路大家应该都不陌生了!
		int y = rand() % col + 1;

		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

我们将我们布置好的雷阵打印出来看看:
请添加图片描述
嗯,没问题,10个‘1’正好代表10个地雷!接下来我们开始排雷!

6.排雷

我们通过输入坐标的方式进行排雷,排雷的过程中需要统计坐标周围地雷的数量。同时每排一次雷还需要通过打印的方式,反馈给玩家相应的信息,每排一次就要打印一次。同时当游戏结束后,还需要返回选择页面。这里我们再创建一个函数来实现这些功能,相应的代码如下:

//game.c
static int get_mine_count(char mine[ROWS][COLS],int x,int y)//这里不必在game.h里面里声明,直接用就行
{
	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';//字符内容转换为数字
}//加上static 这个函数就只能在game.c文件内使用


void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x;
	int y;
	int win = 0;
	while (win < row * col - 10)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标必须合法
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了\n");
				DisplayBoard(mine, row, col);//死不瞑目?
				break;//这里跳出while循环,游戏结束
			}
			else
			{
				//计算(x,y)坐标周围有几个雷
				int n = get_mine_count(mine, x, y);
				show[x][y] = n + '0';//我们想要的是字符'n',而不是数字n
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入坐标非法,无法排雷,重新输入\n");
		}
	}
	if (win == row * col - 10)
	{
		printf("恭喜你!排雷成功!!!\n");
		DisplayBoard(show, row, col);
	}
}

好了,到这里。我们游戏代码的基本功能就基本实现了,接下来就让我们一起来看看效果吧!

7.游戏效果

请添加图片描述
嗯~真香!!!这里我就不再继续玩下去了,因为玩下去我肯定就赢了,人家计算机不要面子的吗?
那关于扫雷游戏的代码,我就放在下面了,大家可以放在自己的电脑上玩一玩。
下一篇文章呢,就让我们一起用C语言来写一个叫三子棋的游戏吧!!!

8.游戏代码

8.1 test.c

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, '*');

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	//DisplayBoard(show, ROW, COL);
	//布置雷阵
	SetMine(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	//排雷
	FineMine(mine, show, ROW, COL);

}
void test()
{
	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);
}
int main()
{
	test();
	return 0;
}

8.2 game.c

void InitBoard(char board[ROW][COL], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 1; i <= rows; i++)
	{
		for (j = 1; j <= cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//列号的打印
	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");
	}
}

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int x = rand() % row + 1;//这个套路大家应该都不陌生了!
		int y = rand() % col + 1;

		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}


static int get_mine_count(char mine[ROWS][COLS],int x,int y)//这里不必在game.h里面里声明,直接用就行
{
	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';//字符内容转换为数字
}//加上static 这个函数就只能在game.c文件内使用


void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x;
	int y;
	int win = 0;
	while (win < row * col - 10)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标必须合法
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了\n");
				DisplayBoard(mine, row, col);//死不瞑目?
				break;//这里跳出while循环,游戏结束
			}
			else
			{
				//计算(x,y)坐标周围有几个雷
				int n = get_mine_count(mine, x, y);
				show[x][y] = n + '0';//我们想要的是字符'n',而不是数字n
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入坐标非法,无法排雷,重新输入\n");
		}
	}
	if (win == row * col - 10)
	{
		printf("恭喜你!排雷成功!!!\n");
		DisplayBoard(show, row, col);
	}
}

8.3 game.h

#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define 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 mine[ROWS][COLS], int row, int col);

//排雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗月の流星劫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值