小心踩雷!手把手教你制作扫雷游戏简单版本

扫雷游戏----C语言必写游戏之一


背景

扫雷在科技历史上也扮演了相似的角色。这个基于数字的逻辑谜题最早来自20世纪六七十年代,当时Jerimac Ratliff推出的名为“Cube”的游戏已经非常受人欢迎。几十年后的1992年,扫雷游戏被加入了Windows3.1,这并不是为了展示Windows是游戏操作系统专家,而是为了训练用户的鼠标左右键操作能力,让这些动作变得非常自然,并培养鼠标移动的速度和准确性。

该介绍来自百度百科


起源与玩法

起源:
扫雷最原始的版本可以追溯到1973年一款名为“方块”的游戏。
不久,“方块”被改写成了游戏“Rlogic”。在“Rlogic”里,玩家的任务是作为美国海军陆战队队员,为指挥中心探出一条没有地雷的安全路线,如果路全被地雷堵死就算输。两年后,汤姆·安德森在“Rlogic”的基础上又编写出了游戏“地雷”,由此奠定了现代扫雷游戏的雏形。
1981年,微软公司的罗伯特·杜尔和卡特·约翰逊两位工程师在Windows3.1系统上加载了该游戏,扫雷游戏才正式在全世界推广开来。
玩法:
这款游戏的玩法是在一个9*9(初级),16*16(中级),16*30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束

该介绍来自百度百科

在这里插入图片描述
比如上面这个扫雷盘,当点击那一个方块时,弹出来数字1,即表示在那个方块周围有一个雷,也就是上面标出的黑色矩形所在位置上有一个雷,只要排查出所有的雷,那么就取得了胜利


基本实现思路

在这里插入图片描述

在这里插入图片描述


涉及语言的主要内容

1.if、switch、for循环、while循环、do...while循环
2.宏定义、break的用法等
3.二维数组的创建、初始化、数组传参等
4.函数的声明、定义、调用等
5.布置雷需要生成随机数、rand()函数的使用等


具体实现步骤

1.基本框架

基本框架十分重要,如果连框架都搭建不起来,那么就更不用去谈论写一个完整的扫雷小游戏代码了。首先我们需要考虑大致实现的功能。
1.程序一运行就弹出菜单页面
2.可以对菜单上的内容进行选择
3.如果不小心选择失误,要能够实现再次选择
因此我们写出如下代码:

int main(void)
{
	int input = 0;
	do
	{
		//打印菜单
		Meau();
		printf("请选择菜单:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("退出游戏!\n");
			break;
		case 2:
			//进入游戏
			Game();
			break;
		case 3:
		    //游戏玩法介绍
			PlayWay();
			break;
		default:
			printf("选择有误,请重新输入\n");
			break;
		}
	} while (input != 1);
	return 0;
}

这样,我们的基本框架就已经搭建好了~


2.菜单页面的实现

在菜单的页面需要认真排版,因为菜单就像一家店的门面一样,如果排版不好,那么兴趣也就提不上来。

void Meau()
{
	printf("********************************\n");
	printf("********** 1.退出游戏  *********\n");
	printf("********** 2.开始游戏  *********\n");
	printf("********** 3.游戏玩法  *********\n");
	printf("********************************\n");
}

3.初始化棋盘

经过上面的步骤我们就要准备开始游戏了。现在游戏里还是一片空白,因此我们要有一个棋盘。对于棋盘,我们要实现的是,创建出一个空棋盘,然后再放置雷。
棋盘是一个二维的东西,因此我们采用二维数组。
为了增加用户的体验效果,我们还需要能够对棋盘的大小进行修改,此处采用宏定义的方式。为什么呢?
宏定义有两个优点:
(1) 方便程序的修改 使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时,我们可以用较短的有意义的标识符来写程序,这样更方便一些。
(2) 提高程序的运行效率。

但是这时我们就会发现:我们很难确定边界的坐标周围有多少个‘雷’,因此我们实际的棋盘大小要更大一些,但呈现在玩家面前的只有中间那一块的棋盘。否则实际上会出现数组越界。
在这里插入图片描述

在实现思路里我说过我才用分块去写,因此声明和定义存放在头文件中

#pragma once

//行
# define ROW 9
//列
# define COL 9

# define ROWS ROW + 2

# define COLS COL + 2

这里需要创建两个二维数组,一个用来存放雷的信息,另一个用来存放排查出雷的信息

	char mine[ROWS][COLS] = { 0 }; //存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 }; //存放排查出的雷的信息

这个时候我们要让创建的棋盘内什么东西都不存放,或者存放一致的东西,方便以后在棋盘中布置雷,因此我们要将棋盘初始化。

//初始化棋盘
void Init_Board(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; //将棋盘全部初始化为set这个传过来的参数
		}
	}
}

此处最后一个参数是方便自己来初始化棋盘。


4.在屏幕中显示棋盘

根据上面的初始化棋盘,我们知道呈现在玩家面前的只有中间那一块的棋盘。也就是只需要打印中间绿色的那一块棋盘。在这里插入图片描述
虽然我们只是操作绿色部分9*9的那一块,但我们在传参的时候依旧传过去的是11*11的棋盘。因此在调用时打印棋盘函数时需要注意参数!

//调用
DisplayBoard(mine, ROW, COL); //实参
//定义
void DisplayBoard(char board[ROWS][COLS], int row, int col)//形参
{

}

为了方便用户游戏,我们还需要将棋盘的列和行信息打印出来。于是得到下面的函数。

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//实现对棋盘列数的信息提示
	for (i = 0; i <= 9; ++i)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; ++i) 
	{
		printf("%d ", i); //实现对棋盘行数的信息提示
		for (j = 1; j <= col; ++j) //因为打印的是中间的棋盘,所以下标从1开始
		{
			printf("%c ", board[i][j]);
		}
		printf("\n"); //换行
	}
	printf("-------------------\n");//美化界面
}

5.布置好棋盘中的雷

初始化棋盘之后,棋盘中并没有雷,因此我们要开始在棋盘中布置雷。EASY_COUNT(雷的个数)在头文件中进行了宏定义。布置雷要涉及到随机数的生成。要使得电脑随机布置雷,这样才能够更好地实现扫雷游戏。在这里我们把字符‘1’当作雷。

//在棋盘中放置雷
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] != '1') //防止上一次这个位置已经布置雷
		{
			board[x][y] = '1';
			count--; //布置了雷才减少一次,因此要放在if里面
		}
	}
}

6.排查出棋盘中的雷

这里涉及到函数的嵌套调用,请仔细看,不然很容易被绕晕。

//统计查找时方块周围雷的个数
int GetMineCount(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 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 (mine[x][y] == '1')
			{
				printf("炸死了吧!下次小心点,别在踩雷了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0'; //让点出来的格子显示它的周围有几个雷
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

扫雷游戏完整代码

头文件:

# include <stdio.h>
# include <stdlib.h>
# include <time.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);

game.c文件:

# include "game.h"

void Meau()
{
	printf("********************************\n");
	printf("********** 1.退出游戏  *********\n");
	printf("********** 2.开始游戏  *********\n");
	printf("********** 3.游戏玩法  *********\n");
	printf("********************************\n");
}

void Game()
{
	char mine[ROWS][COLS] = { 0 }; //存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 }; //存放排查出的雷的信息

	//初始化扫雷盘 
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//在屏幕上显示棋盘
	DisplayBoard(show, ROW, COL);

	//布置雷
	SetMine(mine, ROW, COL);

	//排查出棋盘中的雷
	FindMine(mine, show, ROW, COL);
}

int main(void)
{
	int input = 0;
	srand((unsigned)time(NULL)); //设置生成起点
	do
	{
		//打印菜单
		Meau();
		printf("请选择菜单:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("退出游戏!\n");
			break;
		case 2:
			//进入游戏
			Game();
			break;
		case 3:
			//游戏玩法介绍
			PlayWay();
			break;
		default:
			printf("选择有误,请重新输入\n");
			break;
		}
	} while (input != 1);
	return 0;
}

test.c文件:

# 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; //将棋盘全部初始化为set这个传过来的参数
		}
	}
}

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//实现对棋盘列数的信息提示
	for (i = 0; i <= 9; ++i)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; ++i) 
	{
		printf("%d ", i); //实现对棋盘行数的信息提示
		for (j = 1; j <= col; ++j) //因为打印的是中间的棋盘,所以下标从1开始
		{
			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] != '1') //防止上一次这个位置已经布置雷
		{
			board[x][y] = '1';
			count--; //布置了雷才减少一次,因此要放在if里面
		}
	}
}

//统计查找时方块周围雷的个数
int GetMineCount(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 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 (mine[x][y] == '1')
			{
				printf("炸死了吧!下次小心点,别在踩雷了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0'; //让点出来的格子显示它的周围有几个雷
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

最后运行结果

这里为了方便演示结果,我在棋盘中放置了40个雷,同志们修改雷的个数请随意就好。
这是选择开始游戏的运行结果:
在这里插入图片描述
这里选择选项3的运行结果:
在这里插入图片描述

总结

虽然是一个很简单的游戏,但是却能够很巧妙地包含了C语言所涉及的内容。所有想要步入编程之路的小伙伴们,都值得一试!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小酥诶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值