掃雷遊戲的功能説明 :
• 使⽤控制枱實現經典的掃雷遊戲
• 遊戲可以通過菜單實現繼續玩或者退出遊戲
• 掃雷的棋盤是9*9的格⼦
• 默認隨機佈置10個雷
• 可以排查雷:
◦ 如果位置不是雷,就顯⽰周圍有⼏個雷
◦ 如果位置是雷,就炸死遊戲結束
◦ 把除10個雷之外的所有⾮雷都找出來,排雷成功,遊戲結束
test.c //⽂件中寫遊戲的測試邏輯
game.c //⽂件中寫遊戲中函數的實現等
game.h //⽂件中寫遊戲需要的數據類型和函數聲明等
邏輯開始:
一、菜單
- 輸入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 的遊戲界面
- 使用二維數組實現
- 運用兩個棋盤,一個用於展示,一個用於設置雷,寫出初始化棋盤的函數
- 將展示的棋盤
char show全部初始化為'*',將佈置雷的棋盤char mine全部初始化為0 - 為方便後邊測試,可以先把打印棋盤的函數寫出
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);
三、隨機佈置雷
- 使用
srand((unsigned int) time(NULL))和rand() - 將雷設置為
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);
四、排雷
- 注意輸入的座標,橫縱座標都只能是
0~9,出現其他數字報錯,並重新輸入 - 所排的座標要顯示周圍雷的個數,如果為
0,展開周圍的棋盤(運用到遞歸) - 如果所排的座標是雷,顯示
遊戲結束 - 如果輸入的座標是已經輸入過的座標,顯示
該座標已經排除 - 判斷遊戲勝利,
排除的座標個數與減掉雷後的格子數相等
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);