【C++扫雷小游戏】

前言

尝试挑战经典的扫雷游戏,用C++实现自己的版本
扫雷,这个经典的休闲益智游戏,曾陪伴许多人度过无数时光。玩家需要根据已翻开的方块上的数字,推断出隐藏在方格中的地雷位置,然后用标记或者揭开其他方块的方式逐渐清理出安全的区域。通过逻辑推理与快速反应,玩家能够体验到紧张刺激以及智慧的成就感。

一.游戏规则

🚦设计游戏前先了解扫雷的基本规则,以围绕规则用C++实现:
一个扫雷盘面由许多方格(cell)组成,方格中随机分布着一定数量的雷(mine),一个格子中至多只有1雷。每个方格上都会显示数字来表示以此为中心的3*3的格子中有多少雷,胜利条件是打开所有非雷格(safe cell),失败条件是打开了一个雷格(踩雷)在这里插入图片描述

二.游戏菜单

1.创建菜单

先创建一个简单的游戏菜单

void menu()
{
	cout << "********** MENU **********\n";
	cout << "******   1. play  ******\n";
	cout << "******   0. exit  ******\n";
	cout << "*************************\n";
}

2.在主函数中使用菜单

(1)用switch case来找到选项
(2)用do while来实现一局又一局游戏的菜单弹出

int main()
{
	int input = 0;
	do
	{
		menu();
		cout << "请输入选项>";
		cin>>input;
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			cout << "退出游戏";
			break;
		default:
			cout << "输入错误,请重新输入";
			break;
		}
	} while (input);
	return 0;
}

三.方式:面向对象的思想

1.Mine(扫雷)类的创建

面向对象的设计提供了一种更加结构化的思维方式,使得代码更加清晰、可读和可维护。它能够提高代码的可重用性和可扩展性,同时也能够增强代码的灵活性和可靠性。

class Mine
{
public:
	//初始化棋盘
	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 board[ROWS][COLS], int row, int col)//排查雷
	void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//找周围有多少雷 
	int GetMineCount(char mine[ROWS][COLS], int x, int y)};

并创建一个扫雷对象mine1

Mine mine1;

四.核心内容及其实现

1.棋盘的初始化

以二维数组的形式展现棋盘
1.其中’0’元素为安全格
2.'1’元素为雷区

char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };

mine棋盘为被初始化且被埋雷的棋盘
show棋盘为用户每次操作棋盘时可看到的界面

包含的元素:雷的个数,棋盘大小

#define ROW 5//元素所在区域大小
#define COL 5
#define ROWS ROW + 2//棋盘大小偏大防止越界
#define COLS COL + 2
#define EASY_COUNT 5//雷的个数
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;
			}
		}
	}

对于mine棋盘和show棋盘的初始化

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

2.打印棋盘

代码实现

void DisplayBoard(char board[ROWS][COLS], int row, int col)
	{
		int i = 0;
		int j = 0;
		cout << "------------扫雷------------\n";
		for (i = 0; i <= row; i++)
		{
			cout << i<<" ";
		}
		cout << "\n";
		for (i = 1; i <= row; i++)
		{
			cout << i<<" ";
			for (j = 1; j <= col; j++)
			{
				cout << board[i][j]<<" ";
			}
			cout << "\n";
		}
		cout << "------------扫雷------------\n";
	}

在这里插入图片描述

上图即为向用户展示的棋盘

3.在棋盘中埋雷

在棋盘中埋雷采用横纵坐标随机取值的方法
其中要调用rand()函数//其头文件为cstdlib
而对于x,y随机取值不能越界可使用如下方法

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

并且每次埋完一个雷就要减去一次总计雷的个数并再次进入循环

代码实现

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
		}
		count--;
	}
}

4.统计周围雷的个数

由于用’1’表示的雷,用’0’表示的安全格,则可以直接 返回其周围元素的值之和再减去8个’0’即可

代码实现

int GetMineCount(char mine[ROWS][COLS], int x, int y)
	{
		return mine[x + 1][y] + mine[x - 1][y] +
			mine[x][y + 1] + mine[x][y - 1] +
			mine[x + 1][y + 1] + mine[x - 1][y + 1] +
			mine[x - 1][y - 1] + mine[x + 1][y - 1] - 8 * '0';
	}

5.排查雷

排查雷一共分三种情况
1.第一种是排查到show中的当前元素 不是*时,说明已经翻过此格子,需要重新排查
2.第二种是排查到mine中的当前元素是’1’时,那就是找到了炸弹,游戏结束
3.第三种就是排查到mine中的当前元素是’0’时,找到他周围有多少个雷,并在show中的此位置返回个数值。

要定义一个 count2来进行计数,如果count2的个数达到安全格的个数时,则说明游戏胜利
注意:在每次翻完格子或者在游戏结束时,都要添加打印函数来向用户展示实时情况

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
		{
	int x = 0;
	int y = 0;
	int count2 = 0;
	while (count2 < row * col - EASY_COUNT)
	{
		cout << "请输入横坐标>";
		cin>>x;
		cout << "请输入纵坐标>";
		cin >> y;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				cout << "输入坐标重复,请重新输入\n";
			}
			else if (mine[x][y] == '1')
			{
				cout << "笨猪你被炸死了\n";
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				//不是雷,就统计x,y坐标周围有几个雷
				int c = GetMineCount(mine, x, y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
				count2++;
			}
		}
		else
		{
			cout<<"输入坐标错误,请重新输入\n";
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		cout << "排雷成功\n";
		DisplayBoard(mine, ROW, COL);
	}
}

五.完整代码

类中代码的实现

#include<iostream>
#include<cstdlib>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
using namespace std;
class Mine
{
public:
	//初始化棋盘
	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;
		cout << "------------扫雷------------\n";
		for (i = 0; i <= row; i++)
		{
			cout << i<<" ";
		}
		cout << "\n";
		for (i = 1; i <= row; i++)
		{
			cout << i<<" ";
			for (j = 1; j <= col; j++)
			{
				cout << board[i][j]<<" ";
			}
			cout << "\n";
		}
		cout << "------------扫雷------------\n";
	}
	//布置雷
	void SetMine(char board[ROWS][COLS], int row, int col)
		{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
		}
		count--;
	}
		}
	//排查雷
	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)
	{
		cout << "请输入横坐标>";
		cin>>x;
		cout << "请输入纵坐标>";
		cin >> y;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				cout << "输入坐标重复,请重新输入\n";
			}
			else if (mine[x][y] == '1')
			{
				cout << "笨猪你被炸死了\n";
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				//不是雷,就统计x,y坐标周围有几个雷
				int c = GetMineCount(mine, x, y);
				show[x][y] = c + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			cout<<"输入坐标错误,请重新输入\n";
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		cout << "排雷成功\n";
		DisplayBoard(mine, ROW, COL);
	}
}
	//找周围有多少雷 
	int GetMineCount(char mine[ROWS][COLS], int x, int y)
	{
		return mine[x + 1][y] + mine[x - 1][y] +
			mine[x][y + 1] + mine[x][y - 1] +
			mine[x + 1][y + 1] + mine[x - 1][y + 1] +
			mine[x - 1][y - 1] + mine[x + 1][y - 1] - 8 * '0';
	}
};

把棋盘的实现步骤按顺序存放进新的函数game()中,便于 菜单的调用。
其中包含对象的创建,和对象函数的按序调用。

void game()
{
	Mine mine1;
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	mine1.InitBoard(mine, ROWS, COLS, '0');
	mine1.InitBoard(show, ROWS, COLS, '*');
	mine1.DisplayBoard(show, ROW, COL);
	mine1.SetMine(mine, ROW, COL);
	mine1.FindMine(mine, show, ROW, COL);
}

菜单及主函数的实现

int main()
{
	//srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		cout << "请输入选项>";
		cin>>input;
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			cout << "退出游戏";
			break;
		default:
			cout << "输入错误,请重新输入";
			break;
		}
	} while (input);
	return 0;
}

💘此扫雷小游戏是一个较简单的实现,读者可参考后根据自身写出符合需求的更高阶的制作。

做一个M*N的扫雷游戏,每个方格包含两种状态:关闭和打开,初始化时每个方格都是关闭的,一个打开的方格也会包含两种状态:一个数字或者一个雷。你可以打开一个方格,如果你打开的是一个雷,那么就失败;否则就会打开一个数字,该数字是位于[0,8]的一个整数,该数字表示其所有邻居方格所包含的雷数,应用该信息可以帮助你扫雷。 要求细节: (1) 能够打开一个方格,一个已打开的方格不能再关闭。 (2) 能够标记一个方格,标记方格的含义是对该方格有雷的预测,当一个方格标记后该方格不能被打开,只能执行取消标记的操作,取消标记后才能被打开。 (3) 合理分配各个操作的按键,以及各方格各种状态如何合理显示。 基本要求: 能够给出游戏结果(输,赢,剩余雷数,用掉的时间按秒计)。 游戏界面最好图形化,否则一定要有清楚的字符界面。 输入: 用户鼠标左键点击界面格子打开格子,鼠标右键点击界面格子进行标记。 输出: 界面上用户点击的格子打开,计时器开始计时并显示在界面上。如果不是雷,则显示格子周围格子数目,如果数目是0,则自动打开周围雷数为0的格子,如果是雷,游戏结束。当用户标记一个格子,对应格子显示被标记符号,同时界面显示的剩余雷数减1。当扫完所有雷,玩家获胜,游戏结束。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

马户一口吞不下又鸟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值