生成一張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;
}