Cocos2d-x制作《单机斗地主》源码解剖5:玩家的出牌

当游戏玩家选择好要出的牌以后,首先需要判断该牌型是否符合游戏规则,即合法。如果合法就高亮显示出“出牌”按钮,否则显示灰色“出牌”按钮,表示不可出。怎样判断玩家所选择的牌型是否合法呢?请看下面的代码:

int GameScene::PaiDuanPaiXing(){  
    //对出的牌进行排序  
    PlayerOutPaiXu(m_arrPlayerOut);  
    //牌型判断  
    int lengh = m_arrPlayerOut->count();  
    PaiXing px;  
    //牌的张数少于5张类型判断 单,对,三张,四张  
    if(lengh<5 && lengh>0){  
        Poker* pk = (Poker *)m_arrPlayerOut->objectAtIndex(0);  
        Poker* pk1 = (Poker *)m_arrPlayerOut->objectAtIndex(lengh-1);  
        if(pk->getNum() == pk1->getNum())  
            return lengh;  
        //三带一  
        pk1 = (Poker *)m_arrPlayerOut->objectAtIndex(lengh-2);  
        if(pk->getNum() == pk1->getNum() && lengh == 4)  
            return THREE_ONE_CARD;  
        //双鬼  
        if(pk->getHuaSe()==Gui && pk1->getHuaSe()==Gui)  
            return BOMB_CARD;  
    }  
    //牌的张数大于等于5张的类型判断  
    if(lengh>=5)  
    {  
        //是否为连牌牌型(单)  
        if(IsLianPai())  
            return CONNECT_CARD;  
        if(IsLianDui())   //判断连对  
            return COMPANY_CARD;  
        //判断飞机类型  
        return IsFeiJi();  
    }  
    return ERROR_CARD;  
}

你一定注意到上面的PlayerOutPaiXu(m_arrPlayerOut);这个函数了,它的作用正是注释所说对出的牌进行排序,以方便分析它的牌型,m_arrPlayerOut是玩家选出的牌。先看以下代码:

void GameScene::PlayerOutPaiXu(CCArray* m_arrPlayerOut){  
    //对出的牌进行分离  
    std::vector<JiShu> vec;//JiShu是一个结构体,下面显示代码  
    while(m_arrPlayerOut->count() > 0)  
    {  
        JiShu js;  
        js.arr = CCArray::create();  
        //取出第一个  
        Poker* pk = (Poker*)m_arrPlayerOut->objectAtIndex(0);  
        m_arrPlayerOut->removeObjectAtIndex(0);  
        js.num = 1;  
        js.pkZhi = pk->getNum();  
        js.arr->addObject(pk);  
        //找出与第一个相同的牌  
        int i=0;  
        while (i<m_arrPlayerOut->count())  
        {  
            Poker* pk1 = (Poker*)m_arrPlayerOut->objectAtIndex(i++);  
            if(pk1->getNum() == pk->getNum())  
            {  
                ++js.num;  
                js.arr->addObject(pk1);  
                m_arrPlayerOut->removeObject(pk1);  
                --i;  
            }  
        }  
        //把js存储起来用于排序  
        vec.push_back(js);  
    }  
    //对vec进行排序,按牌值从小到大排序  
    for(int i=0; i<vec.size()-1 && !vec.empty(); ++i){  
        for(int j=0; j<vec.size()-i-1; ++j){  
            if(vec[j].pkZhi > vec[j+1].pkZhi)  
            {  
                JiShu temp = vec[j];  
                vec[j] = vec[j+1];  
                vec[j+1] = temp;  
            }  
        }  
    }                     
    stable_sort(vec.begin(),vec.end(),isShorter);//按牌的数量从小到大再排一次  
    //将排序好的牌重新放入m_playerOut中  
    for(std::vector<JiShu>::iterator it = vec.begin(); it!=vec.end(); ++it){  
        m_arrPlayerOut->addObjectsFromArray(it->arr);  
    }  
}
//记数 排序出的牌用  
struct JiShu  
{  
    int pkZhi;//牌值  
    int num; //牌数量  
    CCArray* arr; //集合牌  
};

       上面综合思想是:判断出的牌“m_arrPlayerOut”里有几个相同的牌并通过JiShu结构体记录下来并保存在std::vector<JiShu> vec中,然后按他们牌值和相同牌的数量进行一次排序,然后再把排序好的牌一个一个放回m_arrPlayerOut中去,这样就会方便以后用来分析牌型了。打个比方:比如出的牌 66633,经过排序会变成33666, 665543经过排序变成345566.

那么上面代码中return BOMB_CARD;  return THREE_ONE_CARD;是指什么呢,他们是一个枚举变量,分别代表一个牌型,请看下面代码:

//斗地主共有13种牌型  
enum CARD_TYPE  
{  
    SINGLE_CARD = 1,        //单牌-  
    DOUBLE_CARD,            //对子-  
    THREE_CARD,             //3不带-  
    BOMB_CARD,              //炸弹  
    THREE_ONE_CARD,         //3带1-  
    THREE_TWO_CARD,         //3带2-  
    BOMB_TWO_CARD,          //四个带2张单牌  
    BOMB_TWOOO_CARD,        //四个带2对  
    CONNECT_CARD,           //连牌-  
    COMPANY_CARD,           //连队-  
    AIRCRAFT_CARD,          //飞机不带-  
    AIRCRAFT_SINGLE_CARD,   //飞机带单牌-  
    AIRCRAFT_DOBULE_CARD,   //飞机带对子-  
    ERROR_CARD              //错误的牌型  
} ;

       下面分析//牌的张数大于等于5张的类型判断,原代码如下:

int GameScene::PaiDuanPaiXing(){    
    //对出的牌进行排序    
    PlayerOutPaiXu(m_arrPlayerOut);    
    //牌型判断    
    int lengh = m_arrPlayerOut->count();    
    PaiXing px;    
    //牌的张数少于5张类型判断 单,对,三张,四张    
    if(lengh<5 && lengh>0){    
        Poker* pk = (Poker *)m_arrPlayerOut->objectAtIndex(0);    
        Poker* pk1 = (Poker *)m_arrPlayerOut->objectAtIndex(lengh-1);    
        if(pk->getNum() == pk1->getNum())    
            return lengh;    
        //三带一    
        pk1 = (Poker *)m_arrPlayerOut->objectAtIndex(lengh-2);    
        if(pk->getNum() == pk1->getNum() && lengh == 4)    
            return THREE_ONE_CARD;    
        //双鬼    
        if(pk->getHuaSe()==Gui && pk1->getHuaSe()==Gui)    
            return BOMB_CARD;    
    }    
    //牌的张数大于等于5张的类型判断    
    if(lengh>=5)    
    {    
        //是否为连牌牌型(单)    
        if(IsLianPai())    
            return CONNECT_CARD;    
        if(IsLianDui())   //判断连对    
            return COMPANY_CARD;    
        //判断飞机类型    
        return IsFeiJi();    
    }    
    return ERROR_CARD;    
}

       首先看一下IsLianPai()这个代码,这个是判断是否为连牌,比如34567.下面贴上代码:

bool GameScene::IsLianPai(){  
    int lengh = m_arrPlayerOut->count();  
    CCArray * arr = m_arrPlayerOut;  
    //所有牌值必须小于2  
    CCObject* object;  
    CCARRAY_FOREACH(arr,object){  
        if (((Poker *)object)->getNum() >= 12)//12代表牌值2,下面解释为什么  
            return false;  
    }  
    //必须是连续的(前一张牌值加1是否等于后一张牌值)  
    for(int i=0; i<lengh-1; ++i){  
        Poker* pk = (Poker *)arr->objectAtIndex(i);  
        Poker* pk1 = (Poker *)arr->objectAtIndex(i+1);  
        if(pk->getNum()+1 != pk1->getNum())  
            return false;  
    }  
    return true;  
}

       大家一定会对上面的12数值感到疑惑,下面截个图来解释:

wenzhong.jpg

牌值我是从0开始设置的,比如3的牌值为0,4为1。

接下来放上判断是否是连对 IsLianDui() 代码:

bool GameScene::IsLianDui(){  
    int lengh = m_arrPlayerOut->count();  
    CCArray * arr = m_arrPlayerOut;  
    //所有牌值必须小于2  
    CCObject* object;  
    CCARRAY_FOREACH(arr,object){  
        if (((Poker *)object)->getNum() >= 12)  
            return false;  
    }  
    //大于等于6张牌并且数量为偶数  
    if(lengh < 6 && lengh%2 != 0)  
        return false;  
    //必须是连续的  
    for(int i=0; i<lengh-2; i+=2){  
        Poker* pk = (Poker *)arr->objectAtIndex(i);  
        Poker* pk1 = (Poker *)arr->objectAtIndex(i+2);  
        if(pk->getNum()+1 != pk1->getNum())  
            return false;  
    }  
    return true;  
}

判断飞机类型 IsFeiJi()代码,函数名取的有点不合适,它返回的是一种飞机的类型,而不是bool值,所以很抱歉:

int GameScene::IsFeiJi(){  
    int lengh = m_arrPlayerOut->count();  
    CRAD_INDEX card_index = FenXiFeiJi();//分析牌是否是飞机,下面解释  
    //判断三带二  
    if(card_index.three_index.size()*3+card_index.duble_index.size()*2==lengh && card_index.three_index.size()==1 && card_index.duble_index.size()==1)  
        return THREE_TWO_CARD;  
    //判断飞机  
    if(card_index.three_index.size()>1 && card_index.four_index.empty() && IsFeiJiLian(card_index.three_index)){  
        //飞机不带  
        if(card_index.three_index.size()*3 == lengh && card_index.duble_index.size()+card_index.single_index.size() == 0)  
            return AIRCRAFT_CARD;  
        //飞机带单  
        if(card_index.three_index.size()*3+card_index.single_index.size() == lengh && card_index.duble_index.size() == 0)  
            return AIRCRAFT_SINGLE_CARD;  
        //飞机带双  
        if(card_index.three_index.size()*3+card_index.duble_index.size()*2 == lengh && card_index.single_index.size() == 0)  
            return AIRCRAFT_DOBULE_CARD;  
    }  
    //判断四带  
    if(card_index.three_index.empty() && !card_index.four_index.empty() && lengh%2 == 0)  
    {  
        //四带单  
        if(card_index.four_index.size()*4+card_index.single_index.size() == lengh && card_index.four_index.size()==1 && card_index.single_index.size()==2)  
            return BOMB_TWO_CARD;  
        //四带对  
        if(card_index.four_index.size()*4+card_index.duble_index.size()*2 == lengh && card_index.four_index.size()==1 && card_index.duble_index.size()==1)  
            return BOMB_TWOOO_CARD;  
    }  
    return ERROR_CARD;  
}

这里注意CRAD_INDEX card_index = FenXiFeiJi(); 这句代码。

CRAD_INDEX为一种结构体:

struct CRAD_INDEX//分析飞机  
{  
    std::vector<int> single_index;//单张  
    std::vector<int> duble_index;//双张  
    std::vector<int> three_index;//三张  
    std::vector<int> four_index;//四张  
};

以上的结构体是为了对出的牌进行分类用,下面看看是如何对牌分类的,下面是FenXiFeiJi()的代码:

CRAD_INDEX GameScene::FenXiFeiJi(){  
    //分析牌型结构  
    CCArray* arr = m_arrPlayerOut;  
    //飞机的类型  
    CRAD_INDEX m_cardIndex;   
    for(int i=0; i<arr->count();)  
    {  
        int time = 0;//相同牌的个数  
        Poker* pk = (Poker *)arr->objectAtIndex(i);  
        //找出相同牌  
        for(int j=i; j<arr->count(); ++j)  
        {  
            Poker* pk1 = (Poker *)arr->objectAtIndex(j);  
            if(pk->getNum() == pk1->getNum()){  
                ++time;  
                ++i;  
            }  
        }  
        //单张  
        if(time == 1)  
 nbsp;           m_cardIndex.single_index.push_back(pk->getNum());  
        else if(time == 2)  
            m_cardIndex.duble_index.push_back(pk->getNum());  
        else if(time == 3)  
            m_cardIndex.three_index.push_back(pk->getNum());  
        else if(time == 4)  
            m_cardIndex.four_index.push_back(pk->getNum());  
    }  
    return m_cardIndex;  
}

       本章至此结束,下面我们就可以判断玩家(人)出的牌是否合法了

猜你喜欢

转载自blog.csdn.net/qq_21743659/article/details/108483699