扫雷游戏增强版代码实现和详解(超详细~)

扫雷游戏增强版代码实现和详解(超详细~)

前言:想必屏幕前的你们,也许都玩过扫雷游戏吧,这款游戏玩法比较简单,也用很容易上手。当玩家通过单击鼠标左键、右键以及同时点击左右键的双击操作进行标记安全格子和地雷,当正确找出所有地雷时则视为胜利,反之如果玩家翻开的格子有地雷,则游戏结束。

1.扫雷游戏分析和设计

1.1 扫雷游戏的功能说明

  • 使用控制台实现经典的扫雷游戏

  • 游戏可以通过菜单实现继续玩或者退出游戏

  • 可以选择游戏难度

    。简单9*9棋盘,10个雷

    。中等16*16棋盘,40个雷

    。困难30*30棋盘,99个雷

    • 可以排查雷

      。如果位置不是雷,就显示周围有几个雷

      。如果位置是雷,就炸死游戏结束

      。把除(10,40,99)个雷之外的所有雷都找出来,那么排雷成功,游戏结束

1.2 游戏的分析和设计

1.2.1数据结构的分析

扫雷过程中,布置的雷和拍出的雷都需要存储。因此我们需要一定的数据结构来存储这些信息。

举个例子,我们需要在9x9布置雷的信息和排查雷,我们首先想到的是创建一个9x9的数组来存放信息。

如果这个位置布置雷,我们就存放1,没有的话就布置存放雷。

假设我们排查(2,5)这个坐标时,我们访问周围的一圈8个黄色位置,统计周围雷的个数是1。

假设我们排查(8,6)这个坐标时,我们访问周围一圈8个黄色位置,统计周围雷的个数时,最羡慕的坐标就会越界,所以我们设计的时候。给数组扩大一圈,雷还是布置在中间的9x9的坐标上,周围一圈不去布置雷就行啦,这样就很好地解决了越界的问题。所以,我们存放数据创建成11x11是比较合适的

另外,我们虽然在棋盘布置了雷,棋盘上雷的信息(1)和非雷的信息(0),那么假设我们排查某个坐标的时候,这个坐标处不是雷,那个坐标的周围有1个雷,那么我们需要将排出的雷的数量信息记录下来存储,并打印出来。作为排雷的重要 参考信息,那个这个雷的个数我们可以存放到哪里呢?如果存档在布置雷的数组中,这样雷的信息和雷的个数信息可能或产生混淆和打印上的困难。

我们可以采用一种方案,专门给一个棋盘(对应的数组mine)存放布置好雷的信息,再给另外一个棋盘(对应另外一个数组show)存放排出的雷的信息。这样就互不干扰了,把雷布置到mine数组,再mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。

同时我们为了保持神秘,show数组开始时初始化为‘*’,为了保持两个数组的数据类型一致,我们可以使用一套函数进行处理,mine数组最开始也初始化为字符‘0’,布置雷改成‘1’。具体如下图所示:

对应的数组如下代码所示:

char mine[11][11]={
    
    0};//用来存放布置好雷的信息
char show[11][11]={
    
    0};//用来存放排查出的雷的个数信息

1.2.2 文件结构设计

一般我们写这种小型项目的时候,代码可能比较多,不会将所有的代码都放到一个文件中;我们往往根据程序的功能,将代码拆分放到多个文件中。

一般情况下,函数的声明放在头文(.h)中,函数的实现放在源文件(.c)中。所以我们之类可以创建三个文件

1.test.c //文件中写游戏的测试逻辑
2.game.c //文件中写游戏中函数的实现等
3.game.h //文件中写游戏需要的数据类型和函数声明等

2.扫雷游戏的代码实现

2.1 游戏程序菜单设置

我们可以通过菜单实现继续玩或者退出游戏,在开始游戏前让玩家选择游戏难度。

首先我们可以先封装一个menu1的函数,让玩家可以选择玩游戏还是退出游戏。

void menu1() {
    
    
	printf("********************************\n");
	printf("******    0.Exit game     *******\n");
	printf("******    1.Enter game    *******\n");
	printf("********************************\n");
}

接下来我们可以封装一个menu2的函数,让玩家选择游戏难度

void menu2() {
    
    
	printf("**************************************\n");
	printf("**********    请选择游戏难度   *********\n");
	printf("**********     1.简单    *************\n");
	printf("**********     2.中等   **************\n");
	printf("**********     3.困难    *************\n");
	printf("**************************************\n");
}

选择好之后我们就可以进入游戏了。

2.2定义宏变量

首先我们要先在game.h中定义这些符号,然后我们把函数的头文件全部写在game.h里面,然后在自己所写源文件的主函数最上面引入头文件game.h,这样我们就不需要在源文件引入**#incldue<stdio.h>#include<stdlib.h>**这些头文件了,引入头文件如下所示:

#include "game.h"

2.3游戏程序主函数

在编写这类游戏代码的时候,我们通常都是写上这个主函数。

并且我们可以通过玩家所选游戏难度等级,把参数传给game,这样可以打印出相对应的棋盘大小和雷的个数出来。

代码如下图所示:

在这里插入图片描述

2.4初始化棋盘和打印棋盘

​ 首先,我们先在源文件test.c主函数中定义函数InitBoard和Displayboard

		//棋盘初始化
		InitBoard(Mine, ROWS, COLS, '0');
		InitBoard(Show, ROWS, COLS, '*');
		//打印棋盘
		//Displayboard(Mine, ROW, COL);
		Displayboard(Show, ROW, COL);

​ 接下来我们在头文件Game.h中声明函数。

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void Displayboard(char board[ROWS][COLS], int row, int col);

​ 接下来,我们再源文件game.c再实现这个函数。

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) {
    
    

			int i = 0;
			if(row==9)
			puts("--------扫雷游戏--------");
			else {
    
    
				puts("---------------------------------------------------扫雷游戏--------------------------------------------------------");
			}
			for (i = 0; i <= col; i++) {
    
    
				if (i <= 9)
					printf("%-2d", i);
				else if (i >= 10)
					printf("%-3d", i);
			}		printf("\n");
			for (i = 1; i <= row; i++) {
    
    
					printf("%d ", i);
			
				int j = 0;
				for (j = 1; j <= col; j++) {
    
    
					if (j <= 9)printf("%-2c", board[i][j]);
					else
						printf("%-3c", board[i][j]);
				}
				printf("\n");
			}
			if(row==9)
			puts("--------扫雷游戏--------");
			else {
    
    
				puts("---------------------------------------------------扫雷游戏--------------------------------------------------------");
			}
}

打印棋盘其实很简单,用for循环就能实现我们的需求了,需要注意的是,我们打印棋盘是从i=1开始的,这样能够避免打印中间的空格区域,只打印中心的(9x9,16x16,30x30)空格,除此之外,我们还添加了行号和列号,这样玩家就知道他们输入的坐标了。

2.5布置雷

同样地,我们需要在源文件test.c主函数中定义这个函数SetMine

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

接着咱们在头文件game.h中声明函数SetMine

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

然后在头文件game.c中实现这个函数。

void SetMine(char Mine[ROWS][COLS], int row, int col) {
    
    
int count = 0;
if (row == 9) {
    
    
	count = EAST_COUNT1;
}
else if (row == 16) {
    
    
	count = EAST_COUNT2;
}
else {
    
    
	count = EAST_COUNT3;
}
while (count) {
    
    
	int x = rand() % row + 1;
	int y = rand() % col + 1;
	if (Mine[x][y] == '0') {
    
    
		Mine[x][y] = '1';
		count--;
	}

同时呢,我们在头文件game.h中引入了EAST_COUNT的变量,根据玩家所选游戏难度来设置雷的个数,那为什么要在这里定义式宏呢,因为到时要改变布置雷数,后面的代码也要改,比较麻烦,因此我们在这里直接定义这三个变量的值。

#define EAST_COUNT1 10
#define EAST_COUNT2 40
#define EAST_COUNT3 99

这些值就代表我们的雷有多少个了。

2.6排查雷

同样地,我们现在源文件test.c中定义函数FineMine

FineMine(Mine, Show, ROW, COL);

然后我们在头文件game.h中声明函数FineMine

void FineMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);

接下来我们就在源文件game.c实现函数FineMine

void FineMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col) {
    
    

	int x = 0;
	int y = 0;
	int win = 0;
	int count = 0;
	if (row == 9) {
    
    
		count = EAST_COUNT1;
	}
	else if (row == 16) {
    
    
		count = EAST_COUNT2;
	}
	else {
    
    
		count = EAST_COUNT3;
	}
	while (win<row*col-count) {
    
    
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
    
    
			if (Show[x][y] != '*') {
    
    
				printf("该坐标已排查过,请重新输入\n");
			}
			else if (Mine[x][y] == '1') {
    
    
				printf("boom,你被炸死了!!!\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");
		}

	}

	if (win == row * col -count) {
    
    
		printf("恭喜您呀,排雷成功!芜湖~~\n");
		Displayboard(Mine, row, col);
	}

}

这里我们需要注意的是,我们要根据玩家所选择游戏难度,确定棋盘的大小,行数和列数,所以呢我们要添加一行代码来确定代码的合理性,咱们举个例子,比如玩家所选难度为“中等”,对应的坐标是16x16的,但是输入了一个(88,88)的坐标,显然不在坐标范围之类,因此我们要提醒他要重新输入坐标。

另外,你可能对**Show[x][y] = count + ‘0’;**这个函数有所疑惑,为何要在count后面加个’0’呢?

讲到这里,我们可以看一下ASCLL码值。

由上图,我们可以知道’0’和’1’的ASCLL码值分别为48和49,我们之前定义Mine数组传的是字符0和字符1,

不是数字0和1,仔细观察,可以发现里面的数字跟对应的数字字符都相差了48,而48恰恰是’0’,所以我们要在count后面加上’0’,这样才能知道这个坐标周围有几个雷。

2.6.1统计雷的个数

那么如何将这些雷依次输出出来呢?

我们可以把行和列分别设为x和y。然后把这些坐标全部输入到二维数组,这样我们就可以求出雷的个数是多少了。

统计雷的个数如下所示:

 static int  getminecount(char Mine[ROWS][COLS], int x, int y) {
    
    

	 int count = (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');
	 return count;
}

这里因为我们输入统计出来的也是’1’字符,对应的ASCLL码值为49,那么统计好这八个坐标后,把它们分别相加起来,再减去8 *‘0’, 我们就能得到雷个数的数字。然后用getminecount这个函数来接收这个返回雷的个数,再通过

count+'0'=show[x][y];

从而得出该坐标附近有几个雷,并打印出相对应的数字字符。

除此之外,我们为什么要在上面代码中增加这个条件?

if (Show[x][y] != '*') {
    
    
				printf("该坐标已排查过,请重新输入\n");
			}

因为当玩家第一次输入排查的坐标后,显示show数组附近有几个雷,按理不应让他再次输入那个点的坐标,这样不符合游戏的逻辑。因此我们加上这个判断条件,可以避免玩家输入相同的坐标。具体运行如下所示:

img

3.总代码

下面是博主这次设计扫雷游戏的总代码,仅供参考~

test.c:文件中的游戏测试逻辑

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//



void menu1() {
    
    

	printf("********************************\n");
	printf("******    0.Exit game     *******\n");
	printf("******    1.Enter game    *******\n");
	printf("********************************\n");
}
void menu2() {
    
    
	printf("**************************************\n");
	printf("********  请选择游戏难度    ******\n");
	printf("**********     1.简单    *************\n");
	printf("**********     2.中等   **************\n");
	printf("**********     3.困难    *************\n");
	printf("**************************************\n");
}


void game(int choice) {
    
    
	if (choice == 1) {
    
    
		//数组的初始化
		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);
		//排查雷
		FineMine(Mine, Show, ROW, COL);
	}
	else if (choice == 2) {
    
    
		char Mine[ROWS2][COLS2];
		char Show[ROWS2][COLS2];
		InitBoard(Mine, ROWS2, COLS2, '0');
		InitBoard(Show, ROWS2, COLS2, '*');
		//Displayboard(Mine, ROW2, COL2);
		Displayboard(Show, ROW2, COL2);
		SetMine(Mine, ROW2, COL2);
		FineMine(Mine, Show, ROW2, COL2);
	}
	else {
    
    
		char Mine[ROWS3][COLS3];
		char Show[ROWS3][COLS3];
		InitBoard(Mine, ROWS3, COLS3, '0');
		InitBoard(Show, ROWS3, COLS3, '*');
		//Displayboard(Mine, ROW3, COL3);
		Displayboard(Show, ROW3, COL3);
		SetMine(Mine, ROW3, COL3);
		FineMine(Mine, Show, ROW3, COL3);
	}
}

int main() {
    
    

	int input = 0;
	int choice = 0;
	srand((unsigned int)time(NULL));
	do {
    
    
		menu1();
		printf("请选择>:");
		scanf("%d", &input);
		switch (input) {
    
    
		case 0:
			break;
		case 1:
			menu2();
			scanf("%d", &choice);
			while (choice < 1 || choice>3) {
    
    
				printf("请重新选择:");
				scanf("%d", &choice);
			}
			game(choice);
			break;
		default: {
    
    
			printf("输入错误,请重新输入数字。\n");
		}
		}
	} while (input);
	return  0; 
}

Game.h:文件中游戏的数据类型以及函数的声明

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//.
#define ROW 9
#define COL  9
#define ROW2 16
#define COL2 16
#define ROW3 30
#define COL3 30

#define ROWS ROW+2
#define COLS  COL+2
#define ROWS2 ROW2+2
#define COLS2 COL2+2
#define ROWS3 ROW3+2
#define COLS3 COL3+2

#define EAST_COUNT1 10
#define EAST_COUNT2 40
#define EAST_COUNT3 99


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 FineMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);

Game.c:游戏程序中函数的实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//.game.c文件中是游戏中函数的实现
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) {
    
    

			int i = 0;
			if(row==9)
			puts("--------扫雷游戏--------");
			else {
    
    
				puts("---------------------------------------------------扫雷游戏--------------------------------------------------------");
			}
			for (i = 0; i <= col; i++) {
    
    
				if (i <= 9)
					printf("%-2d", i);
				else if (i >= 10)
					printf("%-3d", i);
			}		printf("\n");
			for (i = 1; i <= row; i++) {
    
    
					printf("%d ", i);
			
				int j = 0;
				for (j = 1; j <= col; j++) {
    
    
					if (j <= 9)printf("%-2c", board[i][j]);
					else
						printf("%-3c", board[i][j]);
				}
				printf("\n");
			}
			if(row==9)
			puts("--------扫雷游戏--------");
			else {
    
    
				puts("---------------------------------------------------扫雷游戏--------------------------------------------------------");
			}
}

void SetMine(char Mine[ROWS][COLS], int row, int col) {
    
    

	int count = 0;
	if (row == 9) {
    
    
		count = EAST_COUNT1;
	}
	else if (row == 16) {
    
    
		count = EAST_COUNT2;
	}
	else {
    
    
		count = EAST_COUNT3;
	}
	while (count) {
    
    
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (Mine[x][y] == '0') {
    
    
			Mine[x][y] = '1';
			count--;
		}
	}
}


 static int  getminecount(char Mine[ROWS][COLS], int x, int y) {
    
    

	 int count = (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');
	 return count;
}

void FineMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col) {
    
    

	int x = 0;
	int y = 0;
	int win = 0;
	int count = 0;
	if (row == 9) {
    
    
		count = EAST_COUNT1;
	}
	else if (row == 16) {
    
    
		count = EAST_COUNT2;
	}
	else {
    
    
		count = EAST_COUNT3;
	}
	while (win<row*col-count) {
    
    
		printf("请输入要排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
    
    
			if (Show[x][y] != '*') {
    
    
				printf("该坐标已排查过,请重新输入\n");
			}
			else	if (Mine[x][y] == '1') {
    
    
				printf("boom,你被炸死了!!!\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");
		}

	}

	if (win == row * col -count) {
    
    
		printf("恭喜您呀,排雷成功!芜湖~~\n");
		Displayboard(Mine, row, col);
	}

}

3.1 效果演示

好啦,今天的扫雷游戏分享就到此结束啦,如果觉得博主讲得不错的,欢迎大家一键三连支持一下,谢谢!!

猜你喜欢

转载自blog.csdn.net/m0_63564767/article/details/132253237