扫雷小游戏

e6b847bdb72b4f719e5f968a5d9de986.gif

目录

 

游戏介绍。

越界访问问题引出。

总体思路。

游戏的主体。

初始化函数的编写。

实现打印功能。

 布置雷。

查找雷的环节

⭐计算某位置周围雷数量

⭐标记功能

🔥释放一大片。

游戏完整代码


 

游戏介绍。

ac810e9a7fe14703a85ec605bff535da.png

         💡首先给我们一个雷盘,刚开始所有点都是不可见的,然后玩家猜测,如果周围八格没有雷就显示空白,然后周围坐标也没有雷的话展开至有雷的坐标,通过周围八格有几个雷就显示数字几,通过数字来判断周围雷的位置,还可以设置标记,如果排除所有的雷就算游戏成功。


越界访问问题引出。

📜说明:为了防止在后边出现问题,我们在前边就提出这个问题,如果玩家输入的是期盼边缘的位置,那么遍历周围八个位置那就会有越界的情况,例如

8c7b489847064b3aad65700d52902c81.png

        💭如何解决呢?可以扩展一下数组,例如要用到三乘三的数组,我们可以创建一个四乘四的数组,只用到里面的三乘三的部分。在初始化时要传入四乘四的部分,这样就不用很麻烦特殊位置特殊处理,还不用担心越界的问题。

我们在game.h里定义一下,需要用到哪个就传哪个

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

总体思路。

        ⛳️我们仍然用分文件的方式编写,棋盘的话,利用数组来模拟实现,假设字符数组刚开始全部都为*,玩家选中该位置后,排查该位置周围的8个位置,如果有雷则改为相应雷的数量,然而我们一个字符数组既要是*,又要改变为雷的数量,那么雷放在哪里呢,再设立一个字符数组,命名为mine数组,里面装着雷,如果该位置为雷,就设立为字符1,在玩家输入坐标后,我们判断该位置是否为雷,不是雷就遍历周围的八个位置,得到雷的数量,在第一个字符数组中打印出来,第一个字符命名为show数组。


游戏的主体。

int main()
{
	srand((unsigned int)time(NULL));
	int key = 0;
	do
	{
		
		menu();
		printf("请选择;>");
		scanf("%d", &key);
		switch (key)
		{
		case 1:
			printf("游戏开始\n");
			game();
			break;
		case 0:break;
		default:
			printf("选择错误,重新选择\n");
		}
	} while(key);
	return 0;
}	

        📘do while循环先打印菜单,菜单我们就省略了,在这里我们用输入的key来判断是否进入游戏循环(srand函数是因为后边要随机生成雷的位置。)

接下来要进行game函数的实现

       ⭐ 首先来创建两个数组,一个命名为show,一个命名为mine

初始化函数的编写。

        💭在game.c中实现,在game.h中声明。(下边实现某些功能的函数都是如此)

代码如下

void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

🐞注意:我们要初始化的数组有两个,因为要初始化的内容不同,如果在函数里写死的话,要用两个初始化函数才能初始化完成,我们可以多传一个参数,传过来不同的数组初始化为不同的值。还要注意的是传参行和列要传ROWS和COLS,初始化大的数组。

实现打印功能。

void displayboard(char board[ROWS][COLS], int row, int col)//打印出show数组元素
{
	printf("--------扫雷游戏-------\n");
	int i = 0;
	
	for (i = 1; i < row+1; i++)
	{
		
		int j = 0;
		
		for (j = 1; j < col+1; j++)
		{
			
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}

        🌱注意:传入相应的数组,还有行和列,这个时候我们就不需要看外围的了,直接传ROW和COL即可。

        然而想让玩家很直观的看到每个位置的行和列,而不是一个一个位置数,可以逐行逐列打印行号和列号。优化代码如下。

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

初步工作完成

在game函数中创建数组:

    char mine[ROWS][COLS] = {0};
    char show[ROWS][COLS] = {0};

在game()中初始化函数如下

    initboard(mine, ROWS, COLS, '0');
    initboard(show, ROWS, COLS, '*');

在game()函数中,我们将两个字符数组都打印出来

    displayboard(mine, ROW, COL);
    displayboard(show, ROW, COL);

📑运行后如图

ddaecfce65114fe1a3cb30665ea01f65.png

 布置雷。

在mine数组中,随机更改部分0变为1,需要使用到srand函数产生随机值,假设有十个雷。

//随机生成雷
void PutMine(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] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

 👉这里使用了rand,前边我们已经在主函数中添加了srand((unsigned int)time(NULL));这里写成了一个死循环,只有当生成10个雷之后,才能跳出循环,注意的是,生成雷的坐标在ROW和COL空间内。雷的个数count被定义为EASY_COUNT,这里是我们在game.h中定义的雷的个数,方便我们后续的改写。


查找雷的环节

         👉当放置雷之后,接下来就到了,玩家输入坐标,然后判断该位置是否为雷,如果为雷,就提示玩家被炸死了,且打印雷的位置给玩家看,如果该位置不是雷,那就返回周围八个坐标雷的数量,在show数组打印出来。

void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int x=0, y = 0;
	int flag = 0;
	
	while(flag<row*col- EASY_COUNT)
	{
		printf("请输入猜测坐标\n");
		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
			{
				if (show[x][y] != '*')
				{
					printf("该坐标已被查找,请重新输入");
				}
				//不是雷且不重复,就统计雷的个数.
				else
				{
                    system("cls");//清空屏幕
					int count = GetMineCounter(mine, x, y);
					show[x][y] = count + '0';
					displayboard(show, ROW, COL);
					flag++;
				}
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	if (flag == row*col-EASY_COUNT)
	{
		printf("恭喜您,排雷成功\n");
		displayboard(mine, ROW,COL);
	}
}

判断循环是否结束,就是判断是否查找出所有的雷

①如果玩家输入的位置是雷,就提示游戏结束,同时打印出雷的数组,让玩家看具体那个位置是雷

②如果输入已经输入过的坐标,那就提示玩家该坐标已经被查找过。

如果输入的坐标不正确,就提示玩家,不找出所有雷或者不被炸死,循环就不会结束,跳出循环后。

③如果是被炸死,那么就没有找出所有的雷。如果没被炸死,flag一直加加,知道等于总数减去雷的数量,这是就输出排雷成功。

💡system("cls")清空屏幕,让游戏更加美观。

⭐计算某位置周围雷数量

想要知道该坐标周围雷的数量,利用GetMineCounter函数来解决,在game.h中声明,看代码,原理很简单。

GetMineCounter(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}

返回的是雷的数量,为int型的,可以根据ASCLL,在findmine函数里返回值加上字符‘0’,就将其转化为字符类型。


👑打开网页版排雷小游戏,上面有标记雷的功能,而且选中一个不是雷的位置会展开很大一片

079cf69f68f34150bac2106bb28831b8.png

 接下来实现这两个功能。

⭐标记功能

在查找一个位置后,直接弹出选项,是否需要标记某一位置,我们在findmine函数中没有踩到雷时加入以下几行代码。

void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int x=0, y = 0;
	int flag = 0;
	
	while(flag<row*col- EASY_COUNT)
	{
		printf("请输入猜测坐标\n");
		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
			{
				if (show[x][y] != '*')
				{
					printf("该坐标已被查找,请重新输入");
				}
				//不是雷且不重复,就统计雷的个数.
				else
				{
					int count = GetMineCounter(mine, x, y);
					show[x][y] = count + '0';
					displayboard(show, ROW, COL);
					flag++;
					
					printf("是否需要标记某位置:(1/0)");
					int map = 0;
					scanf("%d", &map);
					if (map == 1)
					{
						int a = 0, b = 0;
						scanf("%d %d", &a, &b);
						show[a][b] = '#';
						
					}
					system("cls");
					displayboard(show, ROW, COL);
				}
				
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}	
	}
	if (flag == row*col-EASY_COUNT)
	{
		printf("恭喜您,排雷成功\n");
		displayboard(mine, ROW,COL);
	}
}

🔥释放一大片。

void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* p)
{
	int num = GetMineCounter(mine, x, y);
	if (num == 0)
	{
		(*p)++;//++操作符的优先级比取地址符号高,所以要加括号。
		show[x][y] = '@';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*')//防止已经查找过的坐标再次查找,变成死递归
				{
					ExplosionSpread(mine, show, row, col, i, j, p);
				}
			}
		}
	}
	else
	{
		(*p)++;
		show[x][y] = num + '0';
	}
}

        我们将周围没有雷的坐标改为@这样清晰明了一点(棋盘太简单了,设置为空格不好看),for循环的控制部分和遍历查找雷的数量相同,找该位置周围八个坐标,如果有满足条件的就进入函数,然后不断递归,就达到了想要的效果。如果这个位置周围有雷,那就在show数组里改变这个坐标,显示出这个坐标周围雷的个数。

小技巧:将雷的数量EASY_COUNT改为1,方便调试,在game函数里,提前把show数组提前打印出来,可以在输入猜测坐标前提前知道雷的位置。

运行后效果

9fcb1672c17c48ec955565b78d384871.png


游戏完整代码

game.h

#pragma once
//#pragma pack(1)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define EASY_COUNT 1
#define ROW 9
#define COL 9
#define COLS COL+2
#define ROWS ROW+2
void initboard(char board[ROWS][COLS], int rows,int cols,char set );
void displayboard(char board[ROWS][COLS], int row, int col);
//放置雷
void PutMine(char board[ROWS][COLS], int row, int col);
//查找雷
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);

int GetMineCounter(char mine[ROWS][COLS], int row, int col);
//大面积展开
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* p);

game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
	
}
void displayboard(char board[ROWS][COLS], int row, int col)//打印出show数组元素
{
	printf("--------扫雷游戏-------\n");
	int i = 0;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < row+1; i++)
	{
		
		int j = 0;
		printf("%d ", i );
		for (j = 1; j < col+1; j++)
		{
			
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}

//随机生成雷
void PutMine(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] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

//查找雷
void findMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int x=0, y = 0;
	int flag = 0;
	int* p = &flag;
	while(flag<row*col- EASY_COUNT)
	{
		printf("请输入猜测坐标\n");
		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
			{
				if (show[x][y] != '*')
				{
					printf("该坐标已被查找,请重新输入");
				}
				//不是雷且不重复,就统计雷的个数.
				else
				{
					ExplosionSpread(mine, show, row, col, x, y, p);
					system("cls");

					displayboard(show, ROW, COL);
					flag++;
					
					printf("是否需要标记某位置:(1/0)");
					int map = 0;
					while ((map = getchar()) != '\n');
					
					scanf("%d", &map);
					if (map == 1)
					{
						int a = 0, b = 0;
						scanf("%d %d", &a, &b);
						show[a][b] = '#';
						
					}
					system("cls");
					displayboard(show, ROW, COL);
				}
				
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}	
	}
	if (flag == row*col-EASY_COUNT)
	{
		printf("恭喜您,排雷成功\n");
		displayboard(mine, ROW,COL);
	}
}

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

GetMineCounter(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (mine[i][j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}

void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* p)
{
	int num = GetMineCounter(mine, x, y);
	if (num == 0)
	{
		(*p)++;//++操作符的优先级比取地址符号高,所以要加括号。
		show[x][y] = '@';
		int i = 0;
		int j = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*')//防止已经查找过的坐标再次查找,变成死递归
				{
					ExplosionSpread(mine, show, row, col, i, j, p);
				}
			}
		}
	}
	else
	{
		(*p)++;
		show[x][y] = num + '0';
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void menu()
{
	printf("*****************************\n");
	printf("*******   1.play    *********\n");
	printf("*******   2.exit    *********\n");
	printf("*****************************\n");
}
//在game函数里创建游戏的基本运行条件
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);
	//displayboard(mine, ROW, COL);
	//接下来随机生成雷
	
	
	PutMine(mine, ROW, COL);
	
	//displayboard(mine, ROWS, COLS);
	// 可以观察布置雷怎么样。
	displayboard(mine, ROW, COL);
	//玩家查找雷
	/*int a, b;
	scanf("%d %d", &a, &b);*/
	findMine(mine,show, ROW, COL);
}

int main()
{
	srand((unsigned int)time(NULL));
	int key = 0;
	do
	{
		
		menu();
		printf("请选择;>");
		scanf("%d", &key);
		switch (key)
		{
		case 1:
			printf("游戏开始\n");
			game();
			break;
		case 0:break;
		default:
			printf("选择错误,重新选择\n");
		}
	} while(key);
	return 0;
}	


 

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

we will rise.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值