標籤:
上一篇文章對玩家手中的牌進行分析歸類,下面就該實現電腦玩家出牌與跟牌的策略了。首先我們來看看出牌的策略,代碼如下:
void GameScene::update(float delta){ switch (m_iState) { case 0: SendPk(); break; case 1: schedule(schedule_selector(GameScene::Call),1); break; case 2: scheduleOnce(schedule_selector(GameScene::OutCard),0.5); break; case 3: IsShengLi(); break; default: break; }}
首先解釋下該函數,本函數為一個迴圈,每幀被調用一次。我們看一下標頭檔裡m_iState的注釋:
int m_iState;//目前狀態 ,0:發牌狀態 1:叫地主狀態 2:出牌狀態 3:結果狀態
很明顯,出牌和跟牌策略就在狀態2,該函數延時0.5秒出牌。我們接下來看下OutCard函數的策略:
void GameScene::OutCard(float delta){ switch (m_iOutCard%3) { case 0: m_chuPaiMenu->setVisible(true);//顯示出牌菜單,包括”不出“,”出牌“ m_typeTem = PaiDuanPaiXing();//獲得玩家出的牌的牌型,這個函數在cocos2dx《單機鬥地主》源碼解剖之六 玩家(人)的出牌中有解釋。 if(!m_npcOne->getIsOutPk() && !m_npcTwo->getIsOutPk())//如果兩個電腦玩家沒出過牌,設”不出“按鈕不可點,反應則然。 ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(0))->setEnabled(false); else ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(0))->setEnabled(true); //出牌 if(!m_npcOne->getIsOutPk() && !m_npcTwo->getIsOutPk()) { //清除所有出的牌 ClearOutPk();//下面貼代碼 if (m_typeTem != ERROR_CARD)//ERROR_CARD為錯誤的牌型 ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(1))->setEnabled(true); else ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(1))->setEnabled(false); } else //跟牌 { if(m_arrPlayerOut->count() != 0) { Poker* pk = (Poker*)m_arrGenPk->objectAtIndex(0);//要跟的牌 Poker* pk1 = (Poker*)m_arrPlayerOut->objectAtIndex(0);//玩家出的牌 if(m_typeTem == m_type && pk1->getNum()>pk->getNum() || (m_typeTem==BOMB_CARD && m_type!=BOMB_CARD))//m_type為跟的牌的牌型 ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(1))->setEnabled(true); else ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(1))->setEnabled(false); } else ((CCMenuItemFont *)m_chuPaiMenu->getChildByTag(1))->setEnabled(false); } break; case 1: m_chuPaiMenu->setVisible(false); if(!m_player->getIsOutPk() && !m_npcOne->getIsOutPk()) { //清除所有出的牌 ClearOutPk(); NpcOutPoker(m_npcTwo,m_arrGenPk,m_npcTwoOut);//電腦出牌策略,函數下面解釋。 } else NpcGenPoker(m_npcTwo,m_arrGenPk ,m_npcTwoOut);//電腦跟牌策略,函數下面解釋。 PlayerOutPaiXu(m_arrGenPk);//對要跟的牌進行排序,該函數在cocos2dx《單機鬥地主》源碼解剖之六 玩家(人)的出牌有解釋。 PlayerOutPaiXu(m_npcTwoOut->getArrPk());//對電腦玩家出的牌進行排序 m_npcTwoOut->updatePkWeiZhi();//更新位置 m_npcTwo->updatePkWeiZhi();//同上 ++m_iOutCard; if(IsOutPkFinish())//判斷遊戲是否結束,下面解釋。 m_iState = 3; break; case 2: if(!m_player->getIsOutPk() && !m_npcTwo->getIsOutPk()) { //清除所有出的牌 ClearOutPk(); NpcOutPoker(m_npcOne,m_arrGenPk,m_npcOneOut); } else NpcGenPoker(m_npcOne,m_arrGenPk,m_npcOneOut); PlayerOutPaiXu(m_arrGenPk); PlayerOutPaiXu(m_npcTwoOut->getArrPk()); m_npcOneOut->updatePkWeiZhi(); m_npcOne->updatePkWeiZhi(); ++m_iOutCard; if(IsOutPkFinish()) m_iState = 3; break; default: break; }}
首先介紹一下這個狀態機器,我們看標頭檔對m_iOutCard變數的定義:int m_iOutCard;//論到誰出牌,0為玩家出牌與跟牌的策略,1和2為電腦玩家出牌與跟牌的策略。他們的意義已在代碼裡添加註釋。
在上面代碼中你一定發現了有些令人費解的函數(ClearOutPk(),NpcOutPoker(m_npcTwo,m_arrGenPk,m_npcTwoOut),NpcGenPoker(m_npcTwo,m_arrGenPk ,m_npcTwoOut),IsOutPkFinish()),下面一一解釋:
ClearOutPk()的代碼:
void GameScene::ClearOutPk(){ CCObject* object;
//清除玩家出的牌
CCARRAY_FOREACH(m_playerOut->getArrPk(),object){ Poker* pk = (Poker*)object; pk->setVisible(false); } m_playerOut->getArrPk()->removeAllObjects();
//清除電腦玩家出的牌 CCARRAY_FOREACH(m_npcTwoOut->getArrPk(),object){ Poker* pk = (Poker*)object; pk->setVisible(false); } m_npcTwoOut->getArrPk()->removeAllObjects();
//同上 CCARRAY_FOREACH(m_npcOneOut->getArrPk(),object){ Poker* pk = (Poker*)object; pk->setVisible(false); } m_npcOneOut->getArrPk()->removeAllObjects(); this->getChildByTag(NpcOneBuChu)->setVisible(false); this->getChildByTag(NpcTwoBuChu)->setVisible(false);}
NpcOutPoker(m_npcTwo,m_arrGenPk,m_npcTwoOut) 電腦出牌策略:
電腦出牌策略我這裡只是簡單的判斷,打出排在第一位置牌值最小的牌型。
void GameScene::NpcOutPoker(Player* npc,CCArray* out,Player* out1){ //隱藏上一次出的牌 CCObject* object; CCARRAY_FOREACH(out1->getArrPk(),object){ //out1為上一次出的牌 Poker* pk = (Poker*)object; pk->setVisible(false); } out1->getArrPk()->removeAllObjects(); //打出牌值最小的一個牌型,也就是排在第一位置的牌型 PaiXing px = npc->m_vecPX.front(); out->removeAllObjects(); //三條出牌原則 if(px.type == THREE_CARD){ stable_sort(npc->m_vecPX.begin(),npc->m_vecPX.end(),isShorter1); m_type = THREE_CARD; //帶單 for(std::vector<PaiXing>::iterator iter=npc->m_vecPX.begin();iter!=npc->m_vecPX.end();++iter) { //除非只剩兩手牌,否則不能帶王和2 Poker* pk = iter->vec.front(); if(pk->getNum() >= Er && npc->m_vecPX.size() != 1) break; if(iter->type == SINGLE_CARD) { out1->getArrPk()->addObject(iter->vec.front()); out->addObject(iter->vec.front()); npc->getArrPk()->removeObject(iter->vec.front()); npc->m_vecPX.erase(iter); m_type = THREE_ONE_CARD; break; } } //帶雙 if(out1->getArrPk()->count() == 0) { for(std::vector<PaiXing>::iterator iter=npc->m_vecPX.begin();iter!=npc->m_vecPX.end();++iter) { //除非只剩兩手牌,否則不能帶王和2 Poker* pk = iter->vec.front(); if(pk->getNum() >= Er && npc->m_vecPX.size() != 1) break; if(iter->type == DOUBLE_CARD) { for(std::vector<Poker*>::iterator it=iter->vec.begin();it!=iter->vec.end();++it) { out1->getArrPk()->addObject(*it); out->addObject(*it); npc->getArrPk()->removeObject(*it); } npc->m_vecPX.erase(iter); m_type = THREE_TWO_CARD; break; } } } } //三順出牌原則 if(px.type == AIRCRAFT_CARD){ //有足夠的單就帶單 stable_sort(npc->m_vecPX.begin(),npc->m_vecPX.end(),isShorter1); m_type = AIRCRAFT_CARD; if(GetNpcPxNum(npc,SINGLE_CARD) >= px.vec.size()/3) { int num=0; for (std::vector<PaiXing>::iterator it=npc->m_vecPX.begin(); it!=npc->m_vecPX.end()&&num<px.vec.size()/3;) { if(it->type == SINGLE_CARD) { ++num; out1->getArrPk()->addObject(it->vec.front()); out->addObject(it->vec.front()); npc->getArrPk()->removeObject(it->vec.front()); it = npc->m_vecPX.erase(it); m_type = AIRCRAFT_SINGLE_CARD; } else ++it; } } //有足夠的雙就帶雙 if(GetNpcPxNum(npc,DOUBLE_CARD) >= px.vec.size()/3 && out1->getArrPk()->count() == 0) { int num=0; for (std::vector<PaiXing>::iterator it=npc->m_vecPX.begin(); it!=npc->m_vecPX.end()&&num<px.vec.size()/3;) { if(it->type == DOUBLE_CARD) { ++num; for(std::vector<Poker*>::iterator ite=it->vec.begin(); ite!=it->vec.end(); ++ite) { out1->getArrPk()->addObject(*ite); out->addObject(*ite); npc->getArrPk()->removeObject(*ite); m_type = AIRCRAFT_DOBULE_CARD; } it = npc->m_vecPX.erase(it); } else ++it; } } } //連牌出牌原則,直接出,不做處理 if(px.type == CONNECT_CARD){ m_type = CONNECT_CARD; } //雙順出牌原則,直接出,不做處理 if(px.type == COMPANY_CARD){ m_type = COMPANY_CARD; } //對子和單子出牌原則 if(px.type == DOUBLE_CARD || px.type == SINGLE_CARD){ int threeNum = GetNpcPxNum(npc,THREE_CARD)+GetNpcPxNum(npc,AIRCRAFT_CARD); int chiBangNum = GetNpcPxNum(npc,DOUBLE_CARD)+GetNpcPxNum(npc,SINGLE_CARD); //所有三條<=所有對子+所有單牌-2,出對子,否則出三帶對 if(threeNum <= chiBangNum-2 || threeNum == 0) { if(px.type == DOUBLE_CARD) m_type = DOUBLE_CARD; if(px.type == SINGLE_CARD) m_type = SINGLE_CARD; } else { PaiXing px = npc->m_vecPX.front(); std::vector<PaiXing>::iterator dle = npc->m_vecPX.begin(); npc->m_vecPX.erase(dle); npc->m_vecPX.push_back(px); NpcOutPoker(npc,out,out1); return; } } for(std::vector<Poker*>::iterator iter=px.vec.begin(); iter!=px.vec.end(); ++iter) { out1->getArrPk()->addObject(*iter); out->addObject(*iter); npc->getArrPk()->removeObject(*iter); npc->setIsOutPk(true); } m_lastOut = npc; //從npc->m_vecPX中移除px for(std::vector<PaiXing>::iterator it=npc->m_vecPX.begin();it!=npc->m_vecPX.end();++it) { if(it->type == px.type && it->vec.front()->getNum() == px.vec.front()->getNum()) { npc->m_vecPX.erase(it); break; } }}
NpcGenPoker(m_npcTwo,m_arrGenPk ,m_npcTwoOut),IsOutPkFinish())跟牌策略:
void GameScene::NpcGenPoker(Player* npc,CCArray* out ,Player* out1){ //隱藏上一次出的牌 if(m_isChiBang) { CCObject* object; CCARRAY_FOREACH(out1->getArrPk(),object){ Poker* pk = (Poker*)object; pk->setVisible(false); } out1->getArrPk()->removeAllObjects(); } /************************************************************************/ /*找出對應牌型出牌 */ /************************************************************************/ for (std::vector<PaiXing>::iterator iter=npc->m_vecPX.begin(); iter!=npc->m_vecPX.end(); ++iter) { if(m_type == iter->type) { //對飛機、連牌進行判斷 if(m_type == AIRCRAFT_CARD || m_type == CONNECT_CARD || m_type == COMPANY_CARD) if(out->count() != iter->vec.size()) continue; Poker* pk = (Poker*)out->objectAtIndex(out->count()-1); Poker* pk1 = iter->vec.front(); //如果對方是自己人大於2的牌不出 if(!npc->getIsDiZhu() && !m_lastOut->getIsDiZhu()) { if(pk1->getNum()>=Er || m_type == BOMB_CARD) { //pass if(npc == m_npcOne) this->getChildByTag(NpcOneBuChu)->setVisible(true); if(npc == m_npcTwo) this->getChildByTag(NpcTwoBuChu)->setVisible(true); npc->setIsOutPk(false); return; } } if(pk1->getNum() > pk->getNum()) { out->removeAllObjects(); for(std::vector<Poker*>::iterator it = iter->vec.begin(); it!=iter->vec.end(); ++it){ out1->getArrPk()->addObject(*it); npc->getArrPk()->removeObject(*it); out->addObject(*it); } npc->m_vecPX.erase(iter); npc->setIsOutPk(true); m_lastOut = npc; return; } } } //三帶一或三帶二 if(SanDaiYiOrEr(npc,out,out1)) return; //四帶單或四帶雙 //飛機帶單或帶雙 if(FeiJiDaiChiBang(npc,out,out1)) return; /************************************************************************/ /*如果除炸彈還剩一手牌 */ /************************************************************************/ if(npc->m_vecPX.size() == 2) { for (std::vector<PaiXing>::iterator iter=npc->m_vecPX.begin(); iter!=npc->m_vecPX.end(); ++iter) { if(iter->type == BOMB_CARD && m_type != BOMB_CARD) { out->removeAllObjects(); for(std::vector<Poker*>::iterator it = iter->vec.begin(); it!=iter->vec.end(); ++it){ out1->getArrPk()->addObject(*it); npc->getArrPk()->removeObject(*it); out->addObject(*it); } npc->m_vecPX.erase(iter); m_lastOut = npc; return; } } } /************************************************************************/ /* 如果出牌方是自己人不拆牌跟 */ /************************************************************************/ if(!npc->getIsDiZhu() && !m_lastOut->getIsDiZhu()) { //pass if(npc == m_npcOne) this->getChildByTag(NpcOneBuChu)->setVisible(true); if(npc == m_npcTwo) this->getChildByTag(NpcTwoBuChu)->setVisible(true); npc->setIsOutPk(false); return; } /************************************************************************/ /*拆單張牌跟之 */ /************************************************************************/ if(NpcChaiDan(npc,out,out1)) return; /************************************************************************/ /*拆雙牌跟之 */ /************************************************************************/ if(NpcChaiDui(npc,out,out1)) return; /************************************************************************/ /*拆三張牌跟之 */ /************************************************************************/ if(NpcChaiSan(npc,out,out1)) return; /************************************************************************/ /*拆飛機牌跟之 */ /************************************************************************/ if(NpcChaiFeiJi(npc,out,out1)) return; /************************************************************************/ /*拆連牌跟之 */ /************************************************************************/ if(NpcChaiLianPai(npc,out,out1)) return; /************************************************************************/ /*拆雙順跟之 */ /************************************************************************/ if(NpcChaiShuangShun(npc,out,out1)) return; //炸之 for (std::vector<PaiXing>::iterator iter=npc->m_vecPX.begin(); iter!=npc->m_vecPX.end(); ++iter) { if(iter->type == BOMB_CARD) { //如果出牌方出的不是炸彈就炸之,否則比較大小炸之 if(m_type != BOMB_CARD) { out->removeAllObjects(); for(std::vector<Poker*>::iterator it = iter->vec.begin(); it!=iter->vec.end(); ++it){ out1->getArrPk()->addObject(*it); npc->getArrPk()->removeObject(*it); out->addObject(*it); } npc->m_vecPX.erase(iter); m_type = BOMB_CARD; npc->setIsOutPk(true); m_lastOut = npc; return; }else { Poker* pk = (Poker*)out->objectAtIndex(0); Poker* pk1 = iter->vec.front(); if(pk1->getNum()>pk->getNum()) { out->removeAllObjects(); for(std::vector<Poker*>::iterator it = iter->vec.begin(); it!=iter->vec.end(); ++it){ out1->getArrPk()->addObject(*it); npc->getArrPk()->removeObject(*it); out->addObject(*it); } npc->m_vecPX.erase(iter); m_type = BOMB_CARD; npc->setIsOutPk(true); m_lastOut = npc; return; } } } } //pass if(npc == m_npcOne) { this->getChildByTag(NpcOneBuChu)->setVisible(true); } if(npc == m_npcTwo) { this->getChildByTag(NpcTwoBuChu)->setVisible(true); } npc->setIsOutPk(false);}
其中代碼就不一一分析了,請自行到前三章下載源碼閱讀。本文章到此結束了!感謝大家的支援!!!
cocos2dx《單機鬥地主》源碼解剖之八 電腦玩家出牌與跟牌(結束)