1.設計思路
掃雷游戲要著重考慮以下幾點:雷盤的初始化、埋雷、掃雷、輸入一個坐標,旁邊沒有雷的地方全部變為空地,
我們設計兩個二維陣列,一個展示給玩家,一個則是程式員自己看到的真實埋雷情況,
而程式員自己的雷盤,以字符0和1區分,0表示此時沒有雷,1表示次數為雷,
展示給玩家的雷盤,開始時全部都是以‘ * ’星號來展示,當玩家輸入其中一個星號坐標時,若此時其周圍有雷,則使用程式員的雷盤統計其周圍雷的個數,若沒有雷,則將其置為空,并且遞回擴展空地,
為了便于控制游戲難度,我們使用宏來定義陣列大小,掃雷時,為了統計一個區域周圍雷的個數,我們需要遍歷其周圍8個區域,但是對于處于四個邊的區域,我們其周圍區域不足8個,比如玩家要玩一個9×9的雷盤,我們可以設計實際雷盤大小為11×11,列印時列印中間的9×9的部分,而在計算四邊區域雷的個數時,在11×11的陣列中,其四個邊的雷均置為0,表示沒有雷,這樣在遍歷時,9×9區域的四個邊也可以按照遍歷周圍八個區域的方式來進行遍歷,
因此,game.h檔案主要包含以下內容
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define EASY_COUNT 10//盤上的雷的個數
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//游戲
void game();
//列印選單
void menu();
//初始化陣列
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
//列印陣列
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetBoard(char board[ROWS][COLS], int row, int col,int count);//count為布置的雷的個數
//掃雷函式
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);
//統計一個區域周圍雷的個數
int GetMineCount(char board[ROWS][COLS], int x, int y);
//遞回實作掃雷的擴展
void Extend(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y);
//檢驗此時剩余的未檢驗區域的個數
int CheckWin(char show[ROWS][COLS], int row, int col, int count);
2.game()函式
void game()
{
//創建對應的陣列
char mine[ROWS][COLS];//存放何處埋雷,何處不是雷
char show[ROWS][COLS];//給用戶展示的雷盤
InitBoard(mine, ROWS, COLS,'0');//初始化時,所有的區域都不埋雷,不埋雷表示0,埋雷表示1
InitBoard(show, ROWS, COLS, '*');//用戶最開始看到的雷盤均為 *,隨著掃雷程序,* 減少
//列印雷盤
DisplayBoard(show, ROW, COL);//只列印中間的部分
//埋雷
SetBoard(mine, ROW, COL, EASY_COUNT);
//排雷
FindMine(mine, show, ROW, COL,EASY_COUNT);
}
3.二維陣列的初始化
由于有兩個二維陣列需要初始化,因此我們不必用兩個函式來進行初始化,而是使用一個函式,
void InitBoard(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;
}
}
}
為了便于對不同陣列的初始化,傳參時設定一個 char set引數,表示傳進去的字符,因此們對于兩個陣列的初始化分別如下:
InitBoard(mine, ROWS, COLS,'0');//初始化時,所有的區域都不埋雷,不埋雷表示0,埋雷表示1
InitBoard(show, ROWS, COLS, '*');//用戶最開始看到的雷盤均為 *,隨著掃雷程序,* 減少
4.雷盤的列印
我們希望對于雷盤的列印效果如下圖所示:

為了便于掃雷時玩家能夠很好區分橫縱坐標,我們需要標注好行號與列號,其代碼如下
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("----------------掃雷游戲----------------\n");
printf("\n");
printf(" ");
for (int i = 1; i <= row; i++)
{
printf(" %d ", i);
}
printf("\n\n");
for (int i = 1; i <= row; i++)
{
printf(" %d ", i);
for (int j = 1; j <= col; j++)
{
printf(" %c ", board[i][j]);
if (j <= col - 1)
{
printf("|"); //最后一行不列印 |
}
}
printf("\n");
printf(" ");
for (int j = 1; j <= col; j++)
{
if (i <= col - 1)
{
printf("---"); //最下面一行不列印---
}
if (i <= col - 1 && j <= col - 1)
{
printf("|"); //最后一行和最后一列不列印 |
}
}
printf("\n");
}
}
5.埋雷
埋雷需要每一盤隨機進行,因此我們將橫縱坐標都置為亂數,為了防止開始時每一盤的雷一樣,我們需要在進入main函式前加入如下一句代碼
srand((unsigned int)time(NULL));
從而保證埋雷是隨機的,
該模塊代碼如下:
void SetBoard(char board[ROWS][COLS], int row, int col, int count)
{
while (count) //當count等于0時跳出回圈,表示此時埋雷完畢
{
int x = rand() % row +1;
int y = rand() % col +1;
if (board[x][y] == '0')//若此處未埋雷,則布置,否則重新布置
{
board[x][y] = '1';
count--; //每埋一個雷,count--
}
}
}
**
6.統計某個區域周圍的個數
**
遍歷周邊八個區域,代碼如下:
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (board[i][j] == '1')
count++;
}
}
return count;
}
7.空地的擴展
空地的擴展為游戲設計的難點,但當我們了解游戲規則后,不難想出用遞回來實作,
當輸入坐標不是雷,且其周圍八個區域均不是雷的時候,需要向此地置為空格,并向外擴展,此時需要檢查其周圍的區域有沒有被訪問國,若訪問過則跳過,若沒有訪問則進行訪問,其代碼如下:
//遞回實作掃雷的擴展
void Extend(char show[ROWS][COLS], char mine[ROWS][COLS],int x, int y)
{
if (!GetMineCount(mine, x, y))//如果周圍的個數為0,則對其周圍8個區域進行檢查
{
show[x][y] = ' '; //將周圍雷的個數為0的區域置為空
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
//兩個for回圈對該區域周圍8個區域進行檢查
if (mine[i][j] == '0' && show[i][j]=='*' && (i >= 1 && i <= ROW) && (j >= 1 && j <= COL))//如果沒有被遍歷,且此時檢查的區域不是雷,則進入遞回
{
Extend(show, mine, i, j);
}
}
}
}
else
{
show[x][y] = GetMineCount(mine, x, y) + '0';//若周圍雷的個數不為0,則標出該區域周圍雷的個數
}
}
8.排雷
排雷是本游戲設計的核心,
如果所輸入坐標剛好是雷,則告知玩家被炸死,跳出游戲,并向玩家展示埋雷的真實情況,
如果不是雷,則展示該區域周圍的雷的個數,當該區域周圍的雷的個數為0時,則置為空格,并進行空地的擴展,知道周圍不是0為止,
當剩余的‘*’星號剛好等于埋雷的個數時,證明玩家已經排雷成功,向玩家告知并跳出游戲
其代碼如下
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int count)
{
int x = 0;
int y = 0;
int win = row*col;//win為此時未檢查的區域的個數,初始時為row * col
while (win > count) //若未檢查的個數大于雷的個數,則進入回圈
{
printf("請輸入要排查的坐標:>");
scanf("%d%d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))//檢查坐標合法性,若合法,則進入排查
{
if (mine[x][y] == '1')
{
//是雷
printf("很遺憾,你被炸死了!\n");
DisplayBoard(mine, row, col);
break;
}
else if(mine[x][y] == '0')
{
//如果此時不是雷,則遞回計算
Extend(show, mine, x, y);
DisplayBoard(show, row, col);
win = CheckWin(show, row, col, count);//計算未檢查個數,此數決定了是否進入回圈
}
}
else
{
printf("坐標非法,請重新輸入"); //若坐標不合法,則重新輸入
}
}
if (win == count) //雷的個數等于未排查區域的個數,代表已經排查完畢,未排查區域即為雷
{
printf("恭喜你!排雷成功!\n");
}
}
9.test()函式
最后的完善就是給游戲設計一個簡單的選單,玩家能根據需求來進行進入游戲或者退出游戲,此部分較為簡單,選單設計如代碼如下:
void menu()
{
printf("----------------------------------------\n");
printf("-----------------1.play-----------------\n");
printf("-----------------2.exit-----------------\n");
printf("----------------------------------------\n");
}
test()函式代碼如下:
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戲\n");
break;
case 1:
game();//掃雷游戲的實作
break;
default:
printf("選擇錯誤,請重新選擇!\n");
break;
}
} while (input);
return 0;
}
10.小結
至此,掃雷游戲設計完成,此設計依然可以進行進一步改進,如第一次所點區域不能是雷,感興趣的可以自行嘗試,
掃雷游戲是對二維陣列的一次應用,需要掌握對二維陣列的遍歷,同時,其中空地的擴展,也涉及到了遞回條件的判斷,需要掌握遞回的方法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/253982.html
標籤:其他
上一篇:演算法競賽入門——動態規劃
下一篇:pgzero/pygame zero新手教程,pgzero/pygame zero教程,如何使用pgzero/pygame zero
