C语言小游戏——扫雷

目录

🍁(一)扫雷游戏原理

🍁(二)、 根据以上规则,给出以下思路

🍁(三)、具体实现

🌕1.此次我们依旧采用多文件的方式进行实现,如下:

🌕 2.由上,我们定义了两个二维字符数组,所以接下来我们要考虑初始化的问题。

🌕3.值得注意的是:

🌕 4.因为此处我们再次会应用到数组的行和列,所以再次用define宏定义行列,一个9*9,一个11*11,以及雷的个数,方便分开使用,如下:

🌕5.开始初始化雷盘

🌕6.雷盘初始化后

🌕效果如图:

🍁(四)、以上操作效果如下:

🍁 结尾:


今天又学习到一个有趣的东西,用C语言实现扫雷游戏,下面就让小编带领大家一步一步入手吧。

🍁(一)扫雷游戏原理

(1).首先给定一个雷盘

 (2).在雷盘上任意点击一个格子,若点到雷,则游戏结束,若不是雷则继续选择

 

(3).注意棋盘上面的数字,如下:

 表示以此位置为中心,周围八个格子中有两个雷

(4).还有一个规则:如果所选择的该位置周围的格子的周围的八个格子中没有雷,则会自动显示,以此类推,直到遇到周围八个格子中至少有一个雷则停止显示。

(5).当雷盘上面只有雷未显示时,游戏胜利。

🍁(二)、 根据以上规则,给出以下思路

(1).对于雷盘,很容想到用一个二维数组来表示,(这里我们以9*9表示),并且我们看到没点击一个位置,就会有相应雷盘显示,所以我们想到用两个二维字符数组。

数组一mine:用于布置雷的信息。

数组二show:用于显示排查出的雷的信息。

(2).棋盘设置好后就该存放雷,因为位置随机,所以采用随机数的知识。

(3).排查雷:由玩家输入数组坐标,然后进行判断,直到游戏结束。

🍁(三)、具体实现

🌕1.此次我们依旧采用多文件的方式进行实现,如下:

(1).main函数内容放入文件test.c,并且考虑到玩游戏可以重复进行,所以采用do while()循环套用switch case语句作为主体结构,如下:

#include"game.h"

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

void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	//Display(mine, ROW, COL);
	Display(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	//Display(mine, ROW, COL);
	//Display(show, ROW, COL);
	//排查雷
	FineMine(mine, show, ROW, COL);

}

int main()
{
	srand((unsigned int)time(NULL));
	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;
}

(2).各函数的声明放入文件game.h,考虑到各.c文件中引用此.h文件,所以对于一些库函数,define定义的常量,我们可以放入此game.h文件,这样就不用重复使用了,如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>


#define Mine_count 10
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9

void InitBoard(char board[ROWS][COLS], int row, int col, char ret);//初始化

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

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

void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

int GetMineCount(char mine[ROWS][COLS],int x, int y);//返回坐标(x,y)周围的雷数

(3).各函数的定义,我们放入game.c文件,如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ret;
		}
	}
}


void Display(char board[ROWS][COLS], int row, int col)//打印
{
	int i = 0;
	printf("------扫雷游戏------\n");
	for (i = 0; i < col + 1; i++)//列号
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < row+1; i++)
	{
		printf("%d ", i);//行号
		int j = 0;
		for (j = 1; j < col+1; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{

	int count = Mine_count;
	while (count)
	{
		int x = rand() % row + 1;
 		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查
{
	int x = 0;
	int y = 0;
	int count1 = 0;
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &x, &y);
		if (x > 0 && x < row + 1 && y>0 && y < col + 1)
		{
			if (mine[x][y] != '1')
			{
				int count = GetMineCount(mine,x,y);
				show[x][y] = count+'0';
				Display(show, ROW, COL);
				count1++;
			}
			else
			{
				printf("很遗憾,你被炸死了!\n");
				system("pause");
				system("cls");
				break;
			}

		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
		if (count1 >= row * col - Mine_count)
		{
			printf("恭喜你,游戏胜利!\n");
			Display(show, ROW, COL);
			system("pause");
			system("cls");
			break;
			

		}
	}
}

int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,y)周围的雷数
{
	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';
}

🌕 2.由上,我们定义了两个二维字符数组,所以接下来我们要考虑初始化的问题。

(1).数组一mine:用来存放雷的信息。

我们想到此数组中用‘0’表示非雷,‘1’表示雷,(这样的做的好处将在之后介绍),所以刚开始mine数组初始化为‘0’。

(2).数组二show:用来存放排查雷的信息。

我们想到刚开始初始化为‘*’,作用就是单纯为了美观与显眼,并在玩家选择位置之后显示相应的雷的信息。

🌕3.值得注意的是:

 对于以上这种情况,处于边缘的位置的格子,只需要考虑雷盘以内的地方,而盘外的地方是没有雷的,所以我们对于9*9的雷盘,我们想到将其长宽扩大一格,即用一个11*11的数组来表示,而多出来的格子默认为非雷,即初始化为‘0’ / ‘*’,并且在显示雷盘时,只显示中间9*9的区域。这样做的目的是为了后续方便计算边缘的位置周围的雷的个数。

🌕 4.因为此处我们再次会应用到数组的行和列,所以再次用define宏定义行列,一个9*9,一个11*11,以及雷的个数,方便分开使用,如下:

#define Mine_count 10
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9

🌕5.开始初始化雷盘

这里有个小细节,mine和show数组是同行同列的,所以两者只需要使用同一个初始化函数,将自己的内容也作为一个参数,即可分开初始化,即创建一个这样的函数(void InitBoard(char board[ROWS][COLS], int row, int col, char ret)),对于二维数组的初始化,我们很容易想到双重for循环,代码实现如下:

void InitBoard(char board[ROWS][COLS], int row, int col, char ret)//初始化
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ret;
		}
	}
}

🌕6.雷盘初始化后

我们还需要打印雷盘(二维数组的打印也是用到双重for循环),由上我们说明到mine和show数组的作用,所以我们在选择位置时所看到的的应该是show函数,并且为了美观与方便,我们可以将行列号也打印出来,代码实现如下:

void Display(char board[ROWS][COLS], int row, int col)//打印
{
	int i = 0;
	printf("------扫雷游戏------\n");
	for (i = 0; i < col + 1; i++)//列号
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < row+1; i++)
	{
		printf("%d ", i);//行号
		int j = 0;
		for (j = 1; j < col+1; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

🌕效果如图:

 7.雷盘打印实现后我们就开始布置雷,创建SetMine函数实现。据上,此时我们操作的是mine数组

(1).因为雷的位置不定,所以我们使用到随机数,随机产生一些坐标布置雷,并且雷的个数为自定义的

(2).坐标产生后还应该判断一下此处是否已经布置过雷了,若布置过了,则重新生成坐标,否则将此处的‘0’换成‘1’,以表示布置雷,成功布置一次,计数器减1,用于最后出控制循环

代码实现如下:

void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{

	int count = Mine_count;
	while (count)
	{
		int x = rand() % row + 1;
 		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

8.雷布置好后,玩家就可以选择坐标了,所以创建FineMIne函数用于实现,并且操作的是show函数用于展示雷的信息。

(1).首先是玩家输入坐标(x,y)

(2).然后我们应该判断该坐标合不合法

          1).因为我们要操作的是数组中间的9*9的区域,所以若x>0且x<ROW+1,则合法,然后显示雷盘信息,否则则不合法,提示重新输入。

          2).当坐标合法后,我们就该判断该坐标处是否是雷,而判断是否是雷就应该在mine数组里面判断,此处为'1'则为雷,游戏结束;此处'0’,则玩家继续输入。

          3).当不是雷时,我们应该显示雷盘信息,即此位置有多少个雷,(操作的是show数组),这时前面提到的用‘0’ / ‘1’的好处就来了,如下图:

 所以这就是运用'1' / '0'的好处所在,所以现在我们只需要想办法求(x,y)周围的八个数之和。

因此我们创建GetMineCount函数来求和,实参:(mine,x,y),如下:

很显然我们很容发现(x,y)与其周围八个坐标的关系:

 所以我们只需返回这八个数之和,这里有个小细节,因为'1'  / '0'都是字符,所以我们应该转化为数字进行求和:

一般情况数字与字符数字之间的转化公式为 : 数字+‘0’=字符数字,所以代码实现如下:

int GetMineCount(char mine[ROWS][COLS], int x, int y)//返回坐标(x,y)周围的雷数
{
	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';
}

🍁(四)、以上操作效果如下:

🍁 结尾:

细心的小伙伴应该发现,以此坐标进行展开功能还没有实现,下面给出几点注意事项:

(1).该坐标不是雷

(2).该坐标周围八个位置没有雷

(3).该坐标没有被排查过

满足以上三点即可展开,以加快游戏进度 ,这里涉及到函数的递归等相关知识,小编不多以叙述,感兴趣的小伙伴可以自行思考。

此次知识到此就结束了,拜拜!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

成工小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值