掃雷遊戲的功能説明

• 使⽤控制枱實現經典的掃雷遊戲
• 遊戲可以通過菜單實現繼續玩或者退出遊戲
• 掃雷的棋盤是9*9的格⼦
• 默認隨機佈置10個雷
• 可以排查雷:
◦ 如果位置不是雷,就顯⽰周圍有⼏個雷
◦ 如果位置是雷,就炸死遊戲結束
◦ 把除10個雷之外的所有⾮雷都找出來,排雷成功,遊戲結束

test.c //⽂件中寫遊戲的測試邏輯
game.c //⽂件中寫遊戲中函數的實現等
game.h //⽂件中寫遊戲需要的數據類型和函數聲明等

邏輯開始:

一、菜單

  1. 輸入1進入遊戲,輸入0退出遊戲,輸入其他數字顯示輸入錯誤,並且重新輸入
test.c
#include "game.h"

int main()
{
	menu();
	{
	regain:
		printf("請輸入你的選擇:");
		int input1;
		scanf("%d", &input1);
		switch (input1)
		{
		case 1:
		{
			printf("進入遊戲\n");
			game();
			break;
		}
		case 0:
		{
			printf("退出遊戲\n");
			break;

		}
		default:
		{
			printf("輸入錯誤,請重新輸入:");
			goto regain;
		}
		}
	}
	return 0;
}
game.c
#include "game.h"

void menu()
{
	printf("****************\n");
	printf("**** 1.Play ****\n");
	printf("**** 0.Quit ****\n");
	printf("****************\n");

}
game.h
#pragma once
#include <stdio.h>
#include "game.h"

//菜單
void menu();

二、生成 9X9 的遊戲界面

  1. 使用二維數組實現
  2. 運用兩個棋盤,一個用於展示,一個用於設置雷,寫出初始化棋盤的函數
  3. 將展示的棋盤char show全部初始化為 '*',將佈置雷的棋盤char mine全部初始化為0
  4. 為方便後邊測試,可以先把打印棋盤的函數寫出

test.c文件增加了以下代碼

test.c
#include "game.h"

void game()
{
	//用於佈置雷的二維數組
	char mine[ROWS][COLS] = { 0 };

	//用於遊戲界面的的二維數組
	char show[ROWS][COLS] = { 0 };

	//用於遊戲界面的的二維數組全部初始為 '*'
	set_keyboard(show, ROWS, COLS, '*');

	//用於佈置雷的二維數組全部初始化為 '0'
	set_keyboard(mine, ROWS, COLS, '0');

	//打印函數
	printf_keyboard(show, ROW, COL);
	printf_keyboard(mine, ROW, COL);
}

game.c文件增加了以下代碼

game.c
#include "game.h"

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

//展示棋盤
void printf_keyboard(char board[ROWS][COLS], int row, int col)
{
	printf("-------掃雷--------\n");
	for (int r = 0; r <= row; r++)
	{
		printf("%d ", r);
	}

	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");
	}
}

game.c文件增加了以下代碼

#pragma once
#include <stdio.h>
#include "game.h"

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


//菜單
void menu();

//初始化棋盤
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

//展示棋盤
void printf_keyboard(char board[ROWS][COLS], int row, int col);

三、隨機佈置雷

  1. 使用srand((unsigned int) time(NULL))rand()
  2. 將雷設置為1,雷只能佈置在char mine[x][y] == '0'的地方

game.h文件增加了
#include <time.h>#include "stdlib.h"#define MINE 10void set_mine(char board[ROWS][COLS], int row, int col, int mine);

game.h

#pragma once
#include <stdio.h>
#include "game.h"
#include <time.h> 
#include "stdlib.h"

#define ROW 9
#define COL 9
#define ROWS ROW+3
#define COLS COL+2
#define MINE 10


//菜單
void menu();

//初始化棋盤
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

//展示棋盤
void printf_keyboard(char board[ROWS][COLS], int row, int col);

//隨機佈置雷
void set_mine(char board[ROWS][COLS], int row, int col, int mine);
game.c文件增加以下代碼
game.c

//隨機佈置雷
void set_mine(char board[ROWS][COLS], int row, int col, int mine)
{
	srand((unsigned int) time(NULL));

	while (mine)
	{
		int x = (rand() % row) + 1;
		int y = (rand() % col) + 1;
		if (board[x][y] == '0');
		{
			board[x][y] = '1';
			mine--;
		}
	}

}

test.c文件增加以下代碼

test.c

//隨機佈置雷
set_mine(mine, ROW, COL, MINE);

四、排雷

  1. 注意輸入的座標,橫縱座標都只能是0~9,出現其他數字報錯,並重新輸入
  2. 所排的座標要顯示周圍雷的個數,如果為0,展開周圍的棋盤(運用到遞歸)
  3. 如果所排的座標是雷,顯示遊戲結束
  4. 如果輸入的座標是已經輸入過的座標,顯示該座標已經排除
  5. 判斷遊戲勝利,排除的座標個數與減掉雷後的格子數相等

test.c文件佈局改為以下情況

test.c
void game()
{
	//用於佈置雷的二維數組
	char mine[ROWS][COLS] = { 0 };

	//用於遊戲界面的的二維數組
	char show[ROWS][COLS] = { 0 };

	//用於遊戲界面的的二維數組全部初始為 '*'
	set_keyboard(show, ROWS, COLS, '*');

	//用於佈置雷的二維數組全部初始化為 '0'
	set_keyboard(mine, ROWS, COLS, '0');

	//隨機佈置雷
	set_mine(mine, ROW, COL, MINE);

	//打印函數
	printf_keyboard(show, ROW, COL);
	//printf_keyboard(mine, ROW, COL);
	
	//排雷
	move_mine(show, mine, ROW, COL);
}

game.c文件增加了以下代碼

game.c
//計算周圍雷的個數
int Count_mine(char mine[ROWS][COLS], int x, int y)
{
	return mine[x][y] - '0';
}

//展開棋盤----遞歸
void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
	//寫遞歸首先寫結束條件

	//越界時,返回
	if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1))
	{
		return;
	}

	//遇到以及排過雷的座標返回
	if (show[x][y] != '*')
	{
		return;
	}

	//計算雷的個數
	int count = 0;
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			count += Count_mine(mine, x + i, y + j);
		}
	}
	show[x][y] = count + '0';
	
	//按照遊戲規則,如果座標顯示雷的數目不為零,則返回
	if (show[x][y] != '0')
	{
		return;
	}

	//展開雷
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			Open_keyboard(show, mine, x + i, y + j);
		}
	}

}

//排雷
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int x, y;
	while (1)
	{
		printf("請輸入要排查的座標:");
	regain2:
		scanf("%d %d", &x, &y);
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '0')
				{
					Open_keyboard(show,mine,x,y);
					printf_keyboard(show, ROW, COL);
				}
				else
				{
					printf("很遺憾,踩到雷了,遊戲結束\n以下是雷的位置:");
					printf_keyboard(mine, ROW, COL);
					break;
				}
			}
			else
			{
				printf("該座標已經排查過了,請輸入別的座標:");
				goto regain2;
			}
		}
		else
		{
			printf("輸入錯誤,重新輸入:");
			goto regain2;
		}

		//判斷贏
		int Remove_mine_count = 0;
		for (int i = 1; i <= row; i++)
		{
			for (int j = 1; j <= col; j++)
			{
				if (show[i][j] != '*')
				{
					Remove_mine_count++;
				}
			}
		}
		if (Remove_mine_count == ((ROW * COL) - MINE))
		{
			printf("恭喜你,排除所有的雷,遊戲勝利\n");
			printf_keyboard(mine, ROW, COL);
			break;
		}
	}
}

game.h文件的代碼不變

game.h
#pragma once
#include <stdio.h>
#include "game.h"
#include <time.h> 
#include "stdlib.h"

#define ROW 9
#define COL 9
#define ROWS ROW+3
#define COLS COL+2
#define MINE 10


//菜單
void menu();

//初始化棋盤
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

//展示棋盤
void printf_keyboard(char board[ROWS][COLS], int row, int col);

//隨機佈置雷
void set_mine(char board[ROWS][COLS], int row, int col, int mine);

//排雷
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

五、掃雷遊戲完整代碼

test.c

#include "game.h"  // 包含掃雷遊戲所需的頭文件(聲明函數、宏定義等)

// 遊戲核心邏輯函數,負責初始化遊戲數據、佈置雷、處理排雷過程
void game()
{
	// 定義二維數組mine,用於存儲雷的位置信息('1'表示有雷,'0'表示無雷)
	// ROWS和COLS是宏定義,通常比實際遊戲區域大2(用於處理邊界判斷,避免越界)
	char mine[ROWS][COLS] = { 0 };

	// 定義二維數組show,用於展示給玩家的界面(初始為'*',排雷後顯示周圍雷數或雷)
	char show[ROWS][COLS] = { 0 };

	// 初始化show數組,全部元素設為'*'(表示未探索的格子)
	// set_keyboard是自定義函數,用於批量初始化二維數組
	set_keyboard(show, ROWS, COLS, '*');

	// 初始化mine數組,全部元素設為'0'(先默認所有格子無雷,後續再隨機佈置雷)
	set_keyboard(mine, ROWS, COLS, '0');

	// 在mine數組中隨機佈置雷,MINE是宏定義(雷的總數)
	// ROW和COL是宏定義,代表實際遊戲區域的行數和列數(比ROWS、COLS小2)
	set_mine(mine, ROW, COL, MINE);

	// 打印玩家界面(show數組),展示當前未探索的格子(全為'*')
	printf_keyboard(show, ROW, COL);
	// 調試用:打印雷的位置(mine數組),實際遊戲中不會顯示給玩家
	// printf_keyboard(mine, ROW, COL);
	
	// 進入排雷邏輯,玩家輸入座標,處理排雷結果(顯示周圍雷數、踩雷結束等)
	move_mine(show, mine, ROW, COL);
}

// 主函數,程序入口,負責展示菜單和處理用户選擇
int main()
{
	menu();  // 調用menu函數,打印遊戲菜單(如"1. 開始遊戲 0. 退出遊戲")

	{  // 局部作用域,隔離內部變量
	regain1:  // 跳轉標籤,用於輸入錯誤時重新回到輸入步驟
		printf("請輸入你的選擇:");  // 提示用户輸入選項(1或0)
		int input1;  // 存儲用户輸入的選項
		scanf("%d", &input1);  // 讀取用户輸入

		// 根據用户輸入的選項執行對應操作
		switch (input1)
		{
		case 1:  // 用户選擇"開始遊戲"
		{
			printf("進入遊戲\n");  // 提示進入遊戲
			game();  // 調用game函數,啓動掃雷遊戲邏輯
			break;  // 退出switch分支
		}
		case 0:  // 用户選擇"退出遊戲"
		{
			printf("退出遊戲\n");  // 提示退出遊戲
			break;  // 退出switch分支
		}
		default:  // 用户輸入了無效選項(非1和0)
		{
			printf("輸入錯誤,請重新輸入:");  // 提示輸入錯誤
			goto regain1;  // 跳轉到regain1標籤,重新等待用户輸入
		}
		}
	}

	return 0;  // 程序正常結束
}

game.c

#include "game.h"  // 包含掃雷遊戲所需的頭文件(宏定義、函數聲明等)

// 打印遊戲菜單
void menu()
{
	printf("****************\n");
	printf("**** 1.Play ****\n");  // 1表示開始遊戲
	printf("**** 0.Quit ****\n");  // 0表示退出遊戲
	printf("****************\n");
}

// 初始化棋盤(二維數組)
// board:要初始化的二維數組
// rows、cols:數組的行數和列數
// set:初始化填充的字符(如'*'或'0')
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set)
{
	// 遍歷數組的每個元素,設置為指定字符set
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

// 展示棋盤(打印到控制枱)
// board:要展示的二維數組(玩家界面或雷區)
// row、col:實際遊戲區域的行數和列數(不包含邊界)
void printf_keyboard(char board[ROWS][COLS], int row, int col)
{
	printf("-------掃雷--------\n");
	// 打印列號(0到col),方便玩家定位座標
	for (int r = 0; r <= row; r++)
	{
		printf("%d ", r);
	}
	printf("\n");

	// 打印每行內容(包含行號和對應格子的字符)
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);  // 打印行號(1到row)
		// 打印當前行的每個格子(從第1列到第col列)
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");  // 每行結束後換行
	}
}

// 隨機佈置雷到雷區數組
// board:存儲雷區信息的二維數組('1'表示有雷,'0'表示無雷)
// row、col:實際遊戲區域的行數和列數
// mine:要佈置的雷的總數
void set_mine(char board[ROWS][COLS], int row, int col, int mine)
{
	srand((unsigned int)time(NULL));  // 初始化隨機數種子,確保每次雷的位置不同

	// 循環佈置雷,直到雷的數量為0
	while (mine)
	{
		// 生成1到row範圍內的隨機行座標x
		int x = (rand() % row) + 1;
		// 生成1到col範圍內的隨機列座標y
		int y = (rand() % col) + 1;

		// 如果當前位置沒有雷(為'0'),則佈置雷(設為'1')
		if (board[x][y] == '0')  // 注意:原代碼此處多了一個分號,可能是筆誤,實際應去掉
		{
			board[x][y] = '1';
			mine--;  // 雷的數量減1
		}
	}
}

// 計算指定座標周圍8個方向的雷的總數
// mine:雷區數組
// x、y:要檢查的座標
int Count_mine(char mine[ROWS][COLS], int x, int y)
{
// 計算座標(x,y)周圍8個相鄰格子中雷的總數
// 原理:將周圍8個格子的字符值('0'表示無雷,'1'表示有雷)轉換為數字後求和
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個'0'的ASCII值(將字符轉為數字:'0'→0,'1'→1)
);
}

// 遞歸展開無雷區域(當某個格子周圍無雷時,自動展開周圍所有無雷格子)
// show:玩家界面數組
// mine:雷區數組
// x、y:當前要展開的座標
void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
	// 遞歸結束條件1:座標越界(超出實際遊戲區域)
	if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1))
	{
		return;
	}

	// 遞歸結束條件2:該座標已被探索過(非'*')
	if (show[x][y] != '*')
	{
		return;
	}

	// 計算當前座標周圍8個方向的雷的總數
	int count = 0;
	for (int i = -1; i <= 1; i++)  // 行方向:-1(上)、0(當前)、1(下)
	{
		for (int j = -1; j <= 1; j++)  // 列方向:-1(左)、0(當前)、1(右)
		{
			count += Count_mine(mine, x + i, y + j);  // 累加周圍每個格子的雷數
		}
	}
	// 在玩家界面顯示當前格子周圍的雷數(字符形式,如'0'表示無雷)
	show[x][y] = count + '0';

	// 遞歸結束條件3:如果周圍有雷(count≠0),則停止展開
	if (show[x][y] != '0')
	{
		return;
	}

	// 如果周圍無雷(count=0),遞歸展開周圍8個方向的格子
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1; j <= 1; j++)
		{
			Open_keyboard(show, mine, x + i, y + j);
		}
	}
}

// 排雷核心邏輯
// show:玩家界面數組
// mine:雷區數組
// row、col:實際遊戲區域的行數和列數
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
	int x, y;  // 存儲玩家輸入的排查座標
	while (1)  // 循環處理排雷操作,直到遊戲結束
	{
		printf("請輸入要排查的座標:");
	regain2:  // 跳轉標籤,用於輸入錯誤時重新輸入座標
		scanf("%d %d", &x, &y);  // 讀取玩家輸入的座標(x為行,y為列)

		// 檢查座標是否在有效範圍內(1到row行,1到col列)
		if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))  // 假設row=col=9,可改為x<=row && y<=col
		{
			// 檢查該座標是否未被排查過(仍為'*')
			if (show[x][y] == '*')
			{
				// 如果該位置無雷(mine[x][y]為'0')
				if (mine[x][y] == '0')
				{
					Open_keyboard(show, mine, x, y);  // 展開周圍無雷區域
					printf_keyboard(show, ROW, COL);  // 刷新並顯示玩家界面
				}
				// 如果該位置有雷(mine[x][y]為'1')
				else
				{
					printf("很遺憾,踩到雷了,遊戲結束\n以下是雷的位置:");
					printf_keyboard(mine, ROW, COL);  // 顯示所有雷的位置
					break;  // 退出循環,遊戲結束
				}
			}
			// 該座標已被排查過
			else
			{
				printf("該座標已經排查過了,請輸入別的座標:");
				goto regain2;  // 跳轉到regain2,重新輸入座標
			}
		}
		// 座標輸入無效(超出範圍)
		else
		{
			printf("輸入錯誤,重新輸入:");
			goto regain2;  // 跳轉到regain2,重新輸入座標
		}

		// 判斷玩家是否獲勝(已排查所有非雷格子)
		int Remove_mine_count = 0;  // 記錄已排查的非雷格子數量
		for (int i = 1; i <= row; i++)
		{
			for (int j = 1; j <= col; j++)
			{
				if (show[i][j] != '*')  // 非'*'表示已排查
				{
					Remove_mine_count++;
				}
			}
		}
		// 獲勝條件:已排查的格子數 = 總格子數 - 雷的總數
		if (Remove_mine_count == ((ROW * COL) - MINE))
		{
			printf("恭喜你,排除所有的雷,遊戲勝利\n");
			printf_keyboard(mine, ROW, COL);  // 顯示所有雷的位置
			break;  // 退出循環,遊戲結束
		}
	}
}

game.h

#pragma once  // 防止頭文件被重複包含(只編譯一次)

// 包含所需的標準庫頭文件
#include <stdio.h>    // 提供輸入輸出函數(如printf、scanf)
#include "game.h"     // 包含遊戲相關的其他聲明(注意:此處可能存在循環包含,實際應避免)
#include <time.h>     // 提供時間相關函數(如time,用於初始化隨機數種子)
#include "stdlib.h"   // 提供標準庫函數(如rand、srand,用於生成隨機數)

// 宏定義:遊戲核心參數
#define ROW 9        // 實際遊戲區域的行數(9行)
#define COL 9        // 實際遊戲區域的列數(9列)
#define ROWS ROW+3   // 雷區數組的總行數(比實際行數多3,用於處理邊界,避免越界訪問)
#define COLS COL+2   // 雷區數組的總列數(比實際列數多2,用於處理邊界)
#define MINE 10      // 遊戲中雷的總數(10個)


// 函數聲明:聲明遊戲中用到的所有函數(供其他文件調用)

// 打印遊戲菜單(如開始/退出選項)
void menu();

// 初始化棋盤數組
// 參數:board-要初始化的二維數組,rows-數組行數,cols-數組列數,set-初始化填充的字符
void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

// 打印展示棋盤(玩家界面或雷區)
// 參數:board-要展示的二維數組,row-實際遊戲區域行數,col-實際遊戲區域列數
void printf_keyboard(char board[ROWS][COLS], int row, int col);

// 在雷區數組中隨機佈置雷
// 參數:board-雷區數組,row-實際遊戲區域行數,col-實際遊戲區域列數,mine-要佈置的雷數
void set_mine(char board[ROWS][COLS], int row, int col, int mine);

// 處理玩家的排雷操作(核心遊戲邏輯)
// 參數:show-玩家界面數組,mine-雷區數組,row-實際遊戲區域行數,col-實際遊戲區域列數
void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);