c++ 胡牌 聽牌

生成一張N維的圖去分析, 比較容易一些

這個是2人麻將, 4人麻將改幾個數值就可以用了;

判斷胡牌的思路:

比如 :

 1~9萬, 0x01 ~ 0x09  ;

 1~9筒 , 0x11~0x19;

 1~9條 , 0x21~ 0x29;

 東南西北中發白 : 0x31 ~ 0x39

 花牌 : 0x41 ~ 0x48;

弄一個 arr[4][10] 的數組, 做一個牌型的圖出來 , 不算花牌;

然後比如 筒子 :  (0x11&0xF0 << 4) 作爲花色 , 0x11 & 0x0F 作爲值[不論什麼牌都是 1 ~ 9 ];

數組的第一個索引放花色 ; 這個花色索引對應的數組首個元素存放此花色的數量

 

現在比如有 2萬,2萬, 3萬 ,1筒

在數組應是 arr[0][0] == 3, arr[1][0] == 1;  第0個元素作爲此花色的總數量, 他們的值統計在對應的數組上;

因此 arr[0][2]  == 2 ,  arr[1][1] == 1. 

這樣每個索引都記錄此牌的數量; 如此就知道了所有的牌;

 

胡牌首先判斷7個一對, 有了這個數組, 直接循環就可以得出結果;

然後循環每個花色的首元素, 通過牌的數量來判斷, 如果要胡牌必然有一對, 其他的牌都是3個一組.

因此對 3 除餘. 如果餘數是 1 則不滿足胡牌的牌數, 如果是 2 則表示 此花色或許在此行中 ;

接下去就要分析牌型了, 寫一個能判斷順子和克子的函數, 除了一對所在的行, 把其他行都扔進去看看能否滿足牌型;

之後要對 一對所在的行做特殊處理, 因爲你無法知道一對是什麼樣的牌型 1[11] / [11]

因此需要每次要在對應的索引上 減去2, 然後把剩餘的牌去匹配是否是順子/克子; 

如果能匹配成功, 最後能得到 [一對] 所在的 索引;  

 

下面的代碼只用了一個arr[2][10] 來做演示 , arr[0] 筒子, arr[1] 風牌;

代碼裏全是註釋;

//先寫2個後面要用的函數
int getColor(char card){
    return card>>4; // (card & 0xF0 ) >> 4 也ok
}
int getValue(char card){
    return card & 0x0F;
}
//檢查是否是花牌
bool isFlowerCard(char card){
    return card &0x20;
}

//所有的牌都是16進制
//每個數組第一個元素用於計算此種花色含有多少張牌
//此分佈圖, 不允許放入花牌,花牌先成其他牌後再使用此圖即可
struct card_pic{
    char cards[2][10]; //做演示, 只算筒和字[風]牌, 要算N中花色 把2換成N即可
    card_pic(char * handcards  = 0, int len=0){
        if(handcards && len){
            gen_pic(handcards,len);
        }
    }
    bool gen_pic(char * arr, int len){
        reset();
        for(int i = 0 ; i < len ; ++i){
            int color_index = getColor(arr[i]); //getColor(arr[i]) & 0X0F 也可, 保險
            int value = getValue(arr[i]);
            if(color_index > 1){
                reset();
                return false; //不能是花牌
            }
            ++cards[color_index][0]; //增加此牌型的數量
            ++cards[color_index][value]; //增加此牌的數量
        }
        return true;
    }
    void reset(){
        memset(cards, 0 , sizeof(cards));
    }
    void show(){
        for(int i = 0; i < 2 ; ++i){
            for(int j = 0 ; j < 10 ; ++j){
                cout << (int)cards[i][j] << " ";
            }
            cout << endl;
        }
        cout << endl;
    }
    //復原牌, hand_cars 能足夠長度 14就ok,我就不做檢測了
    int restoreCards(char * hand_cards){
        int count  = cards[0][0] + cards[1][0];
        int  hand_cards_index = 0;
        for(int i =0 ; i < 2 ; ++i){
            if(cards[i][0] > 0){
                for(int j = 1 ; j < 10 ; ++j){
                    if(cards[i][j] > 0){
                        int card_count = cards[i][j];
                        for(int k = 0 ; k  < card_count ; ++k)
                            hand_cards[hand_cards_index++] = (char) ( (i<<4) | j );
                    }
                }
            }
        }
        std::sort(hand_cards, hand_cards + count);
        return count;
    }
    //檢查七對
    bool check_qidui(){
        int qidui_count = 0;
        for(int i = 0 ; i < 2 ;++i){
            for(int j = 1 ; j < 10 ; ++j){
                if(cards[i][j] == 2){
                    ++qidui_count;
                }
                else if(cards[i][j] == 4){
                    qidui_count += 2;
                }
            }
        }
        return qidui_count == 7;
    }
    //胡
    bool checkHu(){
        if(check_qidui()){
            return true;
        }
        //檢測是否滿足33332模型
        //任意一行上必然一對, 因此從總數上  cards[i]%3 == 2 ,就是 " 一對"所在的位置
        //第一遍只從數量上來分析一次, 把不能滿足此條件的先過濾掉
        int ret = 0;
        bool dui_exists = 0;
        int dui_pos = 0;
        for(int i = 0 ; i < 2; ++i){
             ret = cards[i][0] % 3;
             if(ret == 1){ // 餘1張,  如果此行總數是1 , 4, 7 張不能胡牌
                return  false;
             }
             else if( 2 == ret){//一對所在的位置
                 if(dui_exists) //一對不能再次出現
                     return false;
                 dui_exists = true;
                 dui_pos = i;
             }
        }

        //現在開始分析牌面,由於一對所在的行,會影響檢測,把一對所在的行放到後面檢測
        //因此先把別的行進行檢測 , 別的行必然要滿足3333/333/33/3 這種模型
        for(int i = 0 ; i < 2; ++i){
            if(i != dui_pos){
                //如果別的行檢測出錯,證明牌面無法胡
                if(!fenxi_shunzi_kezi(i,i==1))  return false;
            }
        }

        //到此,證明別的行 都能滿足胡的條件了, 現在開始檢測 一對所在的行
       //由於這個[一對]不知道是怎麼形成的,可能是1[11],也可以是11[11],或11,因此要循環
        bool find = false;
        int pos = -1;
        for(int i = 1; i < 10 ; ++i){
            if(cards[dui_pos][i] < 2) continue;
            //嘗試減掉一對 , 然後去分析
            cards[dui_pos][i]-=2;
            cards[dui_pos][0] -=2;
            //如果減掉此一對, 還能成功, 代表能胡了
            find = fenxi_shunzi_kezi(dui_pos,dui_pos == 1);
            cards[dui_pos][i]+=2;
            cards[dui_pos][0] +=2;
            if(find){
                pos = i;
                break;
            }
        }
        cout << "yi dui  :" << pos << endl;
        return find;
    }
    //用於分析第N行的順子和克子
    //遞歸調用 . 每次把找到的順子或克子 減掉後[ -3 ], 在去遞歸,直到把此行的牌都減完,說明此行可以
    bool fenxi_shunzi_kezi(int index , bool isFengPai){
            //如果牌已經減完了,或者此行沒牌
           if(cards[index][0] == 0) {
               return true;
           }
           bool ret = false;
            int start_index = 0;
            //找到第一個不爲0的,開始處理順子和克子
            for(int i = 1 ; i  < 10 ; ++i){
                if(cards[index][i] >0){start_index = i;break;}
            }
            //如果首先找到的是克子
            if(cards[index][start_index] >=3){
                //把數量先減掉,再去查剩餘的牌面
                cards[index][start_index] -=3;
                cards[index][0] -=3;
                //遞歸調用,再去查找
                ret = fenxi_shunzi_kezi(index,isFengPai);
                //還原牌面
                cards[index][start_index] +=3;
                cards[index][0] +=3;
                //到此,此行的牌面全部分析完成
                return ret;
            }
            //由於風牌[東南西北中發白] 沒有順子,因此首先判斷是不是風牌
            if( !isFengPai && start_index < 8  &&
                    cards[index][start_index] > 0 &&
                    cards[index][start_index + 1] > 0 &&
                    cards[index][start_index + 2] > 0){
                //把順子都減掉
                cards[index][0] -=3;
                cards[index][start_index] -=1;
                cards[index][start_index + 1] -=1;
                cards[index][start_index +2] -=1;
                ret = fenxi_shunzi_kezi(index,isFengPai);
                cards[index][0] +=3;
                cards[index][start_index] +=1;
                cards[index][start_index + 1] +=1;
                cards[index][start_index +2] +=1;
                //至此 ,全部檢查完成
                return ret;
            }
           return ret;
    }
};
bool checkTing(char * leftcards,  int leftcards_len , char * handcards, int handcards_len){
    if(leftcards_len < 1 || handcards_len < 1)
        return false;

    char filter_leftcards[72] = {0}; //把花牌都刪去
    int left_count = 0;
    for(int i = 0 ; i < leftcards_len ; ++i){
        if(!isFlowerCard(leftcards[i])){
            filter_leftcards[left_count++] = leftcards[i];
        }
    }
    set<char> sets(filter_leftcards, filter_leftcards+left_count);
    set<char>::iterator iter_end = sets.end();
    set<char>::iterator iter_begin = sets.begin();

    char cp_handcards[14];
    memset(cp_handcards,0,sizeof(cp_handcards));
    memcpy(cp_handcards,handcards,handcards_len);
    cout << "handcards:" << endl;
    for(int i = 0;  i < handcards_len ; ++ i){
        cout << (int)cp_handcards[i] << " ";
    }
    cout << endl;

    static card_pic pic;
    int iCount = 0 ;
    for(; iter_begin != iter_end ; ++iter_begin){
        char card = *iter_begin;
        int color_index = getColor(card);
        char value = getValue(card);
        for(int i = 0 ; i < handcards_len ; ++i){
            ++iCount;
            char restore_card = cp_handcards[i];
            cp_handcards[i] =card;
            if(!pic.gen_pic(cp_handcards,handcards_len)){
                cout << "card is error :" << i << endl;
                cp_handcards[i] = restore_card;
                continue;
            }
            if(pic.checkHu()){
                cout << "can ting , loop :" << iCount << endl;
                pic.show();
                return true;
            }
            cp_handcards[i] = restore_card;
        }
    }
    return false;
}




//測試
int main(int argc, char* argv[])
{
    using namespace std;


    char a[] = {
       0x01, 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09, //萬條筒
        0x01, 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
        0x01, 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
        0x01, 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,

        0x11, 0x12,0x13,0x14,0x15,0x16,0x17,    //風牌
        0x11, 0x12,0x13,0x14,0x15,0x16,0x17,
        0x11, 0x12,0x13,0x14,0x15,0x16,0x17,
        0x11, 0x12,0x13,0x14,0x15,0x16,0x17,
        0x21, 0x22,0x23,0x24,0x25,0x26,0x27,0x28 // 花
    };
    char arr[14] = {0x01, 0x02,0x03,0x01, 0x02,0x03,0x01, 0x02,0x03,
                   0x12,0x12,0x13,0x13,0x13};
    int len = 14;
    char qarr[14] = {0x01, 0x02,0x03, 0x06,0x07,0x08, 0x09,0x09,0x04,
                   0x12,0x12,0x13,0x13,0x13};
    card_pic pic(qarr,len);
    pic.show();
    cout << (pic.checkHu() ? "hu!" : "nooooooooooo") << endl;
    checkTing(a,72,qarr,14);








    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章