宣告:跟隨鵬哥視頻學習,內容有相關性,侵刪,

何為掃雷游戲?這里給一個簡單的概述,比如一個9x9的方格表(如圖),

初始化之后, 每個‘*’后面或者有雷,或者沒雷,雷的個數可以根據要求確定,然后你在鍵盤上輸入一個坐標,如果這個坐標上有雷,你就被炸死,游戲結束,如果沒有雷,就會顯示這個坐標周圍8個坐標上有多少個雷,如圖,表示(2,2)坐標上沒有雷,而且它周圍有8個坐標上一共有1個雷,

但當輸入的坐標上不是雷而且周圍也沒有雷的時候,它就會依次判斷周圍8個坐標的周圍有多少個雷,如果8個坐標中的一個周圍還是沒有雷,繼續依次判斷該坐標周圍坐標的周圍有多少個雷,直到坐標周圍有雷為止(如圖,輸入坐標分別為(6,3),(2,9)),


游戲的目標就是找出所有不是雷的坐標,
如何實作這個小游戲?
為了簡明高效,代碼大致可以分為三部分:
1.頭檔案及函式宣告區(game1.h),
2.主函式實作區(main1.c),
3.呼叫函式實作區(game1.c),
我的習慣是先做好可能用到的頭檔案宣告(宣告了沒用到也沒事)和參考頭檔案,如圖:



在專案總頭檔案處參考常用頭檔案,然后在專案的其它檔案處參考專案頭檔案即可,可以避免大量重復的參考,并且提高簡明度,
思考一個問題,接下來該做什么?
想像一下,你打開一個游戲,第一眼會看到什么?
界面, 游戲界面,所以第一步我們應該初始化一個簡易的游戲界面,但是,問題又來了,界面初始化一次夠了沒有?顯然不是,因為我們可能不僅僅玩一局,
進入界面,用戶可以選擇開始,也可以選擇退出,當然也會出現選擇錯誤,所以這是一個多分支的程式,而且還應該可以回圈,
所以我們應該選用switch-case與do-while嵌套,如圖,

該設計巧妙在選擇0是退出,也是終止回圈的條件,
接下來是game函式的實作,
怎么做?做什么?游戲界面像什么?一個棋盤,所以我們應該先初始化一個二維陣列來表達一個棋盤,
然后呢?
怎么表示雷和非雷?假設我們用0表示非雷,用1表示有雷,在這個二維陣列每個位置上存盤0和1,那么問題來了,如果一個沒有雷的坐標周圍恰好有一個雷,我們也應該在這個坐標上放一個1,那么怎么區分這個1到底是雷還是之后放上去的?
有一個辦法,創建兩個二維陣列,一個放雷專用,一個找雷專用,
如果我們需要一個9x9的雷盤,創建一個多少行多少列的陣列?
考慮極端處的效果,如果是第一行或者第一列的坐標,周圍沒有滿8個坐標,難道后面遍歷周圍8坐標的時候要對他們特殊處理?或許我們可以讓我們能選擇的每一個坐標周圍都有8個坐標,
怎么做?9x9的基礎上往外伸展一行一列,就是11X11,
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
char mine[ROWS][COLS];
char show[ROWS][COLS];
chushihua(mine,ROW,COL,'0');
chushihua(show,ROW,COL,'*');
void chushihua(char board[ROWS][COLS],int row,int col,char set)
{
int i,j;
for(i=0;i<ROWS;i++)
{
for(j=0;j<COLS;j++)
{
board[i][j]=set;
}
}
}
這里采用宏定義行列數,方便以后一鍵修改,mine為埋雷字符陣列,初始化為字符‘0’;show為展示陣列,初始化為‘*’,為什么是字符陣列,因為放的是字符,
初始化之后應該將show展示給玩家,為了使game函式簡潔,所有功能都通過呼叫函式實作,
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
chushihua(mine,ROW,COL,'0');
chushihua(show,ROW,COL,'*');
Dispaly(show,ROW,COL);//列印show棋盤
}
void Dispaly(char board[ROWS][COLS],int row,int col)
{
int i,j;
for(i=0;i<ROWS-1;i++)//先打一行0到9
{
printf("%d ",i);
}
printf("\n");//換行
for(i=1;i<ROWS-1;i++)
{
printf("%d ",i);//每行開頭應該先打行號
for(j=1;j<COLS-1;j++)
{
printf("%c ",board[i][j]);
}
printf("\n");//打完一行后換行
}
}
效果應如圖:

下一步應該埋雷,通過函式setmine(mine,ROW,COL)實作,
#include <stdlib.h>
#include <time.h>
void setmine(char mine[ROWS][COLS],int row,int col)
{
int count=LEI;//LEI為宏定義的埋雷個數
do
{
int x=rand()%row+1;//產生亂數1~9,因為能埋雷的坐標在二維陣列中剛好位于1~9行列之中
int y=rand()%col+1;//若col為9,任何數取余9都在(0,8)之間,+1變成(0,9)
if(mine[x][y]=='0')//沒有埋過雷的位置才能埋,不然會重復,達不到預期效果
{
mine[x][y]='1';
count--;
}
}while(count);//埋完雷后count為0,退出回圈
}
埋好雷之后就開始zhaolei函式,首先用戶應該可以不斷地輸入坐標,所以應該用回圈,
然后思考一個問題,先判斷一個坐標的周圍有沒有雷,如果沒有,繼續判斷周圍8個坐標的周圍有沒有雷,直到那個坐標周圍有雷為止,一個函式被反復呼叫,而且函式里面還要呼叫一樣的函式,這是什么?遞回,
void zhaolei(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
{
int x,y;
do
{
printf("請輸入坐標:\n");
scanf("%d%d",&x,&y);
if(x>=1&&x<=row&&y>=1&&y<=col)//判斷坐標是否在范圍內
{
if(mine[x][y]=='1')//說明有雷
{
printf("很遺憾,你被炸死了\n");
Dispaly(mine,ROW,COL);//炸死后應將雷棋盤展示給玩家
break;
}
else
{
A(mine,show,x,y);//A函式實作一連串的遍歷點作業
Dispaly(show,ROW,COL);
}
}
else
{
printf("坐標非法,請重新輸入:");
}
}while();
怎么實作遞回?怎么判斷輸贏?
如果一個show上的坐標已經被賦予數字(字符),即已經遍歷過了其周圍8個坐標,那么在后面的遞回中不應再次被遍歷,否則將進入無窮回圈即死回圈,通俗的說,假設(4,4)(4,5)兩個坐標周圍8個坐標都沒有雷,那么將會遍歷它們周圍的坐標,由(4,4)進入(4,5),再由(4,5)進入(4,4),回圈往復,無法停止,所以我們應該先判斷,
然后while的條件是什么,即怎么判斷贏了?
贏的條件是找出所有沒有雷的點,所以我們應該每次遍歷一個坐標周圍給它賦值后記錄下來,然而遍歷是在函式內做的,條件是while的,我們應該讓函式可以修改主函式里面的資料,
怎么做?指標,
void zhaolei(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
{
int x,y,win=0;
int *p=&win;//定義一個指標傳給A函式,使函式A可以修改win的值
do
{
printf("請輸入坐標:\n");
scanf("%d%d",&x,&y);
if(x>=1&&x<=row&&y>=1&&y<=col)
{
if(mine[x][y]=='1')
{
printf("很遺憾,你被炸死了\n");
Dispaly(mine,ROW,COL);
break;
}
else
{
A(mine,show,x,y,p);
Dispaly(show,ROW,COL);
}
}
else
{
printf("坐標非法,請重新輸入:");
}
}while(win<ROW*COL-LEI);//找完所有點后,win不再滿足條件,跳出回圈
if(win==ROW*COL-LEI)//因為炸死也會跳出回圈,所以應該判斷win的大小
{
printf("恭喜你,游戲勝利!\n");
}
}
接下來是遞回函式A的實作,
int getmine(char mine[ROWS][COLS],int x,int y)//遍歷一個坐標周圍8個坐標
{
return mine[x+1][y]+mine[x+1][y-1]+mine[x+1][y+1]+mine[x][y+1]+mine[x][y-1]+mine[x-1][y]+mine[x-1][y-1]+mine[x-1][y+1]-8*'0';//因為mine上面放的是字符,所以應減去變成整型
}
void A(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y,int *p)
{
int count=getmine(mine,x,y);
show[x][y]=count+'0';//count為整型,加上‘0’,變為對應的字符
(*p)++;//指標一定要先括起來,然后在加加,應為+運算級高于*
if(count==0)
{
int i,j;
for(i=-1;i<=1;i++)//兩個for完成遍歷
{
for(j=-1;j<=1;j++)
{
if(i!=0||j!=0)//本身不用再次遍歷
{
if(show[x+i][y+j]=='*')//遍歷過的也不用再次遍歷
{
if((x+i)!=0&&(y+j)!=0&&(x+i)!=(ROWS-1)&&(y+j)!=(COLS-1))
{//該處條件是極限處,即最外圍的一圈,就是9x9之外的一圈,不用遍歷,否則會造成判斷區域斷裂
A(mine,show,x+i,y+j,p);//遞回!
}
}
}
}
}
}
}
有一個比較難懂的地方,就是-8*‘0’那里,其實很簡單,字符數字與整型數字在ASCII中成順序排列,數字0就是0,而‘0’是48,所以字符減去48即變為相應的數字,
比如:
printf("%d",'3'-48);
結果是數字三
掃雷就完成啦!趕緊玩一把吧!
代碼鏈接:https://pan.baidu.com/s/1_lasz7Fy_K0UQ_XUNIVynQ?pwd=3cn6
提取碼:3cn6
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/351117.html
標籤:其他
下一篇:<2021SC@SDUSC>開源游戲引擎Overload代碼分析五:OvEditor——RawShaders.cpp
