範例執行程式 (Random Player), 範例執行程式 (Smart Player) by legend,
請注意:
解壓縮後有一個 Fantan.exe 和一個 images\ 資料匣 裡面放的是所需要的影像檔案, 相對的關係要正確, 否則程式會找不到影像而用自己合成的圖片來執行第一個程式中電腦扮演的三個玩家是用亂數來決定出牌的
第二個程式中電腦扮演的三個玩家都是用對自己比較有利的方式來出牌的
這個接龍程式是大家常常四個人一起玩的遊戲, 並不是 Microsoft 那個一個人玩的接龍遊戲, 首先把所有的牌通通發完, 一人 13 張牌, 手上有黑桃七的人先出, 其他的人可以接在已經出的牌之後, 或是可以出紅心七, 磚塊七, 和梅花七。如果手上沒有任何牌可以出的話, 可以蓋一張牌, 這張蓋住的牌以後就不能再出了, 同時其它三個人都不曉得你蓋了什麼牌, 這個遊戲一直完到所有手上 13 張牌都出完了才結束, 然後計算每個人所蓋的牌的總點數, 點數最少的是贏家
virtual int dealCards() { return 0;} virtual bool userChoose(Card card) { return true;} virtual bool player0Move() {return true;} virtual bool player1Move() {return true;} virtual bool player2Move() {return true;}
TRACE("int CTestGame::dealCards()\n");並且以 dbwin32 應用程式來看到這個結果
m_pGO->setHand(int id, Card cards[]);在畫面上顯示每一個玩家所拿到的牌 (m_pGO 是一個 CActionHandler 類別裡的指標成員變數, 你的類別繼承了 CActionHandler, 所以可以直接用到, 這個指標變數指向一個提供視窗輸入輸出的物件, 這個物件提供例如上面這個 setHand() 的函式來設定每一個人手上的牌, 你只要給一個 id 和一個 13 張牌的陣列, 就可以把這些牌顯示在視窗界面中), 例如:
int i; Card card[4][13]; for (i=0; i<13; i++) { card[0][i] = Card(Spade, i+1); card[1][i] = Card(Heart, i+1); card[2][i] = Card(Diamond, i+1); card[3][i] = Card(Club, i+1); } for (i=0; i<4; i++) m_pGO->setHand(i, card[i]);注意你需要 include GraphicOutput.h 和 Card.h
每一張牌的資料結構請運用在 Card.h 中定義好的 Card 類別, 上面函式的第一個參數 id 代表四個玩家, 在畫面左手邊的 id 是 0, 上方的玩家 id 是 1, 右方的玩家 id 是 2, 下方 (真正操作這個應用程式的使用者) id 是 3
請注意當你在畫面上顯示時預設只有下方的玩家的牌是看得到的, 其它三個人的牌都是蓋住的
如果你在測試程式時希望看到所有發出去牌的內容的話, 你可以在程式一開始的時候先呼叫 m_pGO->openAllCards();
CActionHandler::dealCards()
在這個接龍的遊戲中我們應該要由有黑桃七(Spade 7) 的玩家開始出牌, 第一張牌也必須要是黑桃七,
因為在 dealCards() 函式中你自己寫的程式發玩牌了, 所以你應該可以測試出來哪一個玩家有黑桃七, 然後把這個玩家 的 id 值傳回去, 如此 UI 才知道下面該如何進行遊戲
bool CTestGame::userChoose(Card card) { TRACE("CTestGame::userChoose(Card card): suite=%d, face=%d\n", card.suite, card.face); return true; }
bool CTestGame::player0Move() { TRACE("CTestGame::player0Move()\n"); return true; }
bool CTestGame::player1Move() { TRACE("CTestGame::player1Move()\n"); return true; }
bool CTestGame::player2Move() { TRACE("CTestGame::player2Move()\n"); return true; }
上面四個函式的回傳值事實上控制著 UI 如何繼續進行遊戲, 其中 playerXMove() 函式的回傳值如果是 true 的話, UI 會繼續依照順時針方向呼叫下一個 playerXMove() 函式,
userChoose(Card) 函式的傳回值如果是 true 的話, UI 會繼續呼叫 player0Move() 函式, 如果是 false 的話, 代表你發現使用者挑錯牌了, (例如出的牌不能夠接上, 或是 該出黑桃七時不出), 你希望 UI 停下來讓使用者繼續挑選牌
第一種是把牌接在四堆牌中適當的地方, 第二種是蓋牌 (就是當玩家沒有牌可以出時把某一張牌扣住的動作)
你可以呼叫 CGraphicOutput 類別提供的 addCenter(Card) 來把所出的牌接在中間四堆牌中, 不過在呼叫之前應該先呼叫 CGraphicOutput 類別提供的 removeCard(int id, Card card) 從玩家手上的牌中移除, 因為我們知道在這個範例中 Spade 7 在左方玩家, 所以我們在 CTestGame::player0Move() 中加入
m_pGO->removeCard(0, Card(Spade, 7)); // 由玩家手中移除 m_pGO->addCenter(Card(Spade, 7)); // 接在中央牌堆中來顯示這張牌, 這兩個界面函式如果成功的話都會回傳 true, 你可以透過傳回值判斷你自己程式內的資料和 UI 內的資料是不是一致
ASSERT(m_pGO->discardCard(1, Card(Heart, 7)));來蓋住一張牌, 同樣地如果蓋牌的動作成功的話, UI 也會傳回 true, 你可以透過傳回值判斷你自己的程式和 UI 內的資料是不是一致
CGraphicOutput::gameOver(string gameOverMessage)將輸贏訊息傳入, 並且 return false 使得 UI 不再繼續呼叫 player0Move(), player1Move() 或是 player2Move()
在你呼叫過 gameOver() 之後, UI 會顯示一個對話視窗來顯示你傳入的訊息, 同時也會把所有蓋住的牌翻開, 如此使用者可以檢查遊戲的結果
原則上你想要增加什麼功能都是可以的, 記得在心得中寫出來, 這樣我才會注意到, 也才能夠幫你加一些分數, 現在程式還簡單, 你應該有辦法增加功能或是基於這個類別做一些好玩的應用。
回
C++ 程式設計課程
首頁
製作日期: 4/16/2004
by 丁培毅 (Pei-yih Ting)
E-mail: [email protected]
TEL: 02 24622192x6615
海洋大學
工學院
資訊工程系
Lagoon