文章目錄
- 一、初級版掃雷
- 1.1 游戲功能
- 1.2 設計思路
- 二、優化版掃雷
- 2.1 優化功能
- 2.2 設計思路
- 三、完整代碼
- GitHub鏈接
- Gitee鏈接
一、初級版掃雷
1.1 游戲功能
初級版只具備最基礎的兩個功能:
1、顯示當前輸入坐標周圍雷的數目
2、排雷錯誤,則游戲結束
3、當排除所有的非雷區域后,取得勝利
1.2 設計思路
我們以9*9的格子(我們稱為棋盤)為例,
首先,我們需要兩個陣列,其中一個用來布置地雷的位置(這個陣列不對玩家顯示),另一個用來顯示排雷的資訊,

假設我們現在要統計坐標(1,1)和(3,4)周圍雷的數目,坐標(3,4)需要統計周圍8個位置是否有雷,坐標(1,1)只用統計周圍三個位置是否有雷,在棋盤中,大多數格子都被8個格子所包圍,而邊界位置旁邊有3或5個格子,如果分別進行判斷的話,需要討論多種情況,因此我們將棋盤擴大為11 *11,有效區域只有中間的9 *9部分,這樣對于每一個位置,都是統計周圍8個格子是否有雷,

運行效果:

在游戲開始前,我們列印一個游戲選單供玩家進行選擇,當玩家選擇了開始游戲后,我們進入游戲模塊,
void menu()
{
printf("**********************************************\n");
printf("********請選擇-> 1:開始游戲 0:結束游戲****\n");
printf("**********************************************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); //初始化種子
do
{
menu(); //列印選單
scanf("%d",&input);
switch (input)
{
case 1:
printf("掃雷游戲開始:\n");
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
break;
}
} while (input);
return 0;
}
當玩家選擇開始游戲后,我們要先創建兩個陣列,并將其初始化,存放地雷的陣列,先將其全部初始化為字符’0’;存放排雷資訊的陣列先全部初始化為字符 ’ * ',
游戲模塊:
void game()
{
char Myboard[ROWS][COLS] = { 0 }; //用來存放地雷
char ShowBoard[ROWS][COLS] = { 0 }; //用來顯示排雷的資訊
InitBoard(Myboard, ROWS, COLS, '0'); //將地雷棋盤全部初始化為0
InitBoard(ShowBoard, ROWS, COLS, '*'); //將顯示棋盤全部初始化為*
SetBoard(Myboard, ROW, COL); //布雷
PrintBoard(ShowBoard, ROWS, COLS); //列印棋盤
FindBoard(Myboard,ShowBoard, ROWS, COLS); //排雷
}
初始化模塊:
void InitBoard(char board[ROWS][COLS], int row, int col, char c) //初始化棋盤
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
board[i][j] = c;
}
}
然后我們使用srand和rand函式隨機布置地雷的位置(srand函式要放在主函式的回圈體外)因為棋盤的有效區域只有中間的9 *9部分,因此我們要確保rand產生的亂數在1-9這個范圍內,
void SetBoard(char board[ROWS][COLS], int row, int col) //布雷
{
for (int cnt = 0; cnt < COUNT;)
{
int x = rand() % row + 1; //隨機產生橫縱坐標
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
cnt++;
}
}
}
當布置好雷之后,我們列印顯示棋盤,在列印的同時列印上橫縱序號,方便玩家確定坐標
void PrintBoard(char board[ROWS][COLS], int row, int col) //列印棋盤
{
printf(" ");
for (int i = 1; i < row - 1; i++) //列印列坐標
printf("%d ", i);
printf("\n");
for (int i = 1; i < row - 1; i++)
{
printf("%d ", i); //列印橫坐標
for (int j = 1; j < col - 1; j++)
printf("%c ", board[i][j]);
printf("\n");
}
}
最后是游戲的核心部分:排雷
當玩家輸入的坐標不是雷,統計周圍8個位置雷的數目,可以用8個if陳述句或者回圈陳述句來統計,此處我將每個位置的字符相加,最后減去8個’0’,也是雷的數目,
int Num(char board[ROWS][COLS], int x, int y) //統計當前位置周圍有幾個雷
{
return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +
board[x][y - 1] + board[x][y + 1] +
board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';
//如果是雷,則對應的坐標位置是字符 '1',否則是字符'0',將周圍八個位置的字符全部相加最后減去8個字符'0',就是周圍雷的數目
//'1' - '0' = 1(數字1) '0' - '0' = 0(數字0)
}
void FindBoard(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col) //排雷
{
int x = 0;
int y = 0;
int cnt = ROW * COL - COUNT; //cnt表示非雷的數目,即棋盤大小減去地雷數
while (1)
{
printf("請輸入坐標\n");
scanf("%d%d", &x, &y);
if (x < 1 || x >row || y < 1 || y > col)
{
printf("坐標非法,請重新輸入\n");
}
else
{
if (board[x][y] == '1') //當前坐標位置是雷,游戲結束
{
printf("你失敗了,游戲結束\n");
PrintBoard(board, row, col);
break;
}
else if (board[x][y] == '0') //當前坐標不是雷,統計周圍雷的數目
{
if (0 == Num(board, x, y)) //如果周圍沒有雷,則當其置為空
{
ShowBoard[x][y] = ' ';
}
else ShowBoard[x][y] = Num(board, x, y) + '0'; //周圍有雷,將其置為雷的個數
//(因為我們列印的是字符型變數,因此要加上'0'才是所對應的數字字符)
cnt--; //每排完一個雷,非雷的數目減1
PrintBoard(ShowBoard, row, col); //列印當前顯示棋盤的資訊
}
}
if (cnt == 0)
{
printf("恭喜你,成功排雷\n");
PrintBoard(board, row, col);
break;
}
}
}
我們將代碼分別封裝在main.c和game.c兩個檔案中,并在game.h頭檔案中對函式進行宣告
main.c
#include"game.h"
void menu()
{
printf("**********************************************\n");
printf("********請選擇-> 1:開始游戲 0:結束游戲****\n");
printf("**********************************************\n");
}
void game()
{
char Myboard[ROWS][COLS] = { 0 }; //用來存放地雷
char ShowBoard[ROWS][COLS] = { 0 }; //用來顯示排雷的資訊
InitBoard(Myboard, ROWS, COLS, '0'); //將地雷棋盤全部初始化為0
InitBoard(ShowBoard, ROWS, COLS, '*'); //將顯示棋盤全部初始化為*
SetBoard(Myboard, ROW, COL); //布雷
PrintBoard(ShowBoard, ROWS, COLS);
FindBoard(Myboard,ShowBoard, ROWS, COLS); //排雷
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); //初始化種子
do
{
menu(); //列印選單
scanf("%d",&input);
switch (input)
{
case 1:
printf("掃雷游戲開始:\n");
game();
break;
case 0:
printf("退出游戲\n");
break;
default:
printf("輸入錯誤,請重新輸入\n");
break;
}
} while (input);
return 0;
}
game.c
void InitBoard(char board[ROWS][COLS], int row, int col, char c) //初始化棋盤
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
board[i][j] = c;
}
}
void PrintBoard(char board[ROWS][COLS], int row, int col) //列印棋盤
{
printf(" ");
for (int i = 1; i < row - 1; i++) //列印列坐標
printf("%d ", i);
printf("\n");
for (int i = 1; i < row - 1; i++)
{
printf("%d ", i); //列印橫坐標
for (int j = 1; j < col - 1; j++)
printf("%c ", board[i][j]);
printf("\n");
}
}
void SetBoard(char board[ROWS][COLS], int row, int col) //布雷
{
for (int cnt = 0; cnt < COUNT;)
{
int x = rand() % row + 1; //隨機產生橫縱坐標
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
cnt++;
}
}
}
int Num(char board[ROWS][COLS],int x,int y) //統計當前位置周圍有幾個雷
{
return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +
board[x][y - 1] + board[x][y + 1] +
board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';
}
void FindBoard(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col) //排雷
{
int x = 0;
int y = 0;
int cnt = ROW * COL - COUNT; //cnt表示非雷的數目,即棋盤大小減去地雷數
while (1)
{
printf("請輸入坐標\n");
scanf("%d%d", &x, &y);
if (x < 1 || x >row || y < 1 || y > col)
{
printf("坐標非法,請重新輸入\n");
}
else
{
if (board[x][y] == '1') //當前坐標位置是雷,游戲結束
{
printf("你失敗了,游戲結束\n");
PrintBoard(board, row, col);
break;
}
else if (board[x][y] == '0') //當前坐標不是雷,統計周圍雷的數目
{
if (0 == Num(board, x, y)) //如果周圍沒有雷,則當其置為空
{
ShowBoard[x][y] = ' ';
}
else ShowBoard[x][y] = Num(board, x, y) + '0'; //周圍有雷,將其置為雷的個數
//(因為我們列印的是字符型變數,因此要加上'0'才是所對應的數字字符)
cnt--; //每排完一個雷,非雷的數目減1
PrintBoard(ShowBoard, row, col); //列印當前顯示棋盤的資訊
}
}
if (cnt == 0) //所有非雷位置均排查完
{
printf("恭喜你,成功排雷\n");
PrintBoard(board, row, col);
break;
}
}
}
二、優化版掃雷
2.1 優化功能
在初級版掃雷中,我們必須一個一個地將所有非雷位置全部排查完才能完成游戲的勝利,提示的資訊特別少,并且如果運氣不好,第一次就可能排到雷的位置,在優化版掃雷中,增加了自動展開、防止第一次就排到雷的情況同時增加了標記功能,
2.2 設計思路
1、展開功能
當輸入一個坐標,如果它的周圍沒有雷,則將其周圍不是雷的區域展開,如果它周圍的格子周圍也沒有雷,則繼續展開,

同時為了防止陣列越界,在統計雷的數目函式里增加一個if陳述句來判斷,
int Num(char board[ROWS][COLS],int x,int y) //統計當前位置周圍有幾個雷
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //防止陣列越界
return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] +
board[x][y - 1] + board[x][y + 1] +
board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] - 8 * '0';
}
int Blank(char board[ROWS][COLS], char ShowBoard[ROWS][COLS], int x, int y) //判斷當前位置周圍是不是沒有雷
{
int count = 0; //展開的格子數
for (int i = x - 1; i <= x + 1 && i >=0 && i <= ROW; i++)
{
for (int j = y - 1; j <= y + 1 && j >=0 && j <=COL; j++)
{
if (i == x && j == y) //跳過坐標為(x,y)的位置
;
else if (ShowBoard[i][j] == '*' && board[i][j] != '1') //如果坐標(i,j)處沒有被初始化并且不為雷,判斷其周圍有沒有雷
{ //如果周圍也沒有雷,將其展開
int cnt = Num(board, i, j);
if (cnt == 0)
{
ShowBoard[i][j] = ' ';
count += Blank(board, ShowBoard, i, j); //繼續判斷這個坐標周圍的位置是否需要展開
}
else ShowBoard[i][j] = cnt + '0';
if (i >= 1 && i <= ROW && j >= 1 && j <= COL) //當展開的格子在有效棋盤范圍內時,count++
count++;
}
}
}
return count; //總共展開了多少個格子
}
2、防止第一次就排到雷
如果運氣不好,第一次就排到雷,則重置這個雷的位置,
3、在排雷程序中,讓玩家選擇排雷和標記,當選擇標記,將輸入的坐標置為’#’
void FindBoard(char board[ROWS][COLS],char ShowBoard[ROWS][COLS],int row, int col) //排雷
{
int cnt = ROW * COL - COUNT;
int time = 1; //time為1時表示是第一步
int over = 0;
//PrintBoard(board, row, col);
while (1)
{
printf("請選擇-> \n1、排雷 2、標記\n");
int choose = 0;
scanf("%d", &choose);
switch (choose)
{
case 1:
while (1) //排雷回圈
{
printf("請輸入排雷的坐標\n");
int x = 0;
int y = 0;
scanf("%d%d", &x, &y);
if (x < 1 || x >row || y < 1 || y > col)
{
printf("坐標非法,請重新輸入\n");
}
else
{
if (board[x][y] == '1')
{
if (time) //如果第一次就遇見雷,則重置這個雷的位置
{
board[x][y] = '0';
while (1) //重置第一次雷的位置
{
int x1 = rand() % row + 1;
int y1 = rand() % col + 1;
if (board[x1][y1] == '0')
{
board[x1][y1] = '1';
break; //跳出重置第一次雷的回圈
}
}
time = 0; //time置為0表示不是第一次
if (0 == Num(board, x, y))
{
ShowBoard[x][y] = ' ';
int count = Blank(board, ShowBoard, x, y); //如果重置之后,周圍沒有雷,展開這個坐標
cnt -= count;
}
else ShowBoard[x][y] = Num(board, x, y) + '0';
cnt--;
PrintBoard(ShowBoard, row, col);
goto end; //第一次排到雷,判斷是否排完
}
else //不是第一次
{
over = 1;
break; //跳出排雷回圈
}
}
else if (board[x][y] == '0')
{
time = 0;
if (0 == Num(board, x, y))
{
ShowBoard[x][y] = ' ';
int count = Blank(board, ShowBoard, x, y); //如果周圍沒有雷,進行展開
cnt -= count;
}
else ShowBoard[x][y] = Num(board, x, y) + '0';
cnt--;
PrintBoard(ShowBoard, row, col);
break; //跳出排雷回圈
}
}
}
break; //跳出switch
case 2:
printf("請輸入標記的坐標\n");
while (1) //標記回圈
{
int x1 = 0;
int y1 = 0;
scanf("%d%d", &x1, &y1);
if (x1< 1 || x1>row || y1<1 || y1>col)
printf("坐標越界,請重新輸入\n");
else if (ShowBoard[x1][y1] != '*')
printf("此處坐標已排查過,請重新輸入\n");
else if (ShowBoard[x1][y1] == '*')
{
ShowBoard[x1][y1] = '#';
PrintBoard(ShowBoard, row, col);
break; //跳出標記回圈
}
}
break; //跳出switch
default:
printf("輸入錯誤,請重新輸入\n");
break; //跳出switch
}
end :
if (over)
{
printf("你失敗了,游戲結束\n");
PrintBoard(board, row, col);
break; //游戲結束
}
if (cnt == 0)
{
printf("恭喜你,成功排雷\n");
PrintBoard(board, row, col);
break; //游戲結束
}
}
}
運行結果:雷數為10

雷數為1:

雷數為2:

雷數為79(這里我們列印出雷的布局,方便我們測驗):

雷數為80(隨便輸入一個坐標都會取得勝利):

三、完整代碼
GitHub鏈接
掃雷初級版
掃雷優化版
Gitee鏈接
掃雷初級版
掃雷優化版
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/282394.html
標籤:其他
