扫雷游戏(basic)

一、题目概述

完成简单的扫雷游戏(不包括展开一大片、以及插旗方法)

二、基本思路

(1)扫雷的整体框架

扫雷游戏将会用多文件实现
test.c:测试游戏的逻辑
game.c:游戏的实现
game.h:游戏的函数的声明、头文件包含、全局变量的创建等。

在开始玩游戏之前,我们与玩家进行交互,玩家选择玩与不玩。
代码如下:
test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void menu()
{
	printf("*******************************\n");
	printf("**********  1. play  **********\n");
	printf("**********  0. exit  **********\n");
	printf("*******************************\n");
}
int main()
{
	int n = 0;
	do
	{
		menu();

		scanf("%d", &n);

		switch (n)
		{
		case 1:
			printf("扫雷游戏\n");	//(1)用扫雷游戏代替一下真正的游戏,证明扫雷游戏的交互是可以运行的
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (n);
}

(2)主要思路

首先,先看一个基础的扫雷游戏

从图中可以看出,这个扫雷是一个9*9的棋盘,里面布置了10个雷。
在扫雷的之前,我们要生成10个雷,在扫雷的过程中,玩家需要排查雷。所以我们就要做出一个9*9的数组,用来布置雷和排查雷。
如何排查雷?如下图

比如说玩家输入坐标(3,3),就要排查(3,3)周围的8个坐标。如果有雷的话,统计一下周围的个数加起来存放在(3,3)的坐标位置
但是,如果玩家要排查左上角的雷,如下图

如果玩家要排查左上角的雷,那么在排查周围一圈的雷的时候,容易发生数组越界,那么应该如何解决?
如果发生数组越界的话,我们可以把数组扩大一圈,也就是11*11的数组,这样就不容易发生数组越界,但数组的下标会有所改变,后面会说明。如下图

解决了数组越界的问题,但是还有一个问题,也就是布置雷我们用1和0表示,排查雷,排查完一圈如果周围雷有1个,那么还是放入这个二维数组,那么到底这个1,是雷还是我排查出的雷呢?就会产生歧义
所以我们可以用两个同样的数组来解决这个问题,我们用mine数组来存放雷,用show数组来表示排查雷的个数。

这个时候我们就有了两个数组,而show数组是给玩家看的以及游玩的,所以我们需要给他营造点神秘感,把show数组全部初始化为'*',而'*'就可以表示没有排查雷的时候,排查完雷之后统计个数放入show数组中显示给玩家看,但是有一个问题:'*'是字符,而不是整数,如果我们排查出了个数存放到数组里面,那么会出现又数字又字符,处理起来非常麻烦,为了更方便我们处理,我们把个数变成字符个数,同理,mine数组也用字符存放,字符’1’存放雷,字符‘0’存放非雷。这样子,我们就能开始打印棋盘了。

①初始化棋盘&打印棋盘

game.h

#pragma once
#include <stdio.h>

#define ROW 9
#define COL 9

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

void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char arr[ROW][COL], int row, int col);

设置全局变量,方便后续改动。想玩x*x都行,但是如果设置局部变量,那么就要改来改去,非常麻烦。

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

void DisplayBoard(char arr[ROW][COL], int row, int col)
{
	int i = 0;

	printf("------扫雷游戏-----\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏-----\n");
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
	printf("*******************************\n");
	printf("**********  1. play  **********\n");
	printf("**********  0. exit  **********\n");
	printf("*******************************\n");
}
void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];

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

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

	
}
int main()
{
	int n = 0;
	do
	{
		menu();

		scanf("%d", &n);

		switch (n)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (n);
}

运行结果如下:

此代码能正确打印两个棋盘,证明没有问题,但是前面布置雷的棋盘我们是不给玩家看的,所以后续会屏蔽掉打印mine数组的代码。

②布置雷

既然要布置雷,就要先知道我们要布置多少个雷,我们能看到上面的图左上角表示的是10个雷,那么我们将布置10个雷,
然后就要保证我们的雷是随机的,是我们不知道的,如果知道雷在哪那就不叫扫雷叫扫盲了。
雷要随机,那么我们就要用rand()函数,要用rand()函数就要用srand()函数
我们把随机出来的数模上一个row,那么就会取模后就能得到08的数字,再+1,就能得到09的随机数了

代码如下:
game.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define EASY_COUNT 10

#define ROW 9
#define COL 9

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

void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char arr[ROW][COL], int row, int col);

void SetMine(char arr[ROW][COL], int row, int col);//我在这里

调用了rand()函数和srand()函数,就要相应的头文件,然后雷的个数使用全局变量,方便以后更改

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

void DisplayBoard(char arr[ROW][COL], int row, int col)
{
	int i = 0;

	printf("------扫雷游戏-----\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏-----\n");
}

void SetMine(char arr[ROW][COL], int row, int col)
{
	int count = EASY_COUNT;

	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

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

因为雷是随机生成的,所以要用一个if来判断雷是否是重复的,如果是重复的就要重新生成随机数。只有不重复才能设置雷。所以即使只设置了10个雷,但是可能因为雷是重复的而整个while循环不止执行了10次。

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
	printf("*******************************\n");
	printf("**********  1. play  **********\n");
	printf("**********  0. exit  **********\n");
	printf("*******************************\n");
}
void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];

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

	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);

	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);

	
}
int main()
{
	int n = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();

		scanf("%d", &n);

		switch (n)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (n);
}

我们在这里测试的时候,设置了雷之后,可以打印一下看看是否布置了10个雷,运行结果如下:

很明显,布置了10个雷,那么我们布置雷就成功了。
当然,这10个雷的布置位置是不能被玩家知道的,所以后面也会注释掉。

③排查雷

这个时候就是玩家排查雷的时候了,玩家通过输入坐标来去排查相应坐标的有雷。但是以防玩家手抖输入了点奇怪的数字,所以我们在玩家输入数字后要判断坐标是否合法。如果是合法化的才进行排查。

因为雷设置在mine数组,所以玩家要在mine数组里面排查,而mine数组里面存放着’1’和’0’,如果玩家排查到’1’,也就是雷,就被炸死了,游戏结束。如果玩家排查到’0’,那么我们要返回周围一圈有多少个雷,显示在棋盘上,也就是show数组。那么玩家要排查多少次,才是游戏成功呢?答案很显然,因为我们定义的是9*9的数组,一共有10个雷,那么我们只要用9 * 9 = 81再减去10也就是71次,玩家需要排查71次,游戏就胜利了.
我们来看看排查雷的函数:

int ReturnMine(char mine[ROWS][COLS], int x, int y) //(1)
{
	return mine[x - 1][y - 1]
		+ mine[x - 1][y - 1]
		+ 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 win = 0;
	int x = 0;
	int y = 0;
	int count = 0;

	while (win < row * col - EASY_COUNT)
	{
		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
			{
				count = ReturnMine(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}

		if (win == row * col - EASY_COUNT)  //(2)
		{
			printf("恭喜你,排雷成功\n");
			DisplayBoard(mine, ROW, COL);
		}
	}
}
  • (1)这里需要注意的是排查雷时要返回的雷的个数,是(x, y)坐标周围的8个坐标,如下图

​ 而为什么要这样计算呢?是因为字符本质上是ASCII码值,而’0’ = 48,‘0 + x’ = 48 + x,所以我们只要把所有坐标的字符加起来,得到 一个数,再减去8 * ‘0’(因为x周围的8个坐标)就能得到周围有多少个雷

  • (2)这里的判断是因为如果你一开始被炸死了,程序跳出循环,如果你没被炸死,结束了循环,跳出和结束了循环,都不知道你是否是胜利了,所以我们只要用win去判断是否排查了71次,如果排查了71次那么win肯定是等于71的,如果没排查成功被炸死,win肯定是小于71的,那么就不符合if的条件。

三、所有的代码实现

game.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define EASY_COUNT 10

#define ROW 9
#define COL 9

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

void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char arr[ROWS][COLS], int rows, int cols);

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

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

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;

	printf("------扫雷游戏-----\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏-----\n");
}

void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;

	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

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

int ReturnMine(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1]
		+ mine[x - 1][y - 1]
		+ 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 win = 0;
	int x = 0;
	int y = 0;
	int count = 0;

	while (win < row * col - EASY_COUNT)
	{
		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
			{
				count = ReturnMine(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}

		if (win == row * col - EASY_COUNT)
		{
			printf("恭喜你,排雷成功\n");
			DisplayBoard(mine, ROW, COL);
		}
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
	printf("*******************************\n");
	printf("**********  1. play  **********\n");
	printf("**********  0. exit  **********\n");
	printf("*******************************\n");
}
void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];

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

	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);

	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

	FindMine(mine, show, ROW, COL);

	
}
int main()
{
	int n = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();

		scanf("%d", &n);

		switch (n)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (n);

	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鹤言o 0 O

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

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

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

打赏作者

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

抵扣说明:

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

余额充值