C语言实现简单小游戏---扫雷

今天要分享给大家的是扫雷的代码实现,和前几天写的那个三子棋差不多,大体思想就是以二维数组为载体,编写设计函数来实现它的各种功能。我们先来看看最后的成果展示吧(完整代码在文章末尾处~)

首先从这个成果图中可以看出,游戏结束后可以选择再来一次,也可以退出,所以在代码中我们肯定是将整个游戏过程放在一个循环里面,我是采用的do-while循环,大致构思与前面写的三子棋相同。看代码:

void test()
{
	int input = 0;
	do
	{
		srand((unsigned)time(NULL));//利用时间戳来设置随机种子
		menu();
		printf("请选择->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入不合法,请重新输入!\n");
		}
	} while (input);
}

 在这里补充一下为什么要将input作为while循环的条件哈,因为输入1时,input的值为1,为真,与之对应的是play,开始游戏;输入0时,为假,对应的是exit,刚好也是跳出循环,输入其他值时会被要求重新输入,直到输入0或1。这样安排一举两得哈哈哈。

当输入1后,程序会进入到game()函数,也就是我们游戏的主要设计了。

我主要创建了两个相同的二维数组mine[ROWS][COLS]数组用来存放雷的信息;show[ROWS][COLS]数组用来展示雷盘,后续的排雷过程也是在此二维数组上来展示

如果整个游戏的数据设计都基于一个二维数组的话相对来说容易混淆,比如只有一个数组的话,除了要对区分雷区与非雷区加以处理,还有后续排雷的信息要处理。倒也不是说不能用一个二维数组,用2个二维数组是为了使思路更清晰。

创建好了之后就调用以下函数InitBoard()对它们进行初始化

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

然后我们可以写一个打印数组的函数DisplayBoard(),用来模拟玩游戏时出现在界面上的雷盘。

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <=row; i++)
	{
		printf(" %d ", i);
	}
	printf("\n");
	for (i = 1; i <=col; i++)
	{
		printf(" %d ", i);
		for (j = 1; j <=col; j++)
		{
			printf(" %c ", board[i][j]);
		}
		printf("\n");
	}

}

之后我们要开始对mine数组进行埋雷了,因为mine数组是用来存放雷的信息的,而show是来展示的,所以不需要对show数组埋雷。雷区用字符‘1’来表示,非雷区用字符‘0’来表示。这里用的是SetMine()函数,下面是代码实现:

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = COUNT;
	while(count)//雷的个数
	{
		i = rand() % row + 1;
		j = rand() % col + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}

下面函数是用来统计某个非雷区周围有几个雷区的,用get_mine_count()函数实现。很简单,我们直接看代码:

int get_mine_count(char mine[ROWS][COLS], int x, int 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';
}

以上基本就是准备工作,接下来就是排雷操作了。主要用 FindMine()函数来实现。排雷操作我主要是对show数组的数据进行更改。因为展现在大家面前的就是show数组来模拟的一个棋盘。因为mine数组和show数组的格式是相同且一一对应的。然后大致思想就是将用户要排查的坐标在mine数组进行比对,如是否踩到雷或其周围有几个雷等,然后将结果反映到show数组对应的坐标上。让我们来看代码是怎样实现的:

void FindMine(char board1[ROWS][COLS], char board2[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int n = 0;
	while (n < (ROW * COL - COUNT))
	{
		printf("请输入要排查的坐标->");
		scanf("%d %d", &i, &j);
		if (i >= 1 && i <= 9&& j >= 1 && j <= 9&& board2[i][j]=='*')
		{
			if (board1[i][j] == '1')
			{
				printf("很遗憾,,你被炸死了\n");
				DisplayBoard(board1, ROW, COL);
				break;
			}
			else//
			
			{	
				spread(board1, board2, i, j);
				DisplayBoard(board2, row, col);
				n++;
			}
		}
		else
			printf("输入不合法,请重新输入!");
	}
	if (n == row * col - COUNT)
		printf("排雷成功!!!\n");
}

这里有个spread()函数必须要讲一下,这也是游戏设计中较难的一点。其作用就是若以输入坐标为中心,若其周围8个坐标都没有雷则将这8个坐标全部展开。再以展开的坐标依次为中心,判断其周围8个坐标是否不存在雷(已展开的坐标不用再判断,否则会形成死递归),若不存在则再展开它周围的8个坐标,然后再对这8个坐标依次进行判断......以此类推,直到不能再展开为止。我们先来看一看它的具体代码:

void spread(char mine[ROWS][COLS],char show[ROWS][COLS], int x, int y)
{
	if (0 == get_mine_count(mine, x, y))
	{
		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] == '*' && i >0  && j > 0 && i <=ROW && j <= COL)
					spread(mine, show, i, j);
		}
	}
	else
	{
		int m = get_mine_count(mine, x, y);
		show[x][y] = m + '0';
	}
	
}

这段代码是怎样实现上述功能的呢?不难看出,这里用到了递归思想。我是先对传进来的坐标进行判断,若此坐标对应的mine数组的坐标的周围8个坐标都没有雷,则将该坐标对应的show数组的元素置为字符‘ ’,再对它周围的8的坐标一一排查,若排查到某一个坐标的周围也都没有雷,则再将这某一个坐标置为‘ ’,然后再对这某一个坐标的周围8个坐标一一进行排查.......直到排查的某一个坐标的周围8个坐标的周围都至少有一个雷时停止,然后将这些不能被置为‘  ’的坐标对应的show数组的元素置为数字字符,有几个雷就显示数字字符几。

最后再补充一下,我是怎样用数字字符来显示某个非雷区的周围有几个雷的。因为数组定义的是字符数组,所以不能直接用数字来表示。那如果某个非雷区周围有3个雷的话我们怎么重置该字符数组元素好让玩家知道呢?这里就不得不先来看一下这个ASCII表:

 数字3不能用来表示,但我们可以用字符‘3’来表示,通过上图可以看出若想得到字符‘3’,则只需要在字符‘0’上加上数字3即可,字符‘0’对应的ASCII码值为48,加上3就是51,刚好对应字符‘3’。同样的,若想得到字符‘2’,则只需要用‘0’+2就行了,即用‘0’+数字就可以得到与数字相同的字符了

好了好了,到这里实现各功能的函数就大致讲完了,觉得还可以个小伙伴就给个赞吧,三连当然是最好啦。最后在这里附上全部代码。test.c里面是游戏实现逻辑,game.c里面是游戏各函数实现逻辑,game.h里面是头文件的包含,各函数、符号的声明。至于为什么这么设计欢迎看我前几天写的三子棋哈,里面有讲解。

game.h

#pragma once
//头文件的包含
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

//符号的的声明
#define ROW 9
#define COL 9
#define COLS COL+2
#define ROWS ROW+2
#define COUNT 10

//函数的声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘
void DisplayBoard(char bodrd[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char[ROWS][COLS], int row, int col);//布置雷
void FindMine(char board1[ROWS][COLS], char board2[ROWS][COLS], int row, int col);//排查雷

game.c

//游戏各函数实现逻辑
#define _CRT_SECURE_NO_WARNINGS 
#include "game.h"
#define _CRT_SECURE_NO_WARNINGS 

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i <=row; i++)
	{
		printf(" %d ", i);
	}
	printf("\n");
	for (i = 1; i <=col; i++)
	{
		printf(" %d ", i);
		for (j = 1; j <=col; j++)
		{
			printf(" %c ", board[i][j]);
		}
		printf("\n");
	}

}
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = COUNT;
	while(count)//雷的个数
	{
		i = rand() % row + 1;
		j = rand() % col + 1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}
int get_mine_count(char mine[ROWS][COLS], int x, int 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';
}

/*若以输入坐标为中心,其周围8个坐标都没有雷则全部展开。
再以展开的坐标依次为中心,判断其周围8个是否存在雷(已展开的坐标不用再判断,否则会形成死递归)以此类推,直到不能再展开为止。*/
void spread(char mine[ROWS][COLS],char show[ROWS][COLS], int x, int y)
{
	if (0 == get_mine_count(mine, x, y))
	{
		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] == '*' && i >0  && j > 0 && i <=ROW && j <= COL)
					spread(mine, show, i, j);
		}
	}
	else
	{
		int m = get_mine_count(mine, x, y);
		show[x][y] = m + '0';
	}
	
}

void FindMine(char board1[ROWS][COLS], char board2[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int n = 0;
	while (n < (ROW * COL - COUNT))
	{
		printf("请输入要排查的坐标->");
		scanf("%d %d", &i, &j);
		if (i >= 1 && i <= 9&& j >= 1 && j <= 9&& board2[i][j]=='*')
		{
			if (board1[i][j] == '1')
			{
				printf("很遗憾,,你被炸死了\n");
				DisplayBoard(board1, ROW, COL);
				break;
			}
			else//统计雷的个数
			
			{	
				spread(board1, board2, i, j);
				DisplayBoard(board2, row, col);
				n++;
			}
		}
		else
			printf("输入不合法,请重新输入!");
	}
	if (n == row * col - COUNT)
		printf("排雷成功!!!\n");
}

test.c

#define _CRT_SECURE_NO_WARNINGS 
#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');//初始化mine数组全为'0'
	InitBoard(show, ROWS, COLS, '*');//c初始化show数组全为’*‘
	
	SetMine(mine, ROW, COL);//布置雷
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);//打印雷的棋盘	
	FindMine(mine,show,ROW,COL);//排雷
}
void test()
{
	int input = 0;
	do
	{
		srand((unsigned)time(NULL));//利用时间戳来设置随机种子
		menu();
		printf("请选择->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入不合法,请重新输入!\n");
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_63039919/article/details/121319457