C语言小游戏:扫雷

C/C++
174
0
0
2024-05-03

1.游戏规则

扫雷的详细规则是随便点开一个方格,根据展开方格的数字去推断其相邻九宫格内未展开方格下面是否是地雷,最终任务就是点开所有没有地雷的方格,以找出所有的地雷。 要取得高分,就必须在尽量短的时间内清除所有的地雷。 得分计算方法:扫雷得分=分数基数X难度系数/扫雷时间,其中分数基数为10000,难度系数=每局地雷数/每局总格子数,扫雷时间按毫秒计算。

所需要的头文件

代码语言:javascript

复制

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define ROWS ROW+2
#define COL 9
#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 mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

2.思路分析

1.初始化棋盘

玩过扫雷的都知道扫雷的游戏范围是一个n*m的棋盘,以简单的为例(9*9)我们首先将棋盘进行初始化,通常使用一个二维数组arr【9】【9】来进行存储棋盘的数据,但在这里要特别注意,我们排雷的时候,如果点击的不是雷,它就会显示周围雷的个数,万一我们如果排在边缘的位置时,它要显示周围雷的个数的话,就必有3个我们未定义的区域包含在内,这样会造成越界访问。所以在定义数组大小时,我们多加2行2列,这样运行时会更加安全。

 初始化,我们将数组内的值全部置为0,同时为了方便打印(要遮住棋盘)我们统一用字符数组进行存储。

代码语言:javascript

复制

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

这里我们需要初始化两个棋盘,一个用来存储雷的位置(mine数组),一个用来进行展示(show数组),展示的那个里面用*号进行初始化,起到一个遮蔽的作用。初始化完后我们就可以打印了

打印函数

代码语言:javascript

复制

void DisplayBoard(char board[ROWS][COLS], int row, int col) {
	int i = 0;
	int j = 0;
	printf("----------扫雷---------\n");
	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");
	}
	printf("----------扫雷---------\n");
}

效果大概是这样

2.设置雷

设置雷的位置是不能展示出来的,所以我们在mine数组里设置。设置雷的思路,基本上就是给一个随机的坐标往里面放值,这里用字符1表示,问题在于如何给定一个随机数。这里我们介绍一个函数

srand函数是一个随机数生成器,但它生成随机数需要一个种子,没有设置随机数种子,rand()函数在调用时,自动设计随机数种子为1。随机种子相同,每次产生的随机数也会相同。解决办法就是使用srand()函数产生随机种子,去哪找这个“种子”呢?我们再来介绍一个函数

time(NULL) time(NULL)函数的返回值是从1970年1月1日0时整到此时此刻所持续的秒数。(至于为什么是1970.01.01的0时整,网上有很多解释,有兴趣的小伙伴们可去查阅。),时间是在不断变化的,每个时刻时间都不一样,所以我们通常用来当作随机数的生成种子,这样就完成了随机数的创建。

这里以简单模式为例,简单模式有10个雷,我们每设置一个,雷数就-1.

代码语言:javascript

复制

void SetMine(char mine[ROWS][COLS], int row, int col) {
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;//rand生成随机数,对行列求余,保证生成的数在棋盘范围内
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
		   
	}
}

这里x和y的值加1的原因是,使得雷的范围在[1,row]和[1,col]内,因为二维数组的下标是从0开始的,但设计游戏得方便大多数人进行游玩,这样让玩家排雷会更加方便。

3.排雷

排雷的思路很简单,让玩家输入一个坐标,如果对应的坐标是雷,那么直接“炸死”,游戏结束;

如果没有雷,那就显示周围雷的个数,直到排完,玩家胜利。

玩家输入的坐标一定要合法,在1和行列范围内,这里也可以加一条判断,如果输的非法坐标,进行提示一下;

输入的坐标没有雷的情况,就显示周围雷的个数,如何显示呢,这里我们设计一个函数,返回输入坐标周围八个坐标雷的个数,显然返回的值int型,但前面我们提到mine数组是字符类型,这里我们用坐标里的值于字符0做差(字符在计算机里是ascii编码进行存储),得到的差为该坐标周围雷的个数;

这里得用循环进行排雷操作,关键在于循环条件,我们从赢的条件上进行分析,条件是找出所有的雷,游戏结束,这里用一个变量win记录非雷的坐标的个数,如果我们每次输入的坐标不是雷,win就+1,知道它等于非雷的坐标的个数(总数-雷数),循环跳出,宣布玩家胜利,同时,为了避免玩家重复排雷,我们还得加一个条件进行提示。

代码语言:javascript

复制

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_s("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (show[x][y] != '*')
			{
				printf("该坐标被排查了,请勿重复排查\n");//每排成功一次,show数组会显示雷个数,这里如果不等于*的话,就相当于输入的坐标重复了。
			}
			else if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int c=GetMineCount(mine, x, y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
				win++;

			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}

}

 测试函数

代码语言:javascript

复制

#include"game.h"
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 };
	Initboard(mine, ROWS, COLS,'0');
	Initboard(show, ROWS, COLS,'*');//棋盘初始化
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	// DisplayBoard(mine, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL);
}

int main() {
	srand((unsigned int)time(NULL));
	int input = 0;

	do {
		menu();
		printf("请选择:\n");
		scanf_s("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

3.不足之处

1.我们玩的扫雷都是一扫一大片,但这里只能一个一个排,效率不高;

2.棋盘固定,想要更换难度就得修改雷数和棋盘数,不适用于所有的难度。