C++版本掃雷

閒來無事,花了點時間寫了一個簡單版本的掃雷,廢話不多數,開始今天的主題!!!
掃雷簡單版玩法(控制檯未加雙緩衝):進入掃雷界面後,按下空格,光標所在位置處會經行判斷,若是雷,則遊戲over,若是空白區域則展開地圖,若是帶有數字標誌的則僅展示改地圖塊,直至剩下雷區

看一下效果
在這裏插入圖片描述

難點:
1.如何展示
2.如何初始化地圖塊(根據難度選擇):雷位置安放以及周圍地圖塊的數字大小設置
3.如何展開空白區域,即擴散

下面一步步來放代碼

首先先了解一下本次使用的遊戲框架

void main()
{
	while (isRun)
	{
		system("cls");
		if (status == 0)  //開始場景
		{
			
		}
		else if (status == 1)  //難度選擇場景
		{
			//這裏會經行遊戲初始化
		}
		else if (status == 2)  //遊戲運行場景
		{
			GameRun()
			{
				//遊戲繪製
				//遊戲邏輯
			}
		}
		else if (status == 3)  //win界面
		{

		}
		else if (status == 4)    //failed界面
		{
			
		}
		else if (status == 5) //結束界面
		{
				//這裏經行遊戲結束的處理
		}
	}
	system("pause");
}

一、如何展示
這裏我們會用到一個展示地圖DisplayMap和一個遊戲地圖Map,展示的過程其實就是逐步將Map的內容展示到界面上
看一下我們的相關全局變量

int MapW = 0;
int MapH = 0;
int MapS = 0;
//0空白;
//1代表該位置附近有1顆雷,最多有8顆
//9代表該位置有雷
//-9代表該位置的雷被排除了(這裏我沒擴展了,可自行選擇)
char* Map;    //地圖動態數組
char* DisplayMap;//屏幕展示出來的地圖
int* ThunderIndex; //雷位置索引動態數組

int difficult = 0;
bool isRun = true;
int ThunderNum = 0;  //雷數量

//Game光標
int CursonX = 0;
int CursonY = 0;

char pic[][3] = { "□","①","②","③","④","⑤","⑥","⑦","⑧","⊕","▶","■","↖" };   //展示的字符

//遊戲狀態 0:開始場景  1:難度選擇場景 2:遊戲運行場景 3:勝利場景 4:失敗場景 5:遊戲結束場景 
int status = 0;

二、初始化遊戲數據
主要是根據遊戲選擇難度初始化遊戲的相關數據

void GameInit()
{
	if (difficult == 1)
	{
		MapW = 11;
		MapH = 11;
		ThunderNum = 15;
	}
	else if (difficult == 2)
	{
		MapW = 15;
		MapH = 15;
		ThunderNum = 23;
	}
	else if (difficult == 3)
	{
		MapW = 19;
		MapH = 19;
		ThunderNum = 31;
	}
	MapS = MapW * MapH;
	//初始化地圖
	Map = new char[MapS];
	memset(Map, 0, MapS);
	DisplayMap = new char[MapS];
	memset(DisplayMap, 11, MapS);
	ThunderIndex = new int[ThunderNum];
	SetThunderPosition();    //初始化雷的位置
	CountThunder();         //根據雷位置
} 

現在我們講解一下這裏是如何初始化雷的位置
在這裏插入圖片描述

void SetThunderPosition()
{
	int num = 0;   //記錄已經安置的雷數量
	srand((unsigned)time(0));   //使每一次遊戲有不同的地圖
	vector<int> vPosition;       //存儲Map所有的位置 0~MapS-1,不懂容器的可以先在那時理解爲數組
	for (int i = 0; i < MapS; ++i)
	{
		vPosition.push_back(i);
	}
	while (num < ThunderNum)   //開始放置雷
	{
		int index = rand() % vPosition.size();    //隨機找到需要放置的位置
		Map[vPosition[index]] = 9;      //使該位置的值爲9,表示爲雷
		 //此時當前安置的雷位置被記錄
		ThunderIndex[num] = vPosition[index];  
		//刪除已經選出的位置
		vPosition.erase(vPosition.begin() + index);
		num++;  //即將安置下一個雷
	}
}

接下來講解如何設置雷周圍的數字
在這裏插入圖片描述

//該方法效率是非常高的,因爲只遍歷雷的位置及周圍位置
void CountThunder()
{
	//當前位置的周圍 8 方向,可能越界
	const int dir[8] = { -1, -1 - MapW, -MapW, 1 - MapW, 1, 1 + MapW, MapW, -1 + MapW };
	for (int i = 0; i < ThunderNum; ++i)
	{
		for (int j = 0; j < 8; ++j)
		{
			int CountPosition = dir[j] + ThunderIndex[i];      
			if (CountPosition < 0 || CountPosition >= MapS)  //如果位置越界了
				continue;
			//判斷是否處於當前雷位置的周圍:行之差應爲-1~1,列之差也爲-1~1,必須同時滿足
			int Difference_W = CountPosition % MapW - ThunderIndex[i] % MapW; 
			int Difference_H = CountPosition / MapW - ThunderIndex[i] / MapW;
			if (Difference_W >= -1 && Difference_W <= 1 && Difference_H >= -1 && Difference_H <= 1)
			{
				if (Map[CountPosition] == 9)      //滿足周圍後,也必須判斷不爲雷位置,才能計數
					continue;
				Map[CountPosition]++;
			}
		}
	}
}

三、遊戲的運行
1.遊戲繪製:展示DisplayMap,即已經被點開的Map位置

void GameDraw()
{
	for (int i = 0; i < MapS; ++i)
	{
		if (CursonX + CursonY * MapW == i)   //顯示光標
			cout << pic[12];
		else
			cout << pic[DisplayMap[i]];
		if (i % MapW == MapW - 1)
			cout << endl;
	}
}

2.遊戲邏輯

void GameLogic()
{
	//輸贏判斷
	int n = 0; //統計雷個數 
	for (int i = 0; i < MapS; ++i)
	{
		if (DisplayMap[i] != Map[i])
		{
			n++;
			if (n > ThunderNum)
				break;
		}
	}
	if (n == ThunderNum)  //贏了
	{
		status = 3;
		return;
	}
	//光標移動
	if (GetAsyncKeyState(VK_LEFT) & 0x8000 && CursonX > 0)
	{
		CursonX--;
	}
	else if (GetAsyncKeyState(VK_RIGHT) & 0x8000 && CursonX < MapW - 1)
	{
		CursonX++;
	}
	else if (GetAsyncKeyState(VK_UP) & 0x8000 && CursonY > 0)
	{
		CursonY--;
	}
	else if (GetAsyncKeyState(VK_DOWN) & 0x8000 && CursonY < MapH - 1)
	{
		CursonY++;
	}
	else if (GetAsyncKeyState(VK_SPACE) & 0x8000)
	{
		//只能在未展示的地圖上按鍵
		int index = CursonX + CursonY * MapW;
		if (DisplayMap[index] == 11)
		{
			if (Map[index] == 0)
				Spread(index);   //擴散空地
			else if (Map[index] > 0 && Map[index] < 9)  //非雷非空地區
				DisplayMap[index] = Map[index];
			else if (Map[index] == 9)   //踩雷
			{
				status = 4;
				return;
			}
		}
	}
}

3.擴散
在這裏插入圖片描述
在這裏插入圖片描述

//擴散
void Spread(int index)
{
	//仍然選定8方向
	const int dir[8] = { -1, -1 - MapW, -MapW, 1 - MapW, 1, 1 + MapW, MapW, -1 + MapW };
	vector<int> vecSpread;  //沒學過的可以先看看,或者用長度大一點的數組
	vecSpread.push_back(index);
	DisplayMap[index] = 0;      //首先記錄當前空地位置,並設置展示位置爲空地
	for (int i = 0; i < vecSpread.size(); ++i)
	{
		if (DisplayMap[vecSpread[i]] != 0)
			continue;
		for (int j = 0; j < 8; ++j)
		{
			int temp_index = vecSpread[i] + dir[j];
			if (temp_index < 0 || temp_index >= MapS) //越界
				continue;
			//判斷是否處於當前雷位置的周圍:行之差應爲-1~1,列之差也爲-1~1,必須同時滿足
			int Difference_W = temp_index % MapW - vecSpread[i] % MapW;
			int Difference_H = temp_index / MapW - vecSpread[i] / MapW;
			if (Difference_W >= -1 && Difference_W <= 1 && Difference_H >= -1 && Difference_H <= 1)
			{
				if (DisplayMap[temp_index] != 11) //記錄過的無法再記錄
					continue;
				if (Map[temp_index] >= 0 && Map[temp_index] <= 8)
				{
					vecSpread.push_back(temp_index);
					DisplayMap[temp_index] = Map[temp_index];
				}
			}
		}
	}
}

至此,所有存在難度的問題全部解決

下面是源代碼的下載,如有需要可以下載
https://pan.baidu.com/s/18L4X4KIYxmV92itEncq-Wg

發佈了19 篇原創文章 · 獲贊 16 · 訪問量 1205
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章