【C语言】扫雷游戏实现(展开+标记)

扫雷游戏介绍

        扫雷是一款经典的游戏,它有三个规格:9*9(初级),16*16(中级),16*30(高级),初级包含10个雷,中级包含40个雷,高级包含99个雷。

        其游戏的规则为:

        点击一个方格,若该方格不是雷,则其回返回以它为中心的周围八个方格内包含的雷的个数,如周围有一个雷则返回1,周围没有雷则返回空白。而若该方格是雷,则玩家被雷炸死,游戏结束。

        该游戏的胜利条件便为排查出所有不是雷的方格。

        而我们将分为三个文件来编写扫雷:

        game.h包含头文件,函数的声明,游戏需要的数据类型等。

        main.cpp包含游戏的测试逻辑,菜单的打印等。

        game.cpp游戏主体,包含实现游戏的函数等。

扫雷游戏设计

棋盘的设置

        要进行扫雷,我们首先需要能够扫雷的棋盘。而在扫雷中,无论是设置雷还是排查雷等的信息都需要储存,而二维数组可以很好的满足我们的需求。

        所以我们定义两个二维数组,一个用来保存雷设置的信息,另一个用来保存玩家排查雷的信息,使二者数据不相互干扰:

        为了方便后期的代码的维护和难度级别的修改,我们将行和列定义成宏 ,我们这里以初级模式9*9为例。

         这里我们多定义一个行和列的原因是在排查雷的过程中,若排查的方格在边角位(如下图中1的位置),就会发生越界,未避免越界发生,我们可以上下左右各多增添一行,这样既方便排查又不用担心数组越界。

棋盘的初始化

        有了棋盘之后,我们便要将棋盘进行初始化。

        保存雷信息的棋盘(不展示给玩家)先全部初始化为0。

        而存放玩家排查信息的棋盘(展示给玩家)则全初始化为‘*’。

棋盘打印

        有了棋盘之后,我们可以将棋盘打印出来看看,方便排查代码问题。

        tip:在排查代码问题阶段,我们可以先将存放雷信息的棋盘打印出来,给自己开“透视”,方便自己调试。

设置雷

        要扫雷,那棋盘上要先有雷。为了游戏的趣味性,生成的雷的坐标是要有随机性的,所有要用到rand函数(注意配套srand使用)

        注意:生成的雷的坐标应该在1-9之间,不要超过棋盘的范围(设置11*11只是为了方便排查,真正展示实际是9*9);同时注意雷坐标重复问题。

        同时为了游戏难度的更改变化,游戏中雷的个数也设置成宏。

排查雷(判断输赢)

        在一切准备就绪后,我们就要开始游戏的重点——排查雷。

        在此我们要考虑3种情况:1.玩家点到雷了,炸死;

                                                  2.玩家没点到雷,游戏继续;

                                                  3.玩家输入坐标不在棋盘范围内,重新输入。

       而游戏也不可能选择一次就结束,所以我们要用到循环,而循环判断的条件则为剩余非雷的方格个数,若在剩余方格数等于雷的个数前循环停止,则游戏失败,反之则游戏胜利。

        所以我写了两个函数,一个Isend来计算剩余的方格数,而另一个则来判断玩家是否获取胜利:

标记雷

        在玩扫雷的过程中,标记雷的功能也很重要,能大大减轻我们扫雷的难度。

        而有标记,就要有取消标记。该部分代码比较简单我就不做过多赘述:

获取周围雷的个数

        该部分只要会运用数学知识+知晓排查坐标周围八个坐标就好

        注意:存放棋盘信息的二维数组是字符数组,所以计算的时候要减去七个字符0,才能得到正确的答案。

        放上周围八个坐标的图和代码:

 

递归展开

        递归展开能给玩家带来更便捷的游戏体验

        首先来判断计算的周围方格内的雷的个数是否为0,若非0则没必要展开,直接返回计算值。

若为0则展开递归,从选择的方格开始,推及自己周围8个方格逐渐展开。

        在展开的过程中注意限制,一要判断方格是否已经被计算过,减少递归的次数;二是要限制递归展开的范围在棋盘范围内。

游戏的导入菜单

        游戏的主体部分已经完结,要使游戏正常导入自然需要一个菜单

        用do-while来保证游戏可以一次进行多次游玩,让玩家自行决定是否退出游戏。

扫雷代码展示

game.h:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <Windows.h>
//定义雷的个数
#define mine 10
//定义行和列
#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2
//函数声明
void Intboard(char board[ROWS][COLS],int rows,int cols,char c);
void Printfboard(char board[ROWS][COLS], int rows, int cols);
void Setmine(char board[ROWS][COLS], int row, int col);
void Play(char inner[ROWS][COLS], char board[ROWS][COLS], int row, int col);

main.cpp:

#include "game.h"
void menu()
{
	printf("*******************************\n");
	printf("*********** 1.play ************\n");
	printf("*********** 0.exit ************\n");
	printf("*******************************\n");
}
void game()
{
	//初始化+定义棋盘
	char my_chessboard[ROWS][COLS];
	char chessboard[ROWS][COLS];
	Intboard(my_chessboard, ROWS, COLS, '0');
	Intboard(chessboard, ROWS, COLS, '*');
	Setmine(my_chessboard, ROW, COL);//设置雷
	//Printfboard(my_chessboard, ROW, COL);给自己开外挂,方便调试
	Printfboard(chessboard, ROW, COL);//打印棋盘
	Play(my_chessboard, chessboard, ROW, COL);//排查雷
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		printf("扫雷游戏\n");
		menu();
		printf("请输入: ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("——————游戏开始——————\n");
			game();
			break;
		case 0:
			printf("——————游戏结束——————\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

game.cpp:

#include "game.h"
void Intboard(char board[ROWS][COLS], int row, int col, char c)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = c;//c为放在棋盘内的字符
		}
	}
}
void Printfboard(char board[ROWS][COLS], int row, int col)
{
	system("cls");//实时清除屏幕,使代码运行界面不会过于冗长
	printf("——————扫雷游戏——————\n");
	for (int i = 0; i <= col; i++)
		printf("%d ", i);	//输出列序列
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);	//输出行序列
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void Setmine(char board[ROWS][COLS], int row, int col)
{
	int count = mine;
	while (count)
	{
		int x = rand() % 9 + 1;//使雷的坐标具有随机性且在棋盘范围内
		int y = rand() % 9 + 1;
		if (board[x][y] == '0')//防止雷坐标重复
		{
			board[x][y] = '1';
			count--;
		}
	}
}
char caculate(char inner[ROWS][COLS], int x, int y)
{
	char ret = (inner[x - 1][y - 1] + inner[x - 1][y] + inner[x - 1][y + 1] +
				        inner[x][y - 1] + inner[x][y + 1] +
						inner[x + 1][y - 1] + inner[x + 1][y] + inner[x + 1][y + 1]) - 7 * '0';
	return ret;
}
void expand(char inner[ROWS][COLS],char board[ROWS][COLS],int x, int y)
{
	char ret = caculate(inner, x, y);
	if (ret == '0')//判断返回值是否为0,为0则开始展开
	{
		board[x][y] = ' ';
		for (int i = x - 1; i <= x + 1; i++)
		{
			for (int j = y - 1; j <= y + 1; j++)
			{
				if (board[i][j] == '*' && i >= 1 && i <= 9 && j >= 1 && j <= 9)//限制扩展范围在棋盘内
				expand(inner, board, i, j);
			}
		}
	}
	else
		board[x][y] = ret;
}
int Isend(char board[ROWS][COLS], int row, int col)
{
	int count = 0;
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (board[i][j] == '*')
				count++;
		}
	}
	return count - mine;
}
void Markmine(char board[ROWS][COLS],int row,int col)
{
	printf("需要标记雷请输入p,不需要则输入其他字符:");
	char p = 'p';
	getchar();//清除一下字符,防止对scanf造成影响
	scanf("%c", &p);
	if (p == 'p')
	{
		int x, y;
		printf("请输入要标记or取消标记的坐标:\n");
		getchar();
		scanf("%d%d", &x, &y);
		if (board[x][y] == 'M')//判断玩家是要取消标记还是要标记
		{
			board[x][y] = '*';
			Printfboard(board, row, col);
			printf("取消标记成功!\n");
		}
		else
		{
			board[x][y] = 'M';
			Printfboard(board, row, col);
			printf("标记成功!\n");
		}

	}
}
void Play(char inner[ROWS][COLS], char board[ROWS][COLS], int row, int col)
{
	time_t begin = time(NULL);//统计运行时间
	int count = row * col - mine;
	while(count=Isend(board,row,col))//判断除去雷的剩余的格子
	{
		Markmine(board,row,col);//用来标记的函数
		printf("请输入要排查的坐标:\n");
		int x, y;
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
		{
			if (inner[x][y] == '1')
			{
				Printfboard(inner, row, col);
				printf("很抱歉,你被炸死了!\n");
				printf("5秒后进入下一局\n");
				Sleep(5000);//使程序暂停5秒,防止跳转过快
				system("cls");
				break;
			}
			else if (inner[x][y] == '0')
			{
				expand(inner, board, x, y);
				Printfboard(board, row, col);
				continue;
			}
		}
		else
			printf("输入错误,请重新输入\n");
	}
	time_t end = time(NULL);
	int duration = end - begin;
	if (count == 0)
	{
		printf("恭喜你,排查出所有的雷!\n");
		printf("用时为 %d s\n", duration);
		printf("5秒后进入下一局\n");
		Sleep(3000);
		system("cls");
	}
}

感谢您的浏览!

  • 33
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值