【C語言基礎學習---掃雷遊戲】(包含普通版+遞迴煉獄版)

2021-05-06 13:00:47

遊戲背景介紹
掃雷遊戲是一款大眾類的益智小遊戲,於1992年發行。遊戲目標是在最短的時間內根據點選格子出現的數位找出所有非雷格子,同時避免踩雷,踩到一個雷即全盤皆輸。
掃雷在科技歷史上也扮演了相似的角色。這個基於數位的邏輯謎題最早來自20世紀六七十年代,當時Jerimac Ratliff推出的名為「Cube」的遊戲已經非常受人歡迎。幾十年後的1992年,掃雷遊戲被加入了Windows3.1,這並不是為了展示Windows是遊戲作業系統專家,而是為了訓練使用者的滑鼠左右鍵操作能力,讓這些動作變得非常自然,並培養滑鼠移動的速度和準確性。



前期準備:

工程檔案思路:
主函數放到Mine_clearing.c(掃雷)中
掃雷遊戲具體的實現和宣告放到game.c / game.h中
在這裡插入圖片描述
掃雷遊戲測試的思路(Mine_clearing.c):

1、至少玩一次,可以玩多次,do…while迴圈
2、進入遊戲後先列印選單提示
3、提示使用者輸入,根據輸入值來確定後續的遊戲程序
(1代表玩遊戲,0代表退出,其他需要重新選擇)

#include<stdio.h>
void game_menu()
{
	printf("**********************************\n");
	printf("********   1.play game   *********\n");
	printf("********   0.exit game   *********\n");
	printf("**********************************\n");
}
int main()
{
	int input;
	do
	{
		game_menu();
		printf("Please choose:>>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("Game playing......\n");
			//為了驗證Mine_clearing的掃雷遊戲測試是否ok
			//這裡暫時列印「Game playing」
			//後續這裡封裝掃雷遊戲 game()
			break;
		case 0:
			printf("Exit game!\n");
			break;
		default:
			printf("Choose wrong ! Please try again!\n");
			break;
		}
	} while (input);
	return 0;
}

掃雷遊戲設計的思路:

1)我們可以去瀏覽器中隨意開啟一個掃雷線上小遊戲連結,點選進去檢視(選擇最簡單的初級 / 入門模式)
在這裡插入圖片描述
根據別人的掃雷遊戲模型可以設計自己後面要實現的掃雷遊戲模型,比如說:
在橫和列兩端新增數位(因為後面我們實現的掃雷遊戲要輸入座標,新增數位後方便快速定位到座標點)
在這裡插入圖片描述
要知道掃雷遊戲玩的一些具體過程,可以嘗試玩幾把掃雷遊戲,比如:
在這裡插入圖片描述
在玩遊戲的過程中,我們可以總結出以下幾點規律:

1、如果選擇的位置存在雷,直接被炸死
2、如果選擇的位置不存在雷,顯示該座標周圍一圈存在雷的個數
3、如果最後剩下的位置均為雷,那麼玩家遊戲勝利。
進階功能:
1、展開功能:如果玩家選擇的位置周圍均沒有雷,則自動展開(遞迴展開)(本次程式碼已實現)
2、標記雷,如果一個位置確定是雷,可以標記「是雷」(比如用符號‘m’標記);
標記可能是雷,如果一個位置可能是雷,但是不是很確定,可以標記「可能是雷」(比如用符號’?'標記)(本次程式碼暫未實現)

在這裡插入圖片描述

原始碼下載及效果展示:

【掃雷普通版】
在這裡插入圖片描述
【掃雷遞迴煉獄版】
在這裡插入圖片描述
掃雷遊戲github原始碼】(包含普通版+遞迴煉獄版)

完整程式碼:

原始檔Mine_clearing.c內容

/*******************/
//以下是原始檔Mine_clearing.c內容
/*******************/
#include"game.h"
void game_menu()
{
	printf("**********************************\n");
	printf("********   1.play game   *********\n");
	printf("********   0.exit game   *********\n");
	printf("**********************************\n");
}
void Mine_clearing_game()//掃雷遊戲的具體過程
{
	//建立兩個二維陣列
	char mine[ROWS][COLS] = { 0 };//棋盤一:存放雷的棋盤
	char show[ROWS][COLS] = { 0 };//棋盤二:存放排查雷的資訊的棋盤
	//初始化棋盤
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//列印棋盤
	//DisplayBoard(mine, ROW, COL);//列印棋盤的時候只需要列印中間的部分
	DisplayBoard(show, ROW, COL);//擴充出來的最外圍一圈不需要列印出來

	//佈置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);//用於測試檢查使用
	 
	//排查雷
	FindMine(mine, show, ROW, COL);
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		game_menu();
		printf("Please choose:>>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Mine_clearing_game();//掃雷遊戲
			break;
		case 0:
			printf("Exit game!\n");
			break;
		default:
			printf("Choose wrong ! Please try again!\n");
			break;
		}
	} while (input);
	return 0;
}

//int main()
//{
//	int input;
//	do
//	{
//		game_menu();
//		printf("Please choose:>>\n");
//		scanf("%d", &input);
//		switch (input)
//		{
//		case 1:
//			printf("Game playing......\n");
//			//為了驗證Mine_clearing的掃雷遊戲測試是否ok
//			//這裡暫時列印「Game playing」
//			//後續這裡封裝掃雷遊戲 game()
//			break;
//		case 0:
//			printf("Exit game!\n");
//			break;
//		default:
//			printf("Choose wrong ! Please try again!\n");
//			break;
//		}
//	} while (input);
//	return 0;
//}

標頭檔案game.h

/*******************/
//以下是標頭檔案game.h內容
/*******************/
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//佈置雷的個數
#define EASY_COUNT 10
#define MIDDLE_COUNT 20
#define HARD_COUNT 50

//棋盤的行號、列號
//#define ROW 9
//#define COL 9

#define ROW 15
#define COL 15

#define ROWS  ROW+2
#define COLS  COL+2

//初始化棋盤宣告
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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);



原始檔game.c

/*******************/
//以下是原始檔game.c內容
/*******************/
#include"game.h"

//初始化棋盤的實現
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;
	//為了方便區分,我們在列印的時候加上這麼一行話
	printf("-----------Mine_clearing game-------------\n");

	//列印列號
	for (i = 0; i <= col; i++)
	{
		//printf("%d ", i);
		printf("%2d ", i);
	}
	printf("\n");
	
	for (i = 1; i <= row; i++)
	{
		//列印行號
		//printf("%d ", i);
		printf("%2d ", i);
		for (j = 1; j <= col; j++)
		{
			//printf("%c ", board[i][j]);
			printf("%2c ", board[i][j]);
		}
		printf("\n");
	}
	printf("-----------Mine_clearing game-------------\n");

}

//佈置雷的實現
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//假設我們要佈置雷的個數是count
	//int count =	EASY_COUNT;
	int count = HARD_COUNT;
	while (count)
	{
		//我們佈置雷的時候,雷的位置最好是隨機的
		int x = rand() % row + 1;//雷出現的座標位置:行號只能在 1至row 
		int y = rand() % col + 1;//雷出現的座標位置:列號也只能在 1至col
		if (mine[x][y]=='0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

//統計周圍雷的資訊
int get_mine_count(char mine[ROWS][COLS],int x,int y)
{
	方法一:
	//return mine[x - 1][y - 1] +
	//	mine[x - 1][y] +
	//	mine[x - 1][y + 1] +
	//	mine[x][y - 1] +
	//	mine[x][y + 1] +
	//	mine[x + 1][y - 1] +
	//	mine[x + 1][y] +
	//	mine[x + 1][y + 1] - 8 * '0';
	
	//方法二
	int i = 0;
	int j = 0;
	int sum = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			sum += mine[x + i][y + j] - '0';
		}
	}
	return sum;
}

void SetBlank(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
	int count = get_mine_count(mine, x, y);
	if (count == 0)
	{
		show[x][y] = ' ';
		if (x - 1 >= 0 && x <= ROW && y >= 0 && y <= COL && show[x - 1][y] == '*')
		{
			SetBlank(show, mine, x - 1, y);
		}
		if (x + 1 >= 0 && x <= ROW && y >= 0 && y <= COL && show[x + 1][y] == '*')
		{
			SetBlank(show, mine, x + 1, y);
		}
		if (x >= 0 && x <= ROW && y - 1 >= 0 && y <= COL && show[x][y-1] == '*')
		{
			SetBlank(show, mine, x, y - 1);
		}
		if (x >= 0 && x <= ROW && y + 1 >= 0 && y <= COL && show[x][y + 1] == '*')
		{
			SetBlank(show, mine, x, y + 1);
		}
	}
	else
	{
		show[x][y] = count + '0';
	}
}

int Is_win(char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
			{
				count++;
			}
		}
	}
	return count;
}

//排查雷的實現:遞迴展開版本
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//思路:
	//1.輸入排查雷的座標
	//2.進行排查
	//(1)如果該座標是雷,就提示 You lose!you get a mine.
	// (2) 如果該座標不是雷,統計該座標周圍雷的個數---排查雷的資訊放到show陣列中去

	int x = 0;
	int y = 0;
	//判斷輸入座標的合法性
	while (1)
	{
		printf("please input two number between %d to %d:>>",1,ROW);//ROW設定成與COL相等,任意使用其中一個
		scanf("%d%d", &x, &y);
		if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
		{
			if (mine[x][y] == '1')
			{
				printf("You lose!you get a mine!\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				SetBlank(show, mine, x, y);
				int count = Is_win(show, ROW, COL);
				if (count == EASY_COUNT)
				{
					printf("Congratulations,you win!\n");
				    DisplayBoard(show, ROW, COL);
					break;
				}
				DisplayBoard(show, ROW, COL);
			}
		}
		else
		{
			printf("Wrong coordinate,please try again!\n");
		}
	}
}

排查雷的實現:普通版本
//void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	//思路:
//	//1.輸入排查雷的座標
//	//2.進行排查
//	//(1)如果該座標是雷,就提示 You lose!you get a mine.
//	// (2) 如果該座標不是雷,統計該座標周圍雷的個數---排查雷的資訊放到show陣列中去
//
//	int x = 0;
//	int y = 0;
//	int win = 0;
//	//判斷輸入座標的合法性
//	while (win<row*col-EASY_COUNT)
//	{
//		printf("please input two number between %d to %d:>>",1,ROW);
//		scanf("%d%d", &x, &y);
//		if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
//		{
//			if (mine[x][y] == '1')
//			{
//				printf("You lose!you get a mine!\n");
//				DisplayBoard(mine, ROW, COL);
//				break;
//			}
//			else
//			{
//				//不是雷的情況下,統計x,y周圍雷的資訊
//				int count = get_mine_count(mine, x, y);
//				show[x][y] = count + '0';
//				DisplayBoard(show, ROW, COL);
//				win++;
//			}
//		}
//		else
//		{
//			printf("Wrong coordinate,please try again!\n");
//		}
//	}
//	if (win == row * col - EASY_COUNT)
//	{
//		printf("Congratulations,you win!\n");
//		DisplayBoard(mine, ROW, COL);
//	}
//}