1. 程式人生 > >掃雷遊戲的實現C++

掃雷遊戲的實現C++

掃雷遊戲是Windows作業系統自帶的一個小遊戲,幾乎每個電腦使用者都接觸過它。它同時也是一款比較經典的小遊戲,實現它的方法很多,也可以用不同演算法和語言實現。近期用了兩個週末(各一天)和大半個月的空餘時間終於實現了一個比較完整的掃雷程式。現通過C++來呈現這款小遊戲的實現方法。

關於掃雷:

遊戲的規則:在不掀開任何藏有地雷的方塊情況下,以最快的速度找出所有的地雷。如果在掀開方塊的過程中,不小心翻開(踩到)藏有地雷的方塊,則宣告失敗(遊戲結束),遊戲進行的過程中沒有踩到地雷且找到所有的地雷,遊戲才算成功。

左鍵表示翻開方塊,是雷則結束,非雷則顯示數字;

右鍵表示標示或疑似地雷(不管是否真的是雷,奇數次按下表示表示此區域有雷,偶數次按下表示疑問

);當反覆按下右鍵,則方塊會以標示→疑問不斷迴圈。

操作者可以通過地雷區內的數字提示瞭解以數字為中心的周邊八個方格內所含的地雷數,如果翻開的方塊顯示數字“3”,則表示以其為中心的周邊方塊內藏有3個地雷。當按下的方塊不是地雷,且周邊八個方塊也都沒有地雷時,方塊會以被翻開方塊的八個方向將空白方塊翻開。成功地找出全部的地雷,遊戲結束。

說明:由於程式的程式碼有1K行左右,這裡只介紹實現的設計思路和要點,儘量不去貼程式碼。

設計實現:

(1)掃雷介面

新建一個MFC單文件應用程式。工程名MineSweepingView ,初始狀態下可以直接執行程式。初始介面如圖1-1-1所示:

圖1-1-1MFC SDI初始化介面示意圖

1.1 刪除工具欄  介面含有選單和工具欄,這些對於掃雷遊戲介面是多餘的選項,需要刪去和修改。具體方法:在MainFrm.cpp中,找到函式OnCreate,處理如下:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
	{
		return -1;
	}	
	//if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
	//	| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
	//	!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	//{
	//	TRACE0("未能建立工具欄\n");
	//	return -1;      // 未能建立
	//}
	//if (!m_wndStatusBar.Create(this) ||
	//	!m_wndStatusBar.SetIndicators(indicators,
	//	  sizeof(indicators)/sizeof(UINT)))
	//{
	//	TRACE0("未能建立狀態列\n");
	//	return -1;      // 未能建立
	//}
	//// TODO: 如果不需要可停靠工具欄,則刪除這三行
	//m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	//EnableDocking(CBRS_ALIGN_ANY);
	//DockControlBar(&m_wndToolBar);	
	
	return 0;
}

1.2 修改選單欄 修改結果是僅保留“幫助”,其他的都刪除,新增“遊戲(G)”以及子選單“開始”,並且“遊戲”置於幫助之前。首先,開啟 資源檢視——Menu —— IDR_MAINFRAME 在選單欄的空白處,新增“遊戲(G)”,子選單新增“開始”,在屬性欄中編輯“開始”對應的ID:IDM_GAME_START。然後,在MineSweeping.rc 中進行下面的操作:

IDR_MAINFRAMEMENU
BEGIN
   /* POPUP "檔案t(&F)"
BEGIN
。。。。。。
   中間的內容全部註釋
END*/
//將遊戲置於幫助之前,顯示時遊戲將顯示在左側
       POPUP"遊戲(G)"
   BEGIN
        MENUITEM "開始",                          IDM_GAME_START
   END
   POPUP "幫助(&H)"
   BEGIN
        MENUITEM "關於MineSweeping(&A)...",      ID_APP_ABOUT
   END  
END

說明:這裡的“遊戲”和“幫助”對應介面中選單項的第一項和第二項,有順序。寫在前面的置於左側,寫在後面的置於右側。在BEGINEND之間的是POPUP對應選單項的子選單項,這裡同樣有順序要求,這裡的順序(上下)和介面中所呈現的順序是一致。遊戲選項下的子選單也是一樣的,就不在重述。

1.3 介面sizeMianFrm.cpp中找到函式

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
       if(!CFrameWndEx::PreCreateWindow(cs) )
       {
              returnFALSE;
       }    
       //  CREATESTRUCT cs 來修改視窗類或樣式
       //  the CREATESTRUCT cs 
   //新增如下程式碼 改變視窗的樣式    
       cs.style=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;
       //設定視窗大小:480*360
       cs.cx=480;
       cs.cy=360;
      cs.lpszName=_T("掃雷");    //新增介面標題
       return TRUE;
}

說明:上述修改設定視窗的大小方法僅在VS2008以及更低的VS版本設定有效,在VS2010中設定無效。在VS2010中,改變視窗的大小在 CxxxApp中的InitInstance中新增程式碼:

BOOL CSaoLei2App::InitInstance()
{
	/*
           中間的內容全部註釋掉
               。。。。。。
	*/
	//shufac 新增程式碼改變掃雷介面視窗大小VS2010 MFC窗體的大小設定
	m_pMainWnd->SetWindowPos(NULL,0,0,480,360,SWP_NOMOVE); 
	// 唯一的一個視窗已初始化,因此顯示它並對其進行更新
	m_pMainWnd->ShowWindow(SW_SHOW);
	m_pMainWnd->UpdateWindow();
	return TRUE;
} 

至此,掃雷介面顯示為:選單項只有遊戲和幫助的選單項如圖1-3-1所示

圖1-3-1 掃雷視窗介面

1.4 視窗的背景 視窗的繪製背景操作在View類的OnDraw函式中進行,下面這四行程式碼控制整個介面的背景,RGB的三個引數從配置檔案中獲取。

CBrush mybrush; 
mybrush.CreateSolidBrush(RGB(Param1,Param2,Param3));           
CRect myrect(0,0,1200,800); //新增背景的區域
pDC->FillRect(myrect,&mybrush);

1.5 繪製雷區以及邊線  雷區格子的繪製同樣是在View類的OnDraw函式中進行,雷區的左上角頂點左邊(10,50),繪製新增的的程式碼如下:

//畫雷區邊線
//左上角是白線,右下角是黑線,以顯示立體感
CPen mypen;
CPen*myoldPen; 
mypen.CreatePen(PS_SOLID,2,RGB(255,255,255));
myoldPen=pDC->SelectObject(&mypen);   
for(inti=0;i<m_ColCount;i++) 
       {
              for(intj=0;j<m_RowCount;j++)
              { 
                     pDC->MoveTo(10+i*15,50+j*15+15); 
                     pDC->LineTo(10+i*15,50+j*15);  
                     pDC->LineTo(10+i*15+15,50+j*15); 
              } 
       }
pDC->SelectObject(myoldPen);
//右下角黑線
CPen mypen2;
CPen*myoldPen2; 
mypen2.CreatePen(PS_SOLID,1,RGB(0,0,0));
myoldPen2=pDC->SelectObject(&mypen2);
for(intii=0;ii<m_ColCount;ii++) 
{
       for(intjj=0;jj<m_RowCount;jj++)
       { 
              pDC->MoveTo(10+ii*15,50+jj*15+15); 
              pDC->LineTo(10+ii*15+15,50+jj*15+15);     
              pDC->LineTo(10+ii*15+15,50+jj*15);
       } 
}
pDC->SelectObject(myoldPen2);

說明:OnDraw函式同樣是在程式執行時就開始呼叫,執行繪製操作時介面就會顯示新增的背景以及繪製的格子(雷區的邊線),這裡的MoveTo和LineTo函式功能分別為移到某一點和連線至某一點,函式中兩個引數對應座標,客戶區的左上頂點座標為(0,0),向左為x正向向右延伸,向下為y正向向下延伸,單位均為畫素。

1.6 剩餘雷數和表情按鈕以及遊戲進行時間的位置

級別\型別

剩餘雷數

表情按鈕

時間

雷區

雷數

介面size

初級

(5,10)

(65,10)

(105,10)

(10,50)

10

10+9*15+10

中級

(20,10)

(115,10)

(190,10)

(10,50)

40

10+16*15+10

高階

(20,10)

(215,10)

(400,10)

(10,50)

99

10+30*15+10

自定義

(20,10)

((10+列數*15+10)/2-15,10)

(10+列數*15+10-70,10)

(10,50)

設定值

10+列數*15+10

注:單位pix

1 前4列是對應邊框的左上頂點的座標

2 剩餘雷數和時間框大小均50*30  框內的數字顯示距離左邊框的距離均為10個畫素點

3 表情按鈕的大小 30*30 雷區每一個小格子大小均為15*15

4 剩餘雷數邊框 表情按鈕 時間邊框距離客戶區的上端均為10個畫素點

根據以上資訊,自定義介面的size可以表示為:

長:10+15*列數+10  

寬:10+30+10+15*行數+10

為了保持介面的對稱,不同級別的模式下,表情按鈕應該居中,剩餘雷數邊框和時間邊框距離介面左右邊界均應該為10個畫素點。

時間邊框的左邊界x座標即為:介面寬度—10—50

表情按鈕左邊界x座標應該為中軸線x座標減去表情按鈕寬度的一半即:(10+15*列數+10 )/2-15

確定了不同介面的size,呼叫函式

CWnd *CWnd=AfxGetMainWnd();//掃雷的視窗

CWnd->MoveWindow( 300, 200, width,height,TRUE );

(300,200)表示顯示介面的位置,width和height表示的是對應級別介面的大小

注:在初級,中級和高階模式情況下,這4個數值固定,給一組參考資料

級別\位置引數

x

y

width

height

初級

300

200

170

250

中級

300

200

270

355

高階

300

200

480

360

在點選自定義後,在彈出的對話方塊中輸入行、列以及雷數點選確定後,介面會發生相應的變化,在程式設計的時候出現了一個問題,介面的邊界會出現對不齊的問題。重複檢查上面的計算邊框位置後,確定計算方法沒有問題,再調查其他可能的原因,最後發現自定義只是改變了客戶區的大小,主對話方塊並沒有相應地改變。因此,在自定義處理中width,heigh這兩個引數需要做處理,在自定義訊息相應函式中新增下面的程式碼即可控制介面邊界對齊。

CWnd *CWnd=AfxGetMainWnd();//掃雷的視窗
CRect View_Client_Rect;
CRect MainWnd_Rect;
int MainWnd_Border_x=0;
int MainWnd_Border_y=0; 
GetClientRect(&View_Client_Rect);
CWnd->GetWindowRect(&MainWnd_Rect); 
MainWnd_Border_x=MainWnd_Rect.Width()-View_Client_Rect.Width();
MainWnd_Border_y=MainWnd_Rect.Height()-View_Client_Rect.Height(); 
if(dlg.m_uiMineAreaColCount<15)
{
       CWnd->MoveWindow(300, 200, MainWnd_Border_x+10+m_ColCount*15+10,MainWnd_Border_y+50+m_RowCount*15+10, TRUE );   
}
else
{
       CWnd->MoveWindow(300, 200, MainWnd_Border_x+10+m_ColCount*15+10,MainWnd_Border_y+50+m_RowCount*15+10, TRUE );
}
說明:第二個版本中資源大小做過調整,但是思路是一致的。

1.7 剩餘雷數和表情按鈕以及遊戲進行時間的顯示

1.7.1 剩餘雷數的顯示 初始介面顯示相應級別的地雷個數,每發現一個地雷以及右鍵點選一次雷區,數目減1,另外還要注意在定時器中重新整理這個區域

1.7.2 時間的顯示 以滑鼠左鍵點選介面開始計時,另外這個區域必須時時重新整理,否則時間不會變化,知道結束才顯示遊戲進行的時間。這涉及到定時器的控制。定時器的控制邏輯是在滑鼠左鍵點選雷區開始的,遊戲結束時釋放的。

1.7.3 表情按鈕和雷區格子的顯示

控制邏輯如下:

//判斷顯示哪一幅點陣圖
//Mine.iBitMap=1    已按下的數字區 取值0 1 2 3 4 5 6 7 8 顯示按鈕0
//Mine.iBitMap=2    顯示旗 顯示按鈕1
//Mine.iBitMap=3    顯示問號顯示按2
//Mine.iBitMap=-1 結束時顯示中雷  顯示按鈕3

1.8 顯示效果的處理 有了1.6中的各個控制元件的位置座標資訊,通過前面介紹的MoveTo和LineTo函式可以給顯示剩餘雷數和時間邊框以及表情按鈕邊沿繪製白色線條,增強立體顯示效果,實現起來也很簡單。

以上即為介面上需要編輯的全部內容。

(2)新增資源

掃雷程式需要新增的資源包括圖片和聲音資源以及配置檔案。圖片資源包括介面中顯示的圖片、數字、表情等,聲音資源是兩個wave格式的音訊檔案,點選時沒有觸雷發出的聲音,點選後觸雷發出的爆炸聲。

2.1 圖片資源  圖2-1所示的是掃雷遊戲中的所有的圖片資源,通過PhotoShop將他們擷取為單個的點陣圖資源,將所有的圖片資源存放於資料夾Mine中,然後將這個資料夾置於系統目錄:\MineSweeping\res中。前12個是在雷區的,後4個是表情按鈕。為了便於載入,須各自保證其連續性。點陣圖ID號:

按鈕點陣圖:30*30(size ),ID號依次為:IDB_ANNIU1,IDB_ANNIU2,IDB_ANNIU3,IDB_ANNIU4 ;

雷區點陣圖:15*15(size),ID號依次為:IDB_BITMAP0,IDB_BITMAP1,。。。 。。。IDB_BITMAP11;

新增圖片資源:開啟工程的資源檢視  右鍵點選Icon(Bitmap)——新增資源——資源型別選擇Bitmap——匯入——在資料夾\MineSweeping\res\Mine中選擇製作好的上述點陣圖資源,編輯對應的ID

圖2-1 掃雷遊戲中的圖片資源

載入圖片資源:圖片資源新增到專案後在引用的時候需要通過變數來存放,

CBitmap m_Bitmap[12]; //點陣圖陣列
CBitmap m_Anniu[4];  //表情按鈕點陣圖陣列

說明:給點陣圖陣列賦值並載入相應的點陣圖,陣列m_Bitmap的第一個元素載入IDIDB_BITMAP0的點陣圖,第二個元素載入IDIDB_BITMAP1的點陣圖依次類推,第12個元素載入IDIDB_BITMAP11的點陣圖,這樣陣列m_Bitmap包含了一個雷區對應點陣圖的所有顯示的狀態。同理,給表情按鈕陣列賦值並載入相應的點陣圖,陣列m_Anniu的第一個元素載入IDIDB_ANNIU1的點陣圖,第二個元素載入IDIDB_ANNIU2的點陣圖,第三個元素載入IDIDB_ANNIU3的點陣圖,第四個元素載入IDIDB_ANNIU4的點陣圖,這樣陣列m_Anniu包含了一個表情按鈕對應4種點陣圖的所有顯示的狀態

這裡的CBitmap是MFC提供的一個位圖類,裡面封裝了關於點陣圖的所有操作方法,具體的參見msdn。載入圖片的時機應該是在程式執行開始的時候,因此在建構函式中新增一個函式命名為LoadBitmap(),這個函式功能就是載入點陣圖。具體方法:以載入第一幅點陣圖和第一個表情按鈕為例:

m_Bitmap[0].LoadBitmap(IDB_BITMAP0);
m_Anniu[0].LoadBitmap(IDB_ANNIU1);

需要注意的是函式的引數是對應點陣圖的ID,其他的依此類推。

2.2聲音資源在網上下載兩個wave格式的音訊檔案,爆炸聲和另外一種你覺得合適的聲音(點選不觸雷)。爆炸聲命名為BOMB.WAV,另外一個可以命名為OTHERS.WAV。新建一個資料夾命名為Sound,置於資原始檔目錄Res資料夾下。將這兩個聲音檔案存放於Sound資料夾中。然後在資源檢視中新增這兩個聲音資源,具體方法見後續博文MFC關於聲音的處理,這裡篇幅原因不做細究。新增後ID號分別命名為IDR_BOMB和IDR_OTHERS。這裡順便提一下關於這兩種聲音的播放時機以及處理。點選滑鼠左鍵,如果觸雷,呼叫

PlaySound(MAKEINTRESOURCE(IDR_BOMB),AfxGetResourceHandle(),SND_ASYNC|SND_RESOURCE|SND_NODEFAULT);

如果沒有觸雷,呼叫

PlaySound(MAKEINTRESOURCE(IDR_OTHERS),AfxGetResourceHandle(),SND_ASYNC|SND_RESOURCE|SND_NODEFAULT);

另外,播放聲音還需要新增標頭檔案和引用播放聲音的庫,在MineSweepingView.cpp中新增

#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "WINMM.LIB")

2.3配置檔案 程式中的掃雷排行榜需要配置檔案的支援,另外將背景顏色也設為配置項,避免介面單一,這樣個人可以根據自己的情況設定不同的背景。配置檔案中的具體操作在前面的博做過詳細的說明,這裡不再重複。需要說明的是,每一個配置項在程式中需要一個對應的成員變數來儲存和表示。配置檔案中的配置項如下:

[Primary]
TimePrimary=999
NamePrimary=佚名
[Middle]
TimeMiddle=999
NameMiddle=佚名
[High]
TimeHigh=999
NameHigh=佚名 
[ChooseColor]
Param1 = 0
Param2 = 0
Param3 = 255
[NoChooseColor]
Param1 = 192
Param2 = 192
Param3 = 192

具體含義:前三項是不同級別最高記錄的操作者個時間,後兩項分別是有背景和無背景的顏色配置引數。關於後兩項,使用者可以根據自己的喜好來配置不同的背景顏色,只需要配置檔案中改變這三個引數值即可。更多顏色資訊,可參考:RGB顏色對照表

以上即為介面上需要編輯的全部內容。

(3)資料結構

3-1 級別 根據掃雷不同的難度設定不同的等級,定義一個列舉MINE_LEVEL 列舉的定義置於標頭檔案 MineSweepingView.h 中。程式碼如下:

enum MINE_LEVEL
{
       MINE_PRIMARY_LEVEL,//初級
       MINE_MIDDLE_LEVEL,//中級
       MINE_HIGH_LEVEL,//高階
       MINE_CUSTOM_LEVEL //自定義
};

3-2 雷 對於雷單獨定義一個結構體 Mine。結構體的定義置於標頭檔案 MineSweepingView.h 中。程式碼如下:

struct  Mine
{
       //顯示哪一個點陣圖取值
       int iBitMap;
       //這個位置周圍8個位置相應的地雷數
       int iArondMineNum;
};

然後再在View類(MineSweepingView)中新增變數和函式。

3-3 標頭檔案 在標頭檔案 MineSweepingView.h 中 新增變數和函式的宣告

public:
int m_iLeftMineNum;  //剩餘地雷的數目
int m_iMineNum;      //地雷數目
int m_iEnd;          //結束
short m_sTime;       //計時
int m_iTimeStart;    //開始計時
CBitmap m_Bitmap[12];//雷區點陣圖陣列
CBitmap m_anniu[4];  //表情按鈕點陣圖陣列
int m_RowCount;//雷區行數
int m_ColCount;//雷區列數  
Mine m_Mine[50][50];  //最大雷區 50行50列
Minem_Mine[50][50];  //最大雷區
MINE_LEVELm_MineLevel;//表示遊戲等級的成員變數
intm_iBQPosition_X;//表情按鈕在不同的模式(等級下顯示的位置,僅改變x座標)
intm_iTextPosition_LeftMine;//顯示剩餘雷數框左側x座標
boolm_bIsPlayVoice;//是否播放聲音
intm_iGameType;//記錄遊戲型別 1初級2中級 3高階
boolm_bIsSucced;//是否掃雷成功
int m_iMineZeroNum;//表示已經發現的m_Mine[a][b].iArondMineNum==0和1的個數 用於判斷成功掃雷
int m_iFlagNum;//旗幟的數目	
boolm_bIsChangeColor;//是否改變介面顯示顏色
bool m_bIsPlayMusic;//是否播放音樂
//播放背景音樂相關
unsignedlong int m_ulCount;
//配置檔案 start
//掃雷排行榜
intm_iPrimaryRecord;
intm_iMiddleRecord;
intm_iHighRecord;
CStringm_strPrimaryName;
CStringm_strMiddleName;
CStringm_strHighName; 
//背景顏色的配置 設定項
//有顏色的背景
int m_iHaveColor_RGBParam1;
intm_iHaveColor_RGBParam2;
intm_iHaveColor_RGBParam3;
//無顏色背景
intm_iNoColor_RGBParam1;
intm_iNoColor_RGBParam2;
intm_iNoColor_RGBParam3;
//配置檔案 end
新增滑鼠訊息響應函式:
protected:
afx_msg voidOnTimer(UINT nIDEvent); //計時器函式
afx_msg voidOnLButtonDown(UINT nFlags, CPoint        point);//滑鼠按下左鍵函式
afx_msg voidOnLButtonUp(UINT nFlags, CPoint point);  //滑鼠左鍵彈起訊息響應函式
afx_msg voidOnRButtonDown(UINT nFlags, CPoint       point);//滑鼠按下右鍵函式
說明:關於滑鼠左鍵 右鍵 按下彈起的訊息響應函式,在這個工程中有三處需要新增程式碼。函式原型,函式實現,以及用來關聯訊息和訊息響應的巨集。在後續將會針對滑鼠訊息響應做總結,這裡不再多做敘述。
public:
void Minezero();//這個位置周圍雷數為0         
void OnStart(); 
void LoadBitmap();
void OnInitializeMine(MINE_LEVELminelevel, bool bIsSelfDefine,int rownum,int colnum,int minenum);//佈雷並對雷區初始化
注:當第二個引數為false時,後三個引數無效
void DrawBoard(CDC*pDC);//繪製棋盤
void DrawAndShowBitmap(CDC*pDC);//繪製並顯示對應的點陣圖 雷區格子和表情按鈕   
void ShowLeftMineNum(CDC*pDC);
void ShowTime(CDC*pDC);
void LoadConfig();
voidOnHeroProcess();//成功掃雷後 英雄榜的實現處理
boolIsSucced();//掃雷是否成功
voidOnColorProcess(CDC* pDC);//顏色對話方塊處理
 
DWORDgetinfo(DWORD item);//播放背景音樂的輔助函式
//選單訊息響應應函式
afx_msg voidOnGameStart();
afx_msg voidOnGamePrimary();
afx_msg voidOnGameMiddle();
afx_msg voidOnGameHigh();
afx_msg voidOnGameVoice();
afx_msg voidOnGameSelfdefine();
afx_msg voidOnGameHenro();
afx_msg voidOnGameExit();
afx_msg voidOnGameColour();
afx_msg voidOnGameMusic();
afx_msg voidOnGameInstruction();

說明:3-3是程式中完整的標頭檔案,包含了程式中所有的成員變數以及訊息響應函式和功能函式,它們的定義就不在本文中敘述了,只講主要的實現思路。

(4)功能實現

4-1建構函式成員變數的賦值,除此之外,建構函式是在程式執行時就開始執行的,因此建構函式中還包含了點陣圖的載入函式,配置檔案載入函式以及佈雷並對雷區初始化函式。

點陣圖的載入函式和配置檔案載入函式已經做過介紹,這裡簡單介紹一下佈雷並對雷區初始化函式voidOnInitializeMine(MINE_LEVEL minelevel, bool bIsSelfDefine,int rownum,intcolnum,int minenum)

第一個引數對應不同的級別,各個級別函式和列數不同在這個函式中對相應的行數,列數以及雷數賦值。這裡面有個比較核心的問題:佈雷。需要用到隨機數,最開始試過以系統作為隨機種子,呼叫CetCurrentTime(),然後獲取描述,這樣每一次獲取的隨機數都不同,但是這個在不同等級切換的時候會導致很卡,放棄了這種做法。以下面的方式處理:

srand((unsigned)time(0));
	//設定m_iMineNum個雷
	do 
	{  
		//以當前秒數為產生隨機演算法
		int k=(rand())%m_ColCount; 
		int l=(rand())%m_RowCount; 
		//為了避免一個位置同時算兩個雷
		//只允許當前位置不是雷時賦值為雷
		if(m_Mine[k][l].iArondMineNum != -1) 
		{  
			//這個位置有雷
			m_Mine[k][l].iArondMineNum = -1;  
			aa++;  
		} 
	}while(aa!=m_iMineNum); 

	//給方格賦值,計算雷數
	for(int a=0;a<m_ColCount;a++)
	{
		for(int b=0;b<m_RowCount;b++) 
		{
			if(m_Mine[a][b].iArondMineNum==0)  
			{  
				for(int c=a-1;c<a+2;c++) 
				{
					for(int d = b-1;d < b+2; d++)
					{
						if(c>=0 && c< m_ColCount && d >= 0 && d < m_RowCount) 
						{
							if(m_Mine[c][d].iArondMineNum == -1)
							{
								m_Mine[a][b].iArondMineNum++;
							}
						}
					}
				}
			} 
		}
	}

通過以上步驟即完成了隨機佈雷,它是掃雷中核心的一塊。

4-2 Minezero 函式功能掃描,如果是已經被按下且雷數為0,顯示它周圍的八個格,並重畫。掃雷中會出現這樣的一種情況:地雷分佈較分散,有一片一個地雷也沒有,需要實現點選某一個格子而消失一片。要實現這種效果還需要新增函式Minezero(), 函式程式碼如下,它是在定時器中呼叫,每時每刻都在判斷能掃實現掃雷掃一片的效果。

for(int i=0;i<m_ColCount;i++) 
	{
		for(int j=0;j<m_RowCount;j++)  
		{
			//點選後是空白的情況
			if(m_Mine[i][j].iArondMineNum==0 && m_Mine[i][j].iBitMap==1)  
			{  
				
				for(int n=i-1;n<i+2;n++) 
				{
					for(int	m=j-1;m<j+2;m++)
					{
						if(n >= 0 && n < m_ColCount/*25*/ && m >= 0 && m < m_RowCount)  
						{
							if(m_Mine[n][m].iArondMineNum !=-1 && m_Mine[n][m].iBitMap == 0)  
							{ 
								m_Mine[n][m].iBitMap=1; 
								//不必掃雷且沒有雷的格子的數目
								m_iMineZeroNum++;
								CRect rect;  
								rect.left = n*15+10;  
								rect.right = n*15+25;
								rect.top = m*15+50;  
								rect.bottom = m*15+65;  
								InvalidateRect(&rect); 
							}
						}
					}
				}
			}
		}
	}

4-3 IsSucced:判斷是否掃雷成功函式。判斷邏輯相應級別的格子數(行*列是否等於被標記的雷數(用一個變量表示)加上成功開啟的格子數),成功開啟的格子數即為成員變數m_iMineZeroNum,被標記的雷數即為 m_iFlagNum;//旗幟的數目
在滑鼠左鍵按下時,不是地雷,就執行 m_iMineZeroNum++,另外一處就是在函式Minezero中,自動掃除的格子數。這個函式在實現掃雷排行榜至關重要,後面會進一步提到。m_iFlagNum在點選右鍵的時候完成m_iFlagNum++,再點一次完成m_iFlagNum--。這裡還要注意的時候,在開始函式中不要忘了將他們初始化為0,否則會出這樣的一種情況,在從選單中選擇不同的等級的時候,可以正常操作,但是從點選表情按鈕開始的時候,這兩個值會從上一次的基礎上(通常不為0)繼續++ 或--,這樣判斷成功或失效。原因是從選單項開始的時候會呼叫OnInitializeMine,這個函式中已經將他們置為0,所以是正常的,點選表情按鈕是呼叫OnStart函式,因此在這個函式中也必須將他們置為0.當然我的程式是這樣做的,你可以有其他的方法,只是不要忘了初始化。另外判斷成功的方法還有其他的,像只要右鍵點選正確的雷的位置就算成功,也是可以的,好像這種方法更好,也可以是不同等級的雷數加上開啟雷區格子的數目等於雷區格子的總數,總之不唯一,根據自己的設計思路即可。

4-4 OnTimer 計時器函式,實現部分如下:

//結束,返回
if(m_iEnd==1)
{
       return; 
}
//     顯示個數為0的方格
        Minezero(); 
       OnHeroProcess();
       if(m_iTimeStart>=20) 
       {
              m_iTimeStart=1; 
              m_sTime++;
        /*
           。。。。
        重畫時間框 對應不同的級別前面已做過介紹
           */
       注意再次重新整理這個邊框
else
{
       m_iTimeStart++;
}

說明:關於定時器

4-5 滑鼠訊息響應函式

4.5.1 OnLButtonDown控制邏輯:點選到雷區(函式引數的座標點會自動識別),是地雷,結束(其他如時間,表情按鈕,剩餘雷數等做相應的處理),不是雷區,介面上點選有反應的地方只有雷區和表情按鈕,是表情按鈕,重新開始。

4.5.2 OnLButtonUp控制邏輯:判斷按下的是不是雷,是,顯示踩雷標誌點陣圖,表情按鈕顯示張口那一幅;不是,表情按鈕顯示笑臉,點陣圖顯示相應的字數或空白

4.5.3 OnRButtonDown控制邏輯:

m_Mine[a][b].iBitMap=2;//顯示旗幟 3顯示問號  兩個之間切換

注意1:重畫點選的格子以及剩餘雷數的方框

說明:關於滑鼠左鍵 右鍵 按下彈起的訊息響應函式,在這個工程中有三處需要新增程式碼。函式原型,函式實現,以及用來關聯訊息和訊息響應的巨集。在後續將會針對滑鼠訊息響應做總結,這裡不再多做敘述。

標頭檔案中在兩個AFX_MSG註釋巨集之間是訊息響應函式原型的宣告。原始檔中有兩處:一處是在兩個AFX_MSG_MAP註釋巨集之間的訊息對映巨集,通過這兩個巨集把訊息與訊息響應函式關聯起來;另一處是原始檔中的訊息響應函式的實現程式碼。本例中,以滑鼠左鍵按下訊息響應為例,CMineSweepingView類對 WM_LBUTTONDOWN訊息響應的三處資訊分別如下所示:

1)在標頭檔案(CSaoLei2View.h)中宣告訊息響應函式原型

//{{AFX_MSG(CSaoLei2View)   //註釋巨集

afx_msg voidOnLButtonDown(UINT nFlags, CPoint point);

//}}AFX_MSG   //註釋巨集

說明:

在註釋巨集之間的宣告在VC中灰色顯示。afx_msg巨集表示宣告的是一個訊息響應函式。

2)在原始檔(CSaoLei2View.cpp)中進行訊息對映

BEGIN_MESSAGE_MAP(CDrawView,CView)

//{{AFX_MSG_MAP(CDrawView)

ON_WM_LBUTTONDOWN()

//}}AFX_MSG_MAP

// Standardprinting commands

ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT,CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview)

END_MESSAGE_MAP()

說明:

在巨集BEGIN_MESSAGE_MAP()與END_MESSAGE_MAP()之間進行訊息對映。

巨集ON_WM_LBUTTONDOWN()把訊息WM_LBUTTONDOWN與它的響應函式OnLButtonDown()相關聯。這樣一旦有訊息的產生,就會自動呼叫相關聯的訊息響應函式去處理。

巨集ON_WM_LBUTTONDOWN()定義如下:

#defineON_WM_LBUTTONDOWN() \

{WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \

(AFX_PMSG)(AFX_PMSGW)(void(AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown }

3)原始檔中進行訊息響應函式處理。(CSaoLei2View.cpp中自動生成OnLButtonDown函式輪廓,如下)

voidCDrawView::OnLButtonDown(UINT nFlags, CPoint point)

{

// TODO: Addyour message handler code here and/or call default

CView::OnLButtonDown(nFlags,point);

}

其他的 滑鼠左鍵彈起、滑鼠右鍵按下、滑鼠右鍵彈起和滑鼠左鍵按下訊息響應的處理步驟完全一致,不再重述。

以上即為滑鼠訊息響應函式的全部內容。

4-6選單訊息響應函式

4.6.1開始:函式中重新對雷區初始化並佈雷,對介面不操作,點選即可進行掃雷

4.6.2 初級、中級和高階 函式處理基本一致,除了一個成員變數m_MineLevel,不同的級別給這個變數賦相應的值。再改變視窗位置大小(前面已介紹)呼叫初始化並佈雷函式,在呼叫OnDraw函式和OnStart函式

4.6.3自定義:自定義需要建立一個對話方塊。步驟:切換到資源檢視——Dialog——新增Dialog,屬性欄將這個對話方塊的ID設為IDD_DIALOG_SELFDEFINE,Caption設為自定義雷區,雙擊這個對話方塊,根據類導向,建立一個類,類名為SelfDefineMineArea 繼承自Cdialog,然後新增三個靜態文字控制元件、三個文字編輯框,靜態文字框的屬性中Caption自上而下寫行數,列數和雷數。對應的三個文字編輯框中新增3個對應的成員變數

m_uiMineAreaRowCount,m_uiMineAreaColCount,m_uiMineNum

然後再在MineSweepingView.cpp中先新增標頭檔案的引用

//自定義掃雷視窗

#include"SelfDefineMineArea.h"

在自定義訊息響應函式OnGameSelfdefine()中新增程式碼:

if(IDOK == dlg.DoModal())
{
m_ColCount=dlg.m_uiMineAreaRowCount;
m_RowCount=dlg.m_uiMineAreaColCount;
m_iLeftMineNum=dlg.m_uiMineNum;
}

注:以上只是說明兩個介面中數值傳遞的一個過程,並不說明實際就是這樣的。其實實際程式還需要

新增邊界處理,當輸入的行數或者列數都小於9時,都按照初級處理。但這三行程式碼是必須位置和時機不定,以實際為準。後面的步驟和4.6.2一致。關於主視窗和客戶區在這種情況下可能出現的對齊會問題在前面已做過介紹,不再重複。

注:這四個選單項在當前介面只有一個被選中,這種實現方式涉及到選單的操作,後續再做介紹,下面的顏色和聲音也是一樣的。

4.6.4 顏色 通過一個布林型的成員變數來控制是否選中背景色,在Ondraw函式中新增對這個變數的兩種處理方式即可。

4.6.5 聲音 播放聲音的函式已經做過介紹,播放時機是在滑鼠按下後,判斷是否是點選的雷區,是,播放爆炸聲;不是,播放另一種聲音。

4.6.6 音樂 子選單中的開始和停止為背景音樂控制項。特點選擇本地的音樂。具體的實現程式碼可以參考:MFC聲音的播放和錄音的實現(一)

4.6.7 掃雷英雄榜  實現這一功能,需要再新增兩個對話方塊並建立相應的類。具體的操作方式參照4.6.3。


圖4-6-1儲存姓名的介面


圖4-6-2掃雷不同等級最高紀錄資訊

圖4-6-1 所示的是類SettingName 對應的對話方塊,當成功掃雷時,會判斷當前掃雷用時和這項紀錄保持者所費時間(儲存在配置檔案,後讀出來儲存在了相應的成員變數中)進行比較,只有時間比這項紀錄保持者用的時間小,才會彈出這個介面,操作者輸入自己的名字,點選OK後,會將名字和當前成功掃雷的時間同時寫入配置檔案中的對應項中,下次進入到圖4-6-2介面時,就會顯示這項紀錄和創作者。

圖4-6-2是由 DlgHero類對應的 對話方塊,負責顯示出各個級別的最高紀錄和這項紀錄的保持者。這一塊邏輯有一點繞,將完整的程式碼貼在下面,僅供參考。

在CSettingName對話方塊中新增一個文字編輯控制元件,並新增成員變數 CStringm_strName;

只保留確定按鈕,刪除掉取消按鈕。

在CDlgHero對話方塊中新增9個靜態文字控制元件:左側的三個顯示用,只需要改變對應屬性的Caption,自上而下依次為初級、中級、高階;中間三個分別對應初級、中級、高階下的最高紀錄(時間),先編輯對應ID,依次(自上而下)新增成員變數  

UINT m_sTimePrimaryRecord;
UINT m_sTimeMiddleRecord;
UINT m_sTimeHighRecord;

右邊三個分別對應初級、中級、高階下的最高紀錄的保持者的名字,編輯對應的ID,依次(自上而下)新增成員變數  

CString m_sPrimaryHolder;
CString m_sMiddleHolder;
CString m_sHighHolder;

然後再改變取消按鈕的名稱和ID,在它的屬性欄中編輯,caption改為重置,ID改為IDR_RESET

然後對這個控制元件新增訊息處理函式,函式程式碼為:

void CDlgHero::OnBnClickedReset()
{
m_sTimePrimaryRecord=0;
m_sTimeMiddleRecord=0;
m_sTimeHighRecord=0;
m_sPrimaryHolder=_T("佚名"); 
m_sMiddleHolder =_T("佚名"); 
m_sHighHolder=_T("佚名"); 

PROGRAM_PATH 配置檔案在本地中的絕對路徑名稱(路徑問題前面的博文已經做了詳細介紹)
//寫入初級成功掃雷破紀錄的人和所用時間
CString strTemp;
strTemp.Format(_T("%d"),m_sTimePrimaryRecord);
::WritePrivateProfileString(_T("Primary"), _T("NamePrimary"),m_sPrimaryHolder,PROGRAM_PATH);
::WritePrivateProfileString(_T("Primary"), _T("TimePrimary"),strTemp,PROGRAM_PATH);

//寫入初級成功掃雷破紀錄的人和所用時間	
strTemp.Format(_T("%d"),m_sTimeMiddleRecord);
::WritePrivateProfileString(_T("Middle"), _T("NameMiddle"),m_sMiddleHolder,PROGRAM_PATH);
::WritePrivateProfileString(_T("Middle"), _T("TimeMiddle"),strTemp,PROGRAM_PATH);

//寫入初級成功掃雷破紀錄的人和所用時間
strTemp.Format(_T("%d"),m_sTimeHighRecord);
::WritePrivateProfileString(_T("High"), _T("NameHigh"),m_sHighHolder,PROGRAM_PATH);
::WritePrivateProfileString(_T("High"), _T("TimeHigh"),strTemp,PROGRAM_PATH);
}

排行榜處理函式中新增如下程式碼:
void CMineSweepingView::OnHeroProcess()
{
	//掃雷成功後 掃雷英雄榜的處理
	PROGRAM_PATH 配置檔案在本地中的絕對路徑名稱(路徑問題前面的博文已經做了詳細介紹)
	//初級
	if (m_iLeftMineNum == 0 && IsSucced())
	{
		KillTimer(1);
		AfxMessageBox(_T("掃雷成功!"));
		//如果是初級
		if (m_iGameType == 1)
		{
			//比較當前用時和從配置檔案中讀取的初級用時比較
			if (m_sTime < m_iPrimaryRecord)//等於還不算打破記錄
			{
				CSettingName dlg;
				if (IDOK == dlg.DoModal())
				{
					if(dlg.m_strName == _T(""))
					{
						m_strPrimaryName=_T("佚名");
					}
					else
					{
						m_strPrimaryName = dlg.m_strName;
					}

				}
				m_iPrimaryRecord = m_sTime;
			}
			//寫入初級成功掃雷破紀錄的人和所用時間
			CString strTemp;
			strTemp.Format(_T("%d"),m_iPrimaryRecord);
			::WritePrivateProfileString(_T("Primary"), _T("NamePrimary"),m_strPrimaryName,PROGRAM_PATH);
			::WritePrivateProfileString(_T("Primary"), _T("TimePrimary"),strTemp,PROGRAM_PATH);
		}

		//如果是中級
		if (m_iGameType == 2&& IsSucced())
		{
			//比較當前用時和從配置檔案中讀取的初級用時比較
			if (m_sTime < m_iMiddleRecord)//等於還不算打破記錄
			{
				CSettingName dlg;
				if (IDOK == dlg.DoModal())
				{
					if(dlg.m_strName == _T(""))
					{
						m_strPrimaryName=_T("佚名");
					}
					else
					{
						m_strMiddleName = dlg.m_strName;
					}
				}
				m_iMiddleRecord = m_sTime;
			}
			//寫入初級成功掃雷破紀錄的人和所用時間
			CString strTemp;
			strTemp.Format(_T("%d"),m_iMiddleRecord);
			::WritePrivateProfileString(_T("Middle"), _T("NameMiddle"),m_strMiddleName,PROGRAM_PATH);
			::WritePrivateProfileString(_T("Middle"), _T("TimeMiddle"),strTemp,PROGRAM_PATH);
		}

		//如果是高階
		if (m_iGameType == 3&& IsSucced())
		{
			//比較當前用時和從配置檔案中讀取的初級用時比較
			if (m_sTime < m_iHighRecord)//等於還不算打破記錄
			{
				CSettingName dlg;
				if (IDOK == dlg.DoModal())
				{
					if(dlg.m_strName == _T(""))
					{
						m_strHighName=_T("佚名");
					}
					else
					{
						m_strHighName = dlg.m_strName;
					}
				}
				m_iHighRecord = m_sTime;
			}
			//寫入初級成功掃雷破紀錄的人和所用時間
			CString strTemp;
			strTemp.Format(_T("%d"),m_iHighRecord);
			::WritePrivateProfileString(_T("High"), _T("NameHigh"),m_strHighName,PROGRAM_PATH);
			::WritePrivateProfileString(_T("High"), _T("TimeHigh"),strTemp,PROGRAM_PATH);
		}		
	}
}

4-6-8 退出 呼叫函式 AfxGetMainWnd()->PostMessage(WM_CLOSE,0, 0)

4-6-9  關於 修改CAboutDlg即可,在這個介面中新增一個超連結。具體的實現方法參考:MFC 中建立簡單超連結   

4-6-10  顯示雷區地圖  圖4-6-3所示的是當前雷區的佈局情況,-1表示雷的位置,其他的數字表示以這個格子為中心,周圍8個格子所包含的雷數。


圖4-6-3當前雷區地雷分佈示意圖

注:這個需要再新增一個介面,步驟和前面已經詳細介紹的掃雷排行榜步驟是一致的。在響應的選單訊息響應函式中新增下面的程式碼:

CCheatMap dlg;	
	CString map_str=_T("");
	CString tmp_str=_T("");
	for(int i=0;i<m_ColCount;i++)
	{
		for(int j=0;j<m_RowCount ;j++)
		{
		tmp_str=_T("");
		//指定格式化寬度
		tmp_str.Format(_T("%-4d  "),m_Mine[j][i].iArondMineNum);
		map_str+=tmp_str;
		}
		map_str+=_T("\r\n");		
	}
	dlg.m_strtemp=map_str;
	dlg.DoModal();
關於數值在編輯框中的顯示,可以做一下總結,最開始的時候想用列表框控制元件來實現,但是沒有實現,後續再關於控制元件做一個總結。

4-6-11 說明  開啟遊戲的說明。介紹配置項含義,以及程式的操作功能。
呼叫函式ShellExecute(NULL,_T("open"), PROGRAM_PATH, NULL, NULL, SW_SHOW)
PROGRAM_PATH即為需要開啟的檔案路徑,路徑問題見4-6-7中的連結介紹。

相關推薦

[日常練習] 5. 基於掃雷遊戲C語言實現

#include "game.h" void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set) { int i = 0; int j = 0; for(i=0; i<rows; i++) { for(j=0

掃雷遊戲C語言實現

掃雷遊戲 自己動手寫了掃雷遊戲(C語言版)。 我使用了EasyX這個庫(下載地址:http://www.easyx.cn EasyX 2014冬至版)。 在寫掃雷遊戲過程中用到了 1.二維陣列的劃分與建立 劃分: int **p,i; p=(i

掃雷遊戲C語言實現

在學習C語言初期,我們可以找一些平常玩的遊戲進行簡單的C語言實現。今天就和大家分享一下關於windows中掃雷遊戲的實現。 在正式寫程式碼前,簡單說一下對這個遊戲的分析: 1.先提示的應該是一個簡單的menu,其中包括“play”和“exit”; 2.在m

C++ 掃雷遊戲實現

C++ 掃雷遊戲 這個星期在嘗試著自己寫一個掃雷遊戲。功能基本和windows下的功能差不多。左鍵單擊翻開一個格子,如果沒有雷則顯示其周圍格子中含有雷的個數,沒有周圍的沒有雷的話,那麼就擴充套件空白塊。 擴充套件空白塊的步驟為:1、翻開一個格子,

LeetCode 529. 掃雷遊戲C++、python)

讓我們一起來玩掃雷遊戲! 給定一個代表遊戲板的二維字元矩陣。 'M' 代表一個未挖出的地雷,'E' 代表一個未挖出的空方塊,'B' 代表沒有相鄰(上,下,左,右,和所有4個對角線)地雷的已挖出的空白方塊,數字('1' 到 '8')表示有多少地雷與這塊已挖出

基於C語言實現掃雷遊戲

函式實現的功能: 1.佈置雷:用rand函式生成隨機座標再雷陣裡隨機佈雷 2.排雷:踩到雷炸死,不是雷,統計周圍一圈有⼏個雷,並記錄資訊放到另一個數組 實現原理: 1.雷的資訊儲存到一個二維陣列mine裡 2.排出來的雷存到另一個二維陣列show裡    

C語言實現掃雷遊戲要求第一次不踩雷,能展開一片雷區(望各位大佬斧正)

對於實現掃雷遊戲思路如下: 首先佈置一定大小的掃雷區如9*9 隨機(rand())佈置一定數量的雷 實現掃雷(其中要求第一次踩不到雷,能展開一片無雷區) 其具體操作如下: 建立game.h, game.c, test.c檔案以便整理 在test.c檔案中:

c語言簡單實現掃雷遊戲

對於掃雷遊戲,我相信每個人都玩過,大概瞭解它應有的操作和大概的原理。那我們應該怎樣著手去實現掃雷遊戲呢…… 首先對於雷陣這個介面,就想到可以定義的一個二維陣列,我們不可能把把雷的位置也顯示出來,所有需要定義2個二維陣列:mine用來佈雷,show用來顯示掃雷

掃雷遊戲實現C++

掃雷遊戲是Windows作業系統自帶的一個小遊戲,幾乎每個電腦使用者都接觸過它。它同時也是一款比較經典的小遊戲,實現它的方法很多,也可以用不同演算法和語言實現。近期用了兩個週末(各一天)和大半個月的空餘時間終於實現了一個比較完整的掃雷程式。現通過C++來呈現這款小遊戲的實現

C語言】簡易掃雷遊戲——C語言實現

 我們經常在電腦上面玩的掃雷遊戲,很考驗我們的判斷能力,但是實現一個掃雷遊戲並不是很困難,只要多注意一些細節就好,就可以將一個簡單的掃雷遊戲寫出來! 接下來先介紹掃雷遊戲要實現的功能: 首先,要對雷陣進行初始化,在初始化的時候要注意要定義兩個陣列,一個是讓我們掃雷的陣,

C實現掃雷遊戲(優化版)

指點 com C4D 根據 位置 菜單 sig https shadow 完成掃雷程序,並進行以下優化:①第一次下子,不炸死。(如果第一次掃到雷區,則將其置為無雷區,為保證雷的數目不變,再次隨機布一顆雷)?②坐標周圍沒雷,可以實現展開。思路:1、設置遊戲區域(10x10,考

分享我寫的2D格鬥遊戲C語言實現

fpm cal srm i2c cga wsb rpi blank iii Json、FastJson、Gson 數據結構對單鏈表進行數據排序 請教各位幾個小問題,有點暈了不太理解 StringUtil對字符串類型參數進行校驗的工具類 11r玖橇粱http://p.baid

[日常練習] 3. 基於井字棋遊戲C語言實現

井字棋算是童年課堂上的與同桌默契配合的一大樂趣...躲避著老師“關切”的目光,在眼皮底下“頂風作案”,將“燈下黑”體現的淋漓盡致!在C語言中,也算是一個小的專案,今天,我們就用C語言來實現它!!!追逐童年的一份小樂趣! 遊戲介紹: “井字棋”也稱“三子棋”,需要一個3*3的棋盤。假設

編寫程式—實現掃雷遊戲

編寫程式,實現掃雷遊戲 程式程式碼如下: game.h #ifndef __GAME_H__ #define __GAME_H__ #define ROWS 11 #define COLS 11 #define ROW (ROWS-2) #define C

三子棋小遊戲C語言實現

C語言打造簡單的三子棋小遊戲 簡單三子棋是指棋盤為3*3,玩家與電腦之間對決的遊戲。 話不多說,先上圖:其中‘0’代表電腦落子,‘X’:玩家落子 基本思路: 1.列印地圖(列印一個“#”字狀的棋盤) 2.電腦落子(隨機落子) 3.玩家落子(通過輸入座標的方式) 4判斷遊戲結果 程式碼及註釋

Life Game生命遊戲C++實現

因為上了鮑老師的課,每週都要做一次課堂練習。所以大三才想起來開C++的坑= = 程式碼是課上一個半小時寫出來的。難免有考慮不周的地方。如果有什麼問題,請各位不吝賜教~ 首先解釋一下Life Game。開局一張棋盤,隨便放幾個棋子,然後每回合棋盤做一次更新,對棋盤的每個格子來說,看它周圍的

生命細胞遊戲C++實現

生命遊戲 (遊戲作品) 生命遊戲是英國數學家約翰·何頓·康威在1970年發明的細胞自動機。它最初於1970年10月在《科學美國人》雜誌中馬丁·葛登能(Martin Gardner,1914年11月21日-2010年5月22日。又譯:馬丁·加德納)的“數學遊戲”專欄

在Untiy中實現掃雷遊戲,並且打包Apk

前言:《掃雷》是一款大眾類的益智小遊戲,於1992年發行。遊戲目標是在最短的時間內根據點選格子出現的數字找出所有非雷格子,同時避免踩雷,踩到一個雷即全盤皆輸。 在實現掃雷的主要邏輯中,主要的難點: (1)如何檢測以一個物件為中心的八個方向上面的雷的個數。 (2)檢測如果

java swing實現掃雷遊戲

詳細程式碼見我的相應github倉庫: https://github.com/29DCH/Mine-Sweeper 歡迎fork原始碼到你自己的倉庫下面。 效果圖: java awt以及swing實現的掃雷遊戲,實現了基本邏輯.主要用到了floodfill(漫水填充)演算法和knuth

[C]專案--掃雷遊戲

掃雷 楔子: 掃雷遊戲是我們小時候無聊時消磨時間的小玩意,雖然更新到Win10系統後經典的掃雷遊戲不再了,不過它現在仍以一種抓蟲子的遊戲形式存在於Windows這個系統平臺,不禁感慨遊戲還是那個遊戲,不過人已經不是那些人了啊. 其實掃雷遊戲的實現也主要運用