版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、<p> 【摘要】本論文主要闡述以面向?qū)ο蟮某绦蜷_發(fā)語言VC++為開發(fā)工具,設(shè)計一個五子棋游戲。本系統(tǒng)是個小型游戲,可以單機(jī)使用,也可以網(wǎng)絡(luò)兩個人游戲,也可以和電腦進(jìn)行游戲。論文首先介紹了開發(fā)背景及開發(fā)語言的介紹。然后介紹設(shè)計該游戲的框架結(jié)構(gòu),然后介紹了程序的設(shè)計過程,以及程序的相關(guān)算法。</p><p> 關(guān)鍵詞:游戲, 系統(tǒng) ,圖片, VISUAL C++</p><p>
2、;<b> 目 錄</b></p><p> 1 引 言 11.1 五子棋介紹 11.2 開發(fā)背景 11.3 開發(fā)環(huán)境及運(yùn)行環(huán)境 11.3.1 開發(fā)環(huán)境 11.3.2 運(yùn)行環(huán)境 12 軟件架構(gòu) 22.1 棋盤類 22.2 游戲模式類 33 棋盤類——CTable 43.1 主要成員變量說明 43.1.1 網(wǎng)絡(luò)連接標(biāo)志 43.1.2 棋盤等待標(biāo)志 43.1.3 網(wǎng)
3、絡(luò)套接字 43.1.4 棋盤數(shù)據(jù) 43.1.5 游戲模式指針 53.2 主要成員函數(shù)說明 53.2.1 套接字的回調(diào)處理 53.2.2 清空棋盤 53.2.3 繪制棋子 53.2.4 左鍵消息 63.2.5 繪制棋盤 63.2.6 對方落子完畢 63.2.7 設(shè)置游戲模式 63.2.8 勝負(fù)的判斷 74 游戲模式類 84.1 主要成員變量說明 94.1.1 棋盤指針 94.1.2 落子步驟 94.2 主要
4、成員函數(shù)說明 94.2.1 悔棋操作 94.2.2 初始化操作 94.2.3 接收來自對方的消息 104.2.4 發(fā)送落子消息 104.2.5 勝利后的處理 105 消息機(jī)制 115.1 消息機(jī)制的架構(gòu)</p><p><b> 1 引言</b></p><p><b> 1.1 五子棋介紹</b></p><
5、;p> 五子棋是起源于中國古代的傳統(tǒng)黑白棋種之一?,F(xiàn)代五子棋日文稱之為“連珠”,英譯為“Renju”,英文稱之為“Gobang”或“FIR”(Five in a Row的縮寫),亦有“連五子”、“五子連”、“串珠”、“五目”、“五目碰”、“五格”等多種稱謂。</p><p> 五子棋不僅能增強(qiáng)思維能力,提高智力,而且富含哲理,有助于修身養(yǎng)性。五子棋既有現(xiàn)代休閑的明顯特征“短、平、快”,又有古典哲學(xué)的高深
6、學(xué)問“陰陽易理”;它既有簡單易學(xué)的特性,為人民群眾所喜聞樂見,又有深奧的技巧和高水平的國際性比賽;它的棋文化源淵流長,具有東方的神秘和西方的直觀;既有“場”的概念,亦有“點”的連接。它是中西文化的交流點,是古今哲理的結(jié)晶。</p><p><b> 1.2 開發(fā)背景</b></p><p> 當(dāng)前網(wǎng)絡(luò)上流傳的五子棋游戲功能并不盡善盡美,其中最主要的問題就是人機(jī)對戰(zhàn)
7、和網(wǎng)絡(luò)對戰(zhàn)不能夠一起實現(xiàn),所以我決定開發(fā)[1]一個既能夠人機(jī)對戰(zhàn),又能夠進(jìn)行網(wǎng)絡(luò)對戰(zhàn)的五子棋系統(tǒng)。</p><p> 1.3 開發(fā)環(huán)境及運(yùn)行環(huán)境</p><p> 1.3.1 開發(fā)環(huán)境</p><p> Intel® Pentium® 4 2.0GHz,512M內(nèi)存,80G硬盤</p><p> Microsoft
8、® Windows? 2000 Professional</p><p> Microsoft® Visual C++ 6.0</p><p> Microsoft® Developer Network for Visual Studio.NET 2003</p><p> Visual Assist X 10.1.1301.0&
9、lt;/p><p> 1.3.2 運(yùn)行環(huán)境</p><p> Intel® Pentium® 2及以上處理器,32M以上內(nèi)存,4G以上硬盤</p><p> Microsoft® Windows? 9X/NT操作系統(tǒng)</p><p> 800*600或以上的屏幕分辨率</p><p>
10、<b> 2 軟件架構(gòu)</b></p><p> 軟件的總體架構(gòu)如圖2.1:</p><p><b> 圖2.1 軟件架構(gòu)</b></p><p> 考慮到整個的下棋過程(無論對方是電腦抑或其他網(wǎng)絡(luò)玩家)可以分為:己方落子、等待對方落子、對方落子、設(shè)置己方棋盤數(shù)據(jù)這一系列過程,因此一人游戲類、二人游戲類和棋盤類之間
11、的關(guān)系參考了AbstractFactory(抽象工廠)模式,以實現(xiàn)對兩個不同模塊進(jìn)行一般化的控制。[2]</p><p><b> 2.1 棋盤類</b></p><p> 整個架構(gòu)的核心部分,類名為CTable。封裝了棋盤的各種可能用到的功能[3],如保存棋盤數(shù)據(jù)、初始化、判斷勝負(fù)等。用戶操作主界面,主界面與CTable進(jìn)行交互來完成對游戲的操作。</p&
12、gt;<p><b> 2.2 游戲模式類</b></p><p> 用來管理人機(jī)對弈/網(wǎng)絡(luò)對弈兩種游戲模式,類名為CGame。CGame是一個抽象類,經(jīng)由它派生出一人游戲類COneGame和網(wǎng)絡(luò)游戲類CTwoGame,如圖2.2:</p><p> 圖2.2 CGame類派生關(guān)系</p><p> 這樣,CTable類就
13、可以通過一個CGame類的指針[4],在游戲初始化的時候根據(jù)具體游戲模式的要求實例化COneGame或CTwoGame類的對象;然后利用多態(tài)性[5],使用CGame類提供的公有接口就可以完成不同游戲模式下的不同功能了。</p><p> 3 棋盤類——CTable</p><p> 3.1 主要成員變量說明</p><p> 3.1.1 網(wǎng)絡(luò)連接標(biāo)志——m_b
14、Connected</p><p> 用來表示當(dāng)前網(wǎng)絡(luò)連接的情況,在網(wǎng)絡(luò)對弈游戲模式下客戶端連接服務(wù)器的時候用來判斷是否連接成功;事實上,它也是區(qū)分當(dāng)前游戲模式的唯一標(biāo)志。</p><p> 3.1.2 棋盤等待標(biāo)志——m_bWait與m_bOldWait</p><p> 由于在玩家落子后需要等待對方落子,m_bWait標(biāo)志就用來標(biāo)識棋盤的等待狀態(tài)。當(dāng)m_b
15、Wait為TRUE時,是不允許玩家落子的。</p><p> 在網(wǎng)絡(luò)對弈模式下,玩家之間需要互相發(fā)送諸如悔棋、和棋這一類的請求消息,在發(fā)送請求后等待對方回應(yīng)時,也是不允許落子的,所以需要將m_bWait標(biāo)志置為TRUE。在收到對方回應(yīng)后,需要恢復(fù)原有的棋盤等待狀態(tài),所以需要另外一個變量在發(fā)送請求之前保存棋盤的等待狀態(tài)做恢復(fù)之用,也就是m_bOldWait。</p><p> 等待標(biāo)志的
16、設(shè)置,由成員函數(shù)SetWait和RestoreWait完成。</p><p> 3.1.3 網(wǎng)絡(luò)套接字——m_sock和m_conn</p><p> 在網(wǎng)絡(luò)對弈游戲模式下,需要用到這兩個套接字對象。其中m_sock對象用于做服務(wù)器時的監(jiān)聽之用,m_conn用于網(wǎng)絡(luò)連接的傳輸。</p><p> 3.1.4 棋盤數(shù)據(jù)——m_data</p>&l
17、t;p> 這是一個15*15的二位數(shù)組,用來保存當(dāng)前棋盤的落子數(shù)據(jù)。其中對于每個成員來說,0表示落黑子,1表示落白子,-1表示無子。</p><p> 3.1.5 游戲模式指針——m_pGame</p><p> 這個CGame類的對象指針是CTable類的核心內(nèi)容。它所指向的對象實體決定了CTable在執(zhí)行一件事情時候的不同行為,具體的內(nèi)容請參見“游戲模式”一節(jié)。</p
18、><p> 3.2 主要成員函數(shù)說明</p><p> 3.2.1 套接字的回調(diào)處理——Accept、Connect、Receive</p><p> 本程序的套接字派生自MFC的CAsyncSocket類[6],CTable的這三個成員函數(shù)就分別提供了對套接字[7]回調(diào)事件OnAccept、OnConnect、OnReceive的實際處理,其中尤以Receive
19、成員函數(shù)重要,它之中包含了對所有網(wǎng)絡(luò)消息(參見“消息機(jī)制”一節(jié))的分發(fā)處理。</p><p> 3.2.2 清空棋盤——Clear</p><p> 在每一局游戲開始的時候都需要調(diào)用這個函數(shù)將棋盤清空,也就是棋盤的初始化工作。在這個函數(shù)中,主要發(fā)生了這么幾件事情:</p><p> 將m_data中每一個落子位都置為無子狀態(tài)(-1)。</p>&
20、lt;p> 按照傳入的參數(shù)設(shè)置棋盤等待標(biāo)志m_bWait,以供先、后手的不同情況之用。</p><p> 使用delete將m_pGame指針?biāo)赶虻脑杏螒蚰J綄ο髲亩焉蟿h除。</p><p> 3.2.3 繪制棋子——Draw</p><p> 這無疑是很重要的一個函數(shù),它根據(jù)參數(shù)給定的坐標(biāo)和顏色繪制棋子。繪制的詳細(xì)過程如下:</p>
21、<p> 將給定的棋盤坐標(biāo)換算為繪圖的像素坐標(biāo)。</p><p> 根據(jù)坐標(biāo)繪制棋子位圖。</p><p> 如果先前曾下過棋子,則利用R2_NOTXORPEN將上一個繪制棋子上的最后落子指示矩形擦除。</p><p> 在剛繪制完成的棋子四周繪制最后落子指示矩形。</p><p> 3.2.4 左鍵消息——OnLBut
22、tonUp</p><p> 作為棋盤唯一響應(yīng)的左鍵消息,也需要做不少的工作:</p><p> 如果棋盤等待標(biāo)志m_bWait為TRUE,則直接發(fā)出警告聲音并返回,即禁止落子。</p><p> 如果點擊時的鼠標(biāo)坐標(biāo)在合法坐標(biāo)(0, 0)~(14, 14)之外,亦禁止落子。</p><p> 如果走的步數(shù)大于1步,方才允許悔棋。&l
23、t;/p><p> 進(jìn)行勝利判斷,如勝利則修改UI狀態(tài)并增加勝利數(shù)的統(tǒng)計。</p><p> 如未勝利,則向?qū)Ψ桨l(fā)送已經(jīng)落子的消息。</p><p> 落子完畢,將m_bWait標(biāo)志置為TRUE,開始等待對方回應(yīng)。</p><p> 3.2.5 繪制棋盤——OnPaint</p><p> 每當(dāng)WM_PAINT消
24、息觸發(fā)時,都需要對棋盤進(jìn)行重繪。OnPaint作為響應(yīng)繪制消息的消息處理函數(shù)使用了雙緩沖技術(shù),減少了多次繪圖可能導(dǎo)致的圖像閃爍問題。這個函數(shù)主要完成了以下工作:</p><p> 裝載棋盤位圖并進(jìn)行繪制。</p><p> 根據(jù)棋盤數(shù)據(jù)繪制棋子。</p><p> 繪制最后落子指示矩形。</p><p> 3.2.6 對方落子完畢——
25、Over</p><p> 在對方落子之后,仍然需要做一些判斷工作,這些工作與OnLButtonUp中的類似,在此不再贅述。</p><p> 3.2.7 設(shè)置游戲模式——SetGameMode</p><p> 這個函數(shù)通過傳入的游戲模式參數(shù)對m_pGame指針進(jìn)行了初始化,代碼如下:</p><p> void CTable::S
26、etGameMode( int nGameMode )</p><p><b> {</b></p><p> if ( 1 == nGameMode )</p><p> m_pGame = new COneGame( this );</p><p><b> else</b></p
27、><p> m_pGame = new CTwoGame( this );</p><p> m_pGame->Init();</p><p><b> }</b></p><p> 這之后,就可以利用OO的繼承和多態(tài)特點[8]來使m_pGame指針使用相同的調(diào)用來完成不同的工作了,事實上,COneGame::
28、Init和CTwoGame::Init都是不同的。</p><p> 3.2.8 勝負(fù)的判斷——Win</p><p> 這是游戲中一個極其重要的算法,用來判斷當(dāng)前棋盤的形勢是哪一方獲勝。其詳細(xì)內(nèi)容請參見“主要算法”一節(jié)。</p><p> 4 游戲模式類——CGame</p><p> 這個類負(fù)責(zé)對游戲模式進(jìn)行管理,以及在不同的游戲
29、模式下對不同的用戶行為進(jìn)行不同的響應(yīng)。由于并不需要CGame本身進(jìn)行響應(yīng),所以將其設(shè)計為了一個純虛類[9],它的定義如下:</p><p> class CGame</p><p><b> {</b></p><p> protected:</p><p> CTable *m_pTable;</p>
30、;<p><b> public:</b></p><p><b> // 落子步驟</b></p><p> list< STEP > m_StepList;</p><p><b> public:</b></p><p><b>
31、; // 構(gòu)造函數(shù)</b></p><p> CGame( CTable *pTable ) : m_pTable( pTable ) {}</p><p><b> // 析構(gòu)函數(shù)</b></p><p> virtual ~CGame();</p><p> // 初始化工作,不同的游戲方式初始
32、化也不一樣</p><p> virtual void Init() = 0;</p><p> // 處理勝利后的情況,CTwoGame需要改寫此函數(shù)完成善后工作</p><p> virtual void Win( const STEP& stepSend );</p><p><b> // 發(fā)送己方落子<
33、;/b></p><p> virtual void SendStep( const STEP& stepSend ) = 0;</p><p><b> // 接收對方消息</b></p><p> virtual void ReceiveMsg( MSGSTRUCT *pMsg ) = 0;</p><
34、;p><b> // 發(fā)送悔棋請求</b></p><p> virtual void Back() = 0;</p><p><b> };</b></p><p> 4.1 主要成員變量說明</p><p> 4.1.1 棋盤指針——m_pTable</p><
35、;p> 由于在游戲中需要對棋盤以及棋盤的父窗口——主對話框進(jìn)行操作及UI狀態(tài)設(shè)置,故為CGame類設(shè)置了這個成員。當(dāng)對主對話框進(jìn)行操作時,可以使用m_pTable->GetParent()得到它的窗口指針。</p><p> 4.1.2 落子步驟——m_StepList</p><p> 一個好的棋類程序必須要考慮到的功能就是它的悔棋功能,所以需要為游戲類設(shè)置一個落子步驟
36、的列表。由于人機(jī)對弈和網(wǎng)絡(luò)對弈中都需要這個功能,故將這個成員直接設(shè)置到基類CGame中。另外,考慮到使用的簡便性,這個成員使用了C++標(biāo)準(zhǔn)模板庫[10](Standard Template Library,STL)中的std::list,而不是MFC的CList。</p><p> 4.2 主要成員函數(shù)說明</p><p> 4.2.1 悔棋操作——Back</p>&l
37、t;p> 在不同的游戲模式下,悔棋的行為是不一樣的。</p><p> 人機(jī)對弈模式下,計算機(jī)是完全允許玩家悔棋的,但是出于對程序負(fù)荷的考慮(此原因請參見“幾點補(bǔ)充說明”一節(jié)),只允許玩家悔當(dāng)前的兩步棋(計算機(jī)一步,玩家一步)。</p><p> 雙人網(wǎng)絡(luò)對弈模式下,悔棋的過程為:首先由玩家向?qū)Ψ桨l(fā)送悔棋請求(悔棋消息),然后由對方?jīng)Q定是否允許玩家悔棋,在玩家得到對方的響應(yīng)消息
38、(允許或者拒絕)之后,才進(jìn)行悔棋與否的操作。</p><p> 4.2.2 初始化操作——Init</p><p> 對于不同的游戲模式而言,也就有不同的初始化方式。對于人機(jī)對弈模式而言,初始化操作包括以下幾個步驟:</p><p> 設(shè)置網(wǎng)絡(luò)連接狀態(tài)m_bConnected為FALSE。</p><p> 設(shè)置主界面計算機(jī)玩家的姓名
39、。</p><p> 初始化所有的獲勝組合。</p><p> 如果是計算機(jī)先走,則占據(jù)天元(棋盤正中央)的位置。</p><p> 網(wǎng)絡(luò)對弈的初始化工作暫為空,以供以后擴(kuò)展之用。</p><p> 4.2.3 接收來自對方的消息——ReceiveMsg</p><p> 這個成員函數(shù)由CTable棋盤類的R
40、eceive成員函數(shù)調(diào)用,用于接收來自對方的消息。對于人機(jī)對弈游戲模式來說,所能接收到的就僅僅是本地模擬的落子消息MSG_PUTSTEP;對于網(wǎng)絡(luò)對弈游戲模式來說,這個成員函數(shù)則負(fù)責(zé)從套接字讀取對方發(fā)過來的數(shù)據(jù),然后將這些數(shù)據(jù)解釋為自定義的消息結(jié)構(gòu),并回到CTable::Receive來進(jìn)行處理。</p><p> 4.2.4 發(fā)送落子消息——SendStep</p><p> 在玩家
41、落子結(jié)束后,要向?qū)Ψ桨l(fā)送自己落子的消息。對于不同的游戲模式,發(fā)送的目標(biāo)也不同:</p><p> 對于人機(jī)對弈游戲模式,將直接把落子的信息(坐標(biāo)、顏色)發(fā)送給COneGame類相應(yīng)的計算函數(shù)。</p><p> 對于網(wǎng)絡(luò)對弈游戲模式,將把落子消息發(fā)送給套接字,并由套接字轉(zhuǎn)發(fā)給對方。</p><p> 4.2.5 勝利后的處理——Win</p>&
42、lt;p> 這個成員函數(shù)主要針對CTwoGame網(wǎng)絡(luò)對弈模式。在玩家贏得棋局后,這個函數(shù)仍然會調(diào)用SendStep將玩家所下的制勝落子步驟發(fā)送給對方玩家,然后對方的游戲端經(jīng)由CTable::Win來判定自己失敗。</p><p><b> 5 消息機(jī)制</b></p><p> Windows系統(tǒng)擁有自己的消息機(jī)制,在不同事件發(fā)生的時候,系統(tǒng)也可以提供不同
43、的響應(yīng)方式[11]。五子棋程序也模仿Windows系統(tǒng)實現(xiàn)了自己的消息機(jī)制,主要為網(wǎng)絡(luò)對弈服務(wù),以響應(yīng)多種多樣的網(wǎng)絡(luò)消息。</p><p> 5.1 消息機(jī)制的架構(gòu)</p><p> 當(dāng)繼承自CAsyncSocket的套接字類CFiveSocket收到消息時,會觸發(fā)CFiveSocket::OnReceive事件[12],在這個事件中調(diào)用CTable::Receive,CTable::
44、Receive開始按照自定義的消息格式接收套接字發(fā)送的數(shù)據(jù),并對不同的消息類型進(jìn)行分發(fā)處理。</p><p> 圖5.1 自定義的消息機(jī)制</p><p> 如圖5.1所示,當(dāng)CTable獲得了來自網(wǎng)絡(luò)的消息之后,就可以使用一個switch結(jié)構(gòu)來進(jìn)行消息的分發(fā)了。</p><p> 5.2 各種消息說明</p><p> 網(wǎng)絡(luò)間傳遞的
45、消息,都遵循以下一個結(jié)構(gòu)體的形式:</p><p> // 摘自Messages.h</p><p> typedef struct _tagMsgStruct {</p><p><b> // 消息ID</b></p><p> UINT uMsg;</p><p><b>
46、 // 落子信息</b></p><p><b> int x;</b></p><p><b> int y;</b></p><p> int color;</p><p><b> // 消息內(nèi)容</b></p><p>
47、TCHAR szMsg[128];</p><p> } MSGSTRUCT;</p><p> 隨著uMsg表示消息ID,x、y表示落子的坐標(biāo),color表示落子的顏色,szMsg隨著uMsg的不同而有不同的含義。</p><p> 5.2.1 落子消息——MSG_PUTSTEP</p><p> 表明對方落下了一個棋子,其中x、y
48、和color成員有效,szMsg成員無效。在人機(jī)對弈游戲模式下,亦會模擬發(fā)送此消息以達(dá)到程序模塊一般化的效果。</p><p> 5.2.2 悔棋消息——MSG_BACK</p><p> 表明對方請求悔棋,除uMsg成員外其余成員皆無效。接到這個消息后,會彈出MessageBox詢問是否接受對方的請求(如圖5.2所示),并根據(jù)玩家的選擇回返MSG_AGREEBACK或MSG_REFU
49、SEBACK消息。另外,在發(fā)送這個消息之后,主界面上的某些元素將不再響應(yīng)用戶的操作。</p><p><b> 圖5.2 請求悔棋</b></p><p> 5.2.3 同意悔棋消息——MSG_AGREEBACK</p><p> 表明對方接受了玩家的悔棋請求,除uMsg成員外其余成員皆無效。接到這個消息后,將進(jìn)行正常的悔棋操作。<
50、/p><p> 5.2.4 拒絕悔棋消息——MSG_REFUSEBACK</p><p> 表明對方拒絕了玩家的悔棋請求(如圖5.3所示),除uMsg成員外其余成員皆無效。接到這個消息后,整個界面將恢復(fù)發(fā)送悔棋請求前的狀態(tài)。</p><p><b> 圖5.3 拒絕悔棋</b></p><p> 5.2.5 和棋消息
51、——MSG_DRAW</p><p> 表明對方請求和棋,除uMsg成員外其余成員皆無效。接到這個消息后,會彈出MessageBox詢問是否接受對方的請求(如圖5.4所示),并根據(jù)玩家的選擇回返MSG_AGREEDRAW或MSG_REFUSEDRAW消息。另外,在發(fā)送這個消息之后,主界面上的某些元素將不再響應(yīng)用戶的操作。</p><p><b> 圖5.4 請求和棋</
52、b></p><p> 5.2.6 同意和棋消息——MSG_AGREEDRAW</p><p> 表明對方接受了玩家的和棋請求(如圖5.5所示),除uMsg成員外其余成員皆無效。接到這個消息后,雙方和棋。</p><p><b> 圖5.5 同意和棋</b></p><p> 5.2.7 拒絕和棋消息——M
53、SG_REFUSEDRAW</p><p> 表明對方拒絕了玩家的和棋請求(如圖5.6所示),除uMsg成員外其余成員皆無效。接到這個消息后,整個界面將恢復(fù)發(fā)送和棋請求前的狀態(tài)。</p><p><b> 圖5.6 拒絕和棋</b></p><p> 5.2.8 認(rèn)輸消息——MSG_GIVEUP</p><p>
54、 表明對方已經(jīng)投子認(rèn)輸(如圖5.7所示),除uMsg成員外其余成員皆無效。接到這個消息后,整個界面將轉(zhuǎn)換為勝利后的狀態(tài)。</p><p><b> 圖5.7 認(rèn)輸</b></p><p> 5.2.9 聊天消息——MSG_CHAT</p><p> 表明對方發(fā)送了一條聊天信息,szMsg表示對方的信息,其余成員無效。接到這個信息后,會將對
55、方聊天的內(nèi)容顯示在主對話框的聊天記錄窗口內(nèi)。</p><p> 5.2.10 對方信息消息——MSG_INFORMATION</p><p> 用來獲取對方玩家的姓名,szMsg表示對方的姓名,其余成員無效。在開始游戲的時候,由客戶端向服務(wù)端發(fā)送這條消息,服務(wù)端接到后設(shè)置對方的姓名,并將自己的姓名同樣用這條消息回發(fā)給客戶端。</p><p> 5.2.11 再
56、次開局消息——MSG_PLAYAGAIN</p><p> 表明對方希望開始一局新的棋局,除uMsg成員外其余成員皆無效。接到這個消息后,會彈出MessageBox詢問是否接受對方的請求(如圖5.8所示),并根據(jù)玩家的選擇回返MSG_AGREEAGAIN消息或直接斷開網(wǎng)絡(luò)。</p><p><b> 圖5.8 再次開局</b></p><p&g
57、t; 5.2.12 同意再次開局消息——MSG_AGREEAGAIN</p><p> 表明對方同意了再次開局的請求,除uMsg成員外其余成員皆無效。接到這個消息后,將開啟一局新游戲。</p><p><b> 6 主要算法</b></p><p> 五子棋游戲中,有相當(dāng)?shù)钠撬惴ǖ牟糠?。無論是人機(jī)對弈,還是網(wǎng)絡(luò)對弈,都需要合理算法的
58、支持,本節(jié)中將詳細(xì)介紹五子棋中使用的算法。[13]</p><p><b> 6.1 判斷勝負(fù)</b></p><p> 五子棋的勝負(fù),在于判斷棋盤上是否有一個點,從這個點開始的右、下、右下、左下四個方向是否有連續(xù)的五個同色棋子出現(xiàn),如圖6.1:</p><p> 圖6.1 判斷勝負(fù)方向</p><p> 這個算
59、法也就是CTable的Win成員函數(shù)。從設(shè)計的思想上,需要它接受一個棋子顏色的參數(shù),然后返回一個布爾值,這個值來指示是否勝利,代碼如下:</p><p> BOOL CTable::Win( int color ) const</p><p><b> {</b></p><p><b> int x, y;</b>
60、</p><p><b> // 判斷橫向</b></p><p> for ( y = 0; y < 15; y++ )</p><p><b> {</b></p><p> for ( x = 0; x < 11; x++ )</p><p><
61、;b> {</b></p><p> if ( color == m_data[x][y] &&</p><p> color == m_data[x + 1][y] &&</p><p> color == m_data[x + 2][y] &&</p><p>
62、color == m_data[x + 3][y] &&</p><p> color == m_data[x + 4][y] )</p><p><b> {</b></p><p> return TRUE;</p><p><b> }</b></p>&
63、lt;p><b> }</b></p><p><b> }</b></p><p><b> // 判斷縱向</b></p><p> for ( y = 0; y < 11; y++ )</p><p><b> {</b><
64、;/p><p> for ( x = 0; x < 15; x++ )</p><p><b> {</b></p><p> if ( color == m_data[x][y] &&</p><p> color == m_data[x][y + 1] &&</p>
65、;<p> color == m_data[x][y + 2] &&</p><p> color == m_data[x][y + 3] &&</p><p> color == m_data[x][y + 4] )</p><p><b> {</b></p><p&
66、gt; return TRUE;</p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p> // 判斷“\”方向</p><p> for ( y = 0; y <
67、; 11; y++ )</p><p><b> {</b></p><p> for ( x = 0; x < 11; x++ )</p><p><b> {</b></p><p> if ( color == m_data[x][y] &&</p>
68、<p> color == m_data[x + 1][y + 1] &&</p><p> color == m_data[x + 2][y + 2] &&</p><p> color == m_data[x + 3][y + 3] &&</p><p> color == m_data[x +
69、 4][y + 4] )</p><p><b> {</b></p><p> return TRUE;</p><p><b> }</b></p><p><b> }</b></p><p><b> }</b>&
70、lt;/p><p> // 判斷“/”方向</p><p> for ( y = 0; y < 11; y++ )</p><p><b> {</b></p><p> for ( x = 4; x < 15; x++ )</p><p><b> {</b&g
71、t;</p><p> if ( color == m_data[x][y] &&</p><p> color == m_data[x - 1][y + 1] &&</p><p> color == m_data[x - 2][y + 2] &&</p><p> color ==
72、m_data[x - 3][y + 3] &&</p><p> color == m_data[x - 4][y + 4] )</p><p><b> {</b></p><p> return TRUE;</p><p><b> }</b></p>&l
73、t;p><b> }</b></p><p><b> }</b></p><p> // 不滿足勝利條件</p><p> return FALSE;</p><p><b> }</b></p><p> 需要說明的一點是,由于這個
74、算法所遵循的搜索順序是從左到右、自上而下,因此在每次循環(huán)的時候,都有一些坐標(biāo)無需納入考慮范圍。例如對于橫向判斷而言,由于右邊界所限,因而所有橫坐標(biāo)大于等于11的點,都構(gòu)不成達(dá)到五子連的條件,所以橫坐標(biāo)的循環(huán)上界也就定為11,這樣也就提高了搜索的速度。</p><p> 6.2 人機(jī)對弈算法</p><p> 人機(jī)對弈算法完全按照CGame基類定義的接口標(biāo)準(zhǔn),封裝在了COneGame派生
75、類之中。下面將對這個算法進(jìn)行詳細(xì)地介紹。[14]</p><p> 6.2.1 獲勝組合</p><p> 獲勝組合是一個三維數(shù)組,它記錄了所有取勝的情況。也就是說,參考于CTable::Win中的情況,對于每一個落子坐標(biāo),獲勝的組合一共有</p><p> 15 * 11 * 2 + 11 * 11 * 2 = 572種。</p><p&
76、gt; 而對于每個坐標(biāo)的獲勝組合,應(yīng)該設(shè)置一個[15][15][572]大小的三維數(shù)組。</p><p> 在擁有了這些獲勝組合之后,就可以參照每個坐標(biāo)的572種組合給自己的局面和玩家的局面進(jìn)行打分,也就是根據(jù)當(dāng)前盤面中某一方所擁有的獲勝組合多少進(jìn)行權(quán)值的估算,給出最有利于自己的一步落子坐標(biāo)。</p><p> 由于是雙方對弈,所以游戲的雙方都需要一份獲勝組合,也就是:</p&
77、gt;<p> bool m_Computer[15][15][572]; // 電腦獲勝組合</p><p> bool m_Player[15][15][572]; // 玩家獲勝組合</p><p> 在每次游戲初始化(COneGame::Init)的時候,需要將每個坐標(biāo)下可能的獲勝組合都置為true。</p><p> 此外,還需要設(shè)置
78、計算機(jī)和玩家在各個獲勝組合中所填入的棋子數(shù):</p><p> int m_Win[2][572];</p><p> 在初始化的時候,將每個棋子數(shù)置為0。</p><p> 6.2.2 落子后處理</p><p> 每當(dāng)一方落子后,都需要作如下處理:</p><p> 如果己方此坐標(biāo)的獲勝組合仍為true,
79、且仍有可能在此獲勝組合處添加棋子,則將此獲勝組合添加棋子數(shù)加1;</p><p> 如果對方此坐標(biāo)的獲勝組合仍為true,則將對方此坐標(biāo)的獲勝組合置為false,并將對方此獲勝組合添加棋子數(shù)置為-1(不可能靠此組合獲勝)。</p><p> 以玩家落子為例,代碼為:</p><p> for ( i = 0; i < 572; i++ )</p&g
80、t;<p><b> {</b></p><p><b> // 修改狀態(tài)變化</b></p><p> if ( m_Player[stepPut.x][stepPut.y][i] &&</p><p> m_Win[0][i] != -1 )</p><p>
81、; m_Win[0][i]++;</p><p> if ( m_Computer[stepPut.x][stepPut.y][i] )</p><p><b> {</b></p><p> m_Computer[stepPut.x][stepPut.y][i] = false;</p><p> m_Win
82、[1][i] = -1;</p><p><b> }</b></p><p><b> }</b></p><p> 6.2.3 查找棋盤空位</p><p> 在計算機(jī)落子之前,需要查找棋盤的空位,所以需要一個SearchBlank成員函數(shù)完成此項工作,此函數(shù)需要進(jìn)行不重復(fù)的查找,也就是
83、說,對已查找過的空位進(jìn)行標(biāo)記,并返回找到空位的坐標(biāo),其代碼如下:</p><p> bool COneGame::SearchBlank( int &i, int &j,</p><p> int nowTable[][15] )</p><p><b> {</b></p><p><b&g
84、t; int x, y;</b></p><p> for ( x = 0; x < 15; x++ )</p><p><b> {</b></p><p> for ( y = 0; y < 15; y++ )</p><p><b> {</b></p&
85、gt;<p> if ( nowTable[x][y] == -1 && nowTable[x][y] != 2 )</p><p><b> {</b></p><p><b> i = x;</b></p><p><b> j = y;</b></p&
86、gt;<p> return true;</p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p> return false;</p><p><b&
87、gt; }</b></p><p> 6.2.4 落子打分</p><p> 找到空位后,需要對這個點的落子進(jìn)行打分,這個分?jǐn)?shù)也就是這個坐標(biāo)重要性的體現(xiàn),代碼如下:</p><p> int COneGame::GiveScore( const STEP& stepPut )</p><p><b>
88、{</b></p><p> int i, nScore = 0;</p><p> for ( i = 0; i < 572; i++ )</p><p><b> {</b></p><p> if ( m_pTable->GetColor() == stepPut.color )&l
89、t;/p><p><b> {</b></p><p><b> // 玩家下</b></p><p> if ( m_Player[stepPut.x][stepPut.y][i] )</p><p><b> {</b></p><p> sw
90、itch ( m_Win[0][i] )</p><p><b> {</b></p><p><b> case 1:</b></p><p> nScore -= 5;</p><p><b> break;</b></p><p><
91、b> case 2:</b></p><p> nScore -= 50;</p><p><b> break;</b></p><p><b> case 3:</b></p><p> nScore -= 500;</p><p><b
92、> break;</b></p><p><b> case 4:</b></p><p> nScore -= 5000;</p><p><b> break;</b></p><p><b> default:</b></p>&
93、lt;p><b> break;</b></p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p><b> else</b></p>
94、<p><b> {</b></p><p><b> // 計算機(jī)下</b></p><p> if ( m_Computer[stepPut.x][stepPut.y][i] )</p><p><b> {</b></p><p> switch
95、( m_Win[1][i] )</p><p><b> {</b></p><p><b> case 1:</b></p><p> nScore += 5;</p><p><b> break;</b></p><p><b>
96、 case 2:</b></p><p> nScore += 50;</p><p><b> break;</b></p><p><b> case 3:</b></p><p> nScore += 100;</p><p><b>
97、 break;</b></p><p><b> case 4:</b></p><p> nScore += 10000;</p><p><b> break;</b></p><p><b> default:</b></p><p
98、><b> break;</b></p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p><b> }</b></p><p
99、> return nScore;</p><p><b> }</b></p><p> 如代碼所示,考慮到攻守兩方面的需要,所以將玩家落子給的分?jǐn)?shù)置為負(fù)值。</p><p> 6.2.5 防守策略</p><p> 落子的考慮不單單要從進(jìn)攻考慮,還要從防守考慮。這一細(xì)節(jié)的實現(xiàn)其實就是讓計算機(jī)從玩家棋盤布
100、局分析戰(zhàn)況,然后找出對玩家最有利的落子位置。整個過程如下:</p><p> for ( m = 0; m < 572; m++ )</p><p><b> {</b></p><p> // 暫時更改玩家信息</p><p> if ( m_Player[i][j][m] )</p>&l
101、t;p><b> {</b></p><p> temp1[n] = m;</p><p> m_Player[i][j][m] = false;</p><p> temp2[n] = m_Win[0][m];</p><p> m_Win[0][m] = -1;</p><p>
102、;<b> n++;</b></p><p><b> }</b></p><p><b> }</b></p><p> ptempTable[i][j] = 0;</p><p><b> pi = i;</b></p><
103、;p><b> pj = j;</b></p><p> while ( SearchBlank( i, j, ptempTable ) )</p><p><b> {</b></p><p> ptempTable[i][j] = 2; // 標(biāo)記已被查找</p><p> st
104、ep.color = m_pTable->GetColor();</p><p> step.x = i;</p><p> step.y = j;</p><p> ptemp = GiveScore( step );</p><p> if ( pscore > ptemp ) // 此時為玩家下子,運(yùn)用極小極大法時應(yīng)
105、選取最小值</p><p> pscore = ptemp;</p><p><b> }</b></p><p> for ( m = 0; m < n; m++ )</p><p><b> {</b></p><p><b> // 恢復(fù)玩家信
106、息</b></p><p> m_Player[pi][pj][temp1[m]] = true;</p><p> m_Win[0][temp1[m]] = temp2[m];</p><p><b> }</b></p><p> 6.2.6 選取最佳落子</p><p>
107、 在循環(huán)結(jié)束的時候,就可以根據(jù)攻、守兩方面的打分綜合地考慮落子位置了。代碼如下:</p><p> if ( ctemp + pscore > cscore )</p><p><b> {</b></p><p> cscore = ctemp + pscore;</p><p> bestx = pi
108、;</p><p> besty = pj;</p><p><b> }</b></p><p> 在這之后,重新改變一下棋盤的狀態(tài)(6.2.2)即可。</p><p><b> 7 幾點補(bǔ)充說明</b></p><p> 考慮到程序的響應(yīng)速度,人機(jī)對弈算法只對玩
109、家的棋子進(jìn)行了一步的推測。</p><p> 由于計算機(jī)在落子時選取的是得分最高的一步落子,所以如果玩家在開局的時候不改變落子步驟,那么將會獲得從頭至尾相同的棋局。</p><p> 考慮到下棋同時還要聊天,所以并未對落子時間加入任何限制,同樣如果玩家離開游戲也不會判負(fù)。</p><p> 對于人機(jī)對弈的悔棋處理,由于這個算法的開銷相當(dāng)大,每一步落子都會存在不
110、同的棋盤布局,所以實現(xiàn)從頭到尾的悔棋不是很現(xiàn)實(將會存在過多的空間保存棋盤布局),因而在人機(jī)對弈模式下,只允許玩家悔最近的兩步落子。</p><p><b> 8 心得體會</b></p><p> 通過編寫這個程序,我體會最為深刻的一點是系統(tǒng)架構(gòu)和設(shè)計模式的重要性。即使是對于一個并不大的程序,代碼的組織都是非常重要的,因為這關(guān)系到日后的維護(hù)以及擴(kuò)展。這個游戲之中
111、,有關(guān)網(wǎng)絡(luò)Socket編程或者博弈樹算法的知識都可以直接從無所不包的Internet上獲取,甚至可以直接獲得一個完整的五子棋人機(jī)對弈算法的源代碼級模塊。但是對于系統(tǒng)的架構(gòu),卻完全是自己的事情,幾千上萬行的代碼需要通過合適的方法組織起來,使程序員編寫代碼更加有條理,更加符合軟件工程的標(biāo)準(zhǔn),這才是最重要的。</p><p> 在剛開始編寫這個程序的時候,我幼稚地認(rèn)為其中最重要的是博弈樹算法。但是頭一個月編寫程序的時
112、候卻發(fā)現(xiàn)程序越寫越不容易維護(hù),可見是我走錯了方向。后來我向公司真正的軟件設(shè)計人員及系統(tǒng)架構(gòu)師討教,他們告訴我:我們的先人早已為我們準(zhǔn)備好了各種精良可用的現(xiàn)成算法,我們所要做的就是直接“拿來主義”罷了;但是對于代碼的組織(也就是軟件的架構(gòu))才是真正軟件工業(yè)的核心部分,因為軟件事實上是直接和經(jīng)濟(jì)掛鉤的,因此我們必須在編寫代碼之前選擇一種最為合適的方法來組織這些代碼,否則我們將會失去更多的時間和金錢。[15]</p><p
113、> 于是,我將以前寫的代碼全部刪除,認(rèn)真地思考了三天的時間。我也在這三天內(nèi)真正從一個學(xué)生程序員走入了軟件開發(fā)的大門,我開始發(fā)現(xiàn)其實軟件開發(fā)并不是純數(shù)學(xué)——正相反,數(shù)學(xué)只占了很小的一部分。它其實是一種哲學(xué),一種有著數(shù)學(xué)美感的哲學(xué)。</p><p><b> 參考文獻(xiàn)</b></p><p> MSDN for Visual Studio 6.0</p&
114、gt;<p> 設(shè)計模式——可復(fù)用面向?qū)ο筌浖幕A(chǔ),Erich Gamma/Richard Helm/Ralph Johnson/John Vlissides著,李英軍/馬曉星/蔡敏/劉建中 等譯,機(jī)械工業(yè)出版社</p><p> 深入淺出MFC(第2版),侯俊杰著,華中科技大學(xué)出版社</p><p> Microsoft® Visual C++.NET 技
115、術(shù)內(nèi)幕(第6版),George Shepherd/David Kruglinski著,潘愛民譯,清華大學(xué)出版社</p><p> Visual C++網(wǎng)絡(luò)通信協(xié)議分析與應(yīng)用實現(xiàn),汪曉平/鐘軍 等編著,人民郵電出版社</p><p> C++編程思想,Bruce Eckel著,劉宗田/邢大紅/孫慧杰 等譯,機(jī)械工業(yè)出版社</p><p> 21天學(xué)通C++,Je
116、sse Liberty著,康博創(chuàng)作室譯,人民郵電出版社</p><p> C++標(biāo)準(zhǔn)程序庫,Nicolai M.Josuttis著,侯捷/孟巖 譯,華中科技大學(xué)出版社</p><p> Windows程序設(shè)計,Charles Petzold著,北京博彥科技發(fā)展有限公司譯,北京大學(xué)出版社</p><p> Visual C++.NET網(wǎng)絡(luò)編程,易君 編著,中國鐵
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 五子棋游戲畢業(yè)設(shè)計
- 畢業(yè)設(shè)計--五子棋人機(jī)對弈
- flash五子棋畢業(yè)設(shè)計論文
- java五子棋畢業(yè)設(shè)計論文
- 畢業(yè)設(shè)計---網(wǎng)絡(luò)五子棋游戲
- 網(wǎng)絡(luò)五子棋五子棋設(shè)計與實現(xiàn).doc
- java五子棋畢業(yè)設(shè)計--java五子棋對弈程序的設(shè)計與實現(xiàn)
- 網(wǎng)絡(luò)五子棋游戲畢業(yè)設(shè)計
- java五子棋畢業(yè)設(shè)計(整套)
- 五子棋對弈系統(tǒng)設(shè)計畢業(yè)設(shè)計
- 畢業(yè)設(shè)計--五子棋程序設(shè)計
- qt網(wǎng)絡(luò)五子棋五子棋設(shè)計與實現(xiàn)
- 五子棋畢業(yè)論文-html開發(fā)五子棋的原型設(shè)計
- 人機(jī)對弈五子棋游戲畢業(yè)設(shè)計
- 五子棋畢業(yè)論文
- 五子棋項目
- 五子棋游戲設(shè)計畢業(yè)論文
- 五子棋棋譜
- 五子棋.1
- 五子棋游戲設(shè)計報告
評論
0/150
提交評論