admin 管理员组

文章数量: 1086019

跑/歪胡子c++算法

    跑胡子/歪胡子算法和麻将算法类似,也是属于2n+3类型,通过找出所有的3的组合牌,最后再分析剩下的两张牌是否能组成门子(指的是相关/相同的两张牌,例如小2、小4)。

    基本说明:将一张卡牌用一个BYTE表示,高四位对应花色(小字0、大字1)、低四位对应点数(1-10)。为了便于统计和计算卡牌,将卡牌的BYTE转换成一个索引,这样能够很快的遍历玩家手上的牌,索引数组下标对应具体的卡牌索引,数组的值表示这张牌的个数,具体函数如下:

  

//扑克数据转换成索引
//小字:(1-10)*4 、大字:(1-10)*4
//cardIndex:小字(0-9)、大字(10-19)
BYTE SwitchToCardIndex(BYTE cbCardData)
{return ((cbCardData&MASK_COLOR)>>4)*10+(cbCardData&0X0F)-1;	//高四位花色,低四位点数
}

    算法思路:

1、首先分析是不是特殊对子牌型,和麻将的七对类似,需要单独判断;

2、判断是不是2n+3的牌型;

3、再判断胡息是否足够,是否是特殊牌型

4、最后计算歪、坎、豪等所有息数

1、特殊对子牌型

   0/1/9/10对判断,这个比较简单,判断数组中对子的个数,需要注意的是四张算两对;

//对子胡牌
bool IsAllDuiZi(const WORD &wChairID, const BYTE cbCardIndex[LEN_MAX_INDEX])
{bool bQidui = true;//十对:针对庄家,胡牌手牌20张,九对:针对闲家:胡牌手牌19张if( LEN_MAX_HANDCARD_NUM != GetCardCount(cbCardIndex) && (LEN_MAX_HANDCARD_NUM-1) != GetCardCount(cbCardIndex))return false;//并且不能出牌,只判断起手的牌(与天胡类似)if ( !((1 == m_card_config.send_cards_sum) && (0 ==m_card_config.out_card_sum)) ){return false;}//对子数目BYTE cbPairCount = 0;//四个数目BYTE cbFourCount = 0;//临时数据BYTE cbCardIndexTemp[LEN_MAX_INDEX];CopyMemory(cbCardIndexTemp, cbCardIndex, sizeof(cbCardIndexTemp));//计算手上大于2张的数目for (BYTE i=0; i<LEN_MAX_INDEX; i++){//对子的数目BYTE cbCardCount = cbCardIndexTemp[i];if ( 2 == cbCardCount || 3 == cbCardCount ){			//对子数cbPairCount++;}if (4 == cbCardCount){cbPairCount+=2;cbFourCount++;	}}//十对if (10 == cbPairCount){return true;}if (0 == cbPairCount)	//无对{return true;}if (9 == cbPairCount)	//九对{return true;}if ((1 == cbPairCount) || ((2 == cbPairCount) && (1==cbFourCount)))	//一对	(四个算一对){return true;}return false;
}

2、2n+3牌型判断

   首先,单吊的情况单独判断,单吊指的是手上只剩一张牌;然后找出手牌中可能的三个的组合(顺子/坎),每几个组合在一起,若能还原手牌,最后再判断剩余的两张是不是门子,是门子,说明这种组合对应的牌型可以胡牌;继续循环在所有的组合中遍历,直到找出所有能胡牌的牌型为止。下面将几个主要的函数分享给大家。

      第一个:找出数组中所有可能的三个组合。

//类型子项
struct tagKindItem
{WORD	wWeaveKind;	//组合类型BYTE	cbCenterCard;	//中心扑克			BYTE	cbCardIndex[3];	//扑克索引BYTE	cbRedCard;	//是不是二七十	0-不是	1-是
};//分析是不是一句话:包括碰子(三张相同)、顺子
bool AnalyseSentence(const WORD &wChairID, BYTE *cbCardIndex, BYTE cbCurrentCard, tagKindItem *KindItem, BYTE &cbKindCount)
{//校验if (NULL == cbCardIndex || KindItem == NULL)return false;//遍历所有牌for (BYTE i=0; i<LEN_MAX_INDEX; i++){//坎判断if ( 3 <= cbCardIndex[i] )		//同一张牌大于等于3,当做坎处理{//若是胡的那张牌,系统发给自己的算歪,发给别人的算碰if ( cbCurrentCard == SwitchToCardData(i) ){//发给自己if (wChairID == m_playing_para.provide_user){KindItem[cbKindCount].cbCardIndex[0] = i;KindItem[cbKindCount].cbCardIndex[1] = i;KindItem[cbKindCount].cbCardIndex[2] = i;KindItem[cbKindCount].wWeaveKind = WIK_WAI;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);		cbKindCount++;}else{//4张,若不是给自己发的牌,则手中肯定有一坎if (4 == cbCardIndex[i]){KindItem[cbKindCount].cbCardIndex[0] = i;KindItem[cbKindCount].cbCardIndex[1] = i;KindItem[cbKindCount].cbCardIndex[2] = i;KindItem[cbKindCount].wWeaveKind = WIK_KAN;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);		cbKindCount++;}KindItem[cbKindCount].cbCardIndex[0] = i;KindItem[cbKindCount].cbCardIndex[1] = i;KindItem[cbKindCount].cbCardIndex[2] = i;KindItem[cbKindCount].wWeaveKind = WIK_PENG;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);		cbKindCount++;}}else{//只有最开始发的在手上的三张算坎KindItem[cbKindCount].cbCardIndex[0] = i;KindItem[cbKindCount].cbCardIndex[1] = i;KindItem[cbKindCount].cbCardIndex[2] = i;KindItem[cbKindCount].wWeaveKind = WIK_KAN;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);		cbKindCount++;}}//顺子判断,手牌中的顺子都存为左吃类型if ( (i<LEN_MAX_INDEX) && ((i%ONE_COLOR_CARD_NUM) < (ONE_COLOR_CARD_NUM-2)) ){//二、七、十if (i == SMALL_TWO_CARD_INDEX || i == BIG_TWO_CARD_INDEX){//变量定义BYTE cbIndex[3] = { cbCardIndex[i], cbCardIndex[i+5], cbCardIndex[i+8] };int nTemp = 0;BYTE cbValidIndex[3] = {i, i+5, i+8};//循环找到所有的顺子while( 3 <= (cbIndex[0]+cbIndex[1]+cbIndex[2]) ){for( BYTE j = 0; j < 3; j++ ){if( 0 < cbIndex[j] ) {cbIndex[j]--;}else {nTemp--;}}if( 0 <= nTemp ){					KindItem[cbKindCount].wWeaveKind = WIK_CHI_LEFT;KindItem[cbKindCount].cbRedCard = 1;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);KindItem[cbKindCount].cbCardIndex[0] = cbValidIndex[0];KindItem[cbKindCount].cbCardIndex[1] = cbValidIndex[1];KindItem[cbKindCount].cbCardIndex[2] = cbValidIndex[2];cbKindCount++;}else break;}}//连牌判断,即顺子		//TIDO 这个判断后面可能还需要看看//只要牌数大于等于3,则进行组合if( 3 <= (cbCardIndex[i]+cbCardIndex[i+1]+cbCardIndex[i+2]) ){BYTE cbIndex[3] = { cbCardIndex[i], cbCardIndex[i+1], cbCardIndex[i+2] };int nTemp = 0;BYTE cbValidIndex[3];//循环找到所有的顺子while( 3 <= (nTemp+cbIndex[0]+cbIndex[1]+cbIndex[2]) ){for( BYTE j = 0; j < 3; j++ ){if( 0 < cbIndex[j] ) {cbIndex[j]--;}else {nTemp--;}}if( 0 <= nTemp ){KindItem[cbKindCount].cbCardIndex[0] = i;KindItem[cbKindCount].cbCardIndex[1] = i+1;KindItem[cbKindCount].cbCardIndex[2] = i+2;KindItem[cbKindCount].wWeaveKind = WIK_CHI_LEFT;KindItem[cbKindCount].cbCenterCard = SwitchToCardData(i);	//手牌中的顺子,中心扑克都是左吃的牌cbKindCount++;}else break;}}}}return true;
}

    第二个:如何从三个组合中循环找出所有的可能牌型

//分析子项
struct tagAnalyseItem
{bool	bIsHand[6];		//是不是手牌上的组合类型,还是已经吃碰杠的BYTE	cbMenZi[2];			//门子(一对将或相关的两张牌)bool	bDuiziEye;			//牌眼是不是对子WORD	wWeaveKind[6];	//组合类型(包括门子和将)BYTE	cbCenterCard[6];	//中心扑克BYTE    cbCardData[6][4];  //实际扑克BYTE	cbRedCard[6];	//是不是二七十	0-不是	1-是
};//变量定义BYTE cbCardIndexTemp[LEN_MAX_INDEX];ZeroMemory(cbCardIndexTemp, sizeof(cbCardIndexTemp));//变量定义BYTE cbIndex[6]={0,1,2,3,4,5};tagKindItem *pKindItem[6];ZeroMemory(&pKindItem, sizeof(pKindItem));//开始组合do{//设置变量CopyMemory(cbCardIndexTemp, cbCardIndex, sizeof(cbCardIndexTemp));for (BYTE i=0; i<cbLessKindItem; i++)pKindItem[i] = &KindItem[cbIndex[i]];//数量判断bool bEnoughCard = true;for (BYTE i=0; i<cbLessKindItem*3; i++)		//循环遍历组合中的所有卡牌索引{//存在判断BYTE cbCardIndex = pKindItem[i/3]->cbCardIndex[i%3]; if (cbCardIndexTemp[cbCardIndex] == 0){bEnoughCard = false;break;}else cbCardIndexTemp[cbCardIndex]--;}//判断是有门子if ( bEnoughCard ){//是否有门子BYTE cbDDMenzi[2];ZeroMemory(cbDDMenzi, sizeof(cbDDMenzi));bool bDuiziEye = false;if ( IsHaveMenZi(cbCardIndexTemp, cbDDMenzi, bDuiziEye) ){//变量定义tagAnalyseItem AnalyseItem;ZeroMemory(&AnalyseItem, sizeof(tagAnalyseItem));//设置已经吃碰杠的组合类型for (BYTE i=0; i<cbWeaveCount; i++){AnalyseItem.wWeaveKind[i] = WeaveItem[i].wWeaveKind;AnalyseItem.cbCenterCard[i] = WeaveItem[i].cbCenterCard;AnalyseItem.cbRedCard[i] = WeaveItem[i].cbRedCard;AnalyseItem.bIsHand[i] = false;	//不是手牌,全部是已经吃碰杠过的GetWeaveCardData( WeaveItem[i].wWeaveKind, WeaveItem[i].cbCenterCard, AnalyseItem.cbCardData[i], AnalyseItem.cbRedCard[i] );}//设置手牌中的组合类型for (BYTE i=0; i<cbLessKindItem; i++) {AnalyseItem.wWeaveKind[i+cbWeaveCount]   = pKindItem[i]->wWeaveKind;AnalyseItem.cbCenterCard[i+cbWeaveCount]  = pKindItem[i]->cbCenterCard;AnalyseItem.cbRedCard[i+cbWeaveCount] = pKindItem[i]->cbRedCard;AnalyseItem.cbCardData[i+cbWeaveCount][0] = SwitchToCardData(pKindItem[i]->cbCardIndex[0]);AnalyseItem.cbCardData[i+cbWeaveCount][1] = SwitchToCardData(pKindItem[i]->cbCardIndex[1]);AnalyseItem.cbCardData[i+cbWeaveCount][2] = SwitchToCardData(pKindItem[i]->cbCardIndex[2]);AnalyseItem.bIsHand[i+cbWeaveCount] = true;	//是手牌中的组合}//设置门子memcpy_s(AnalyseItem.cbMenZi, sizeof(AnalyseItem.cbMenZi), cbDDMenzi, sizeof(AnalyseItem.cbMenZi));AnalyseItem.bDuiziEye = bDuiziEye;//插入结果AnalyseItemArray.Add(AnalyseItem);}}//设置索引,防止444 56 或234 44 456这种牌型出现,所以要用while循环if (cbIndex[cbLessKindItem-1] == (cbKindItemCount-1)){BYTE i = cbLessKindItem-1;for (i = cbLessKindItem-1; i>0; i--){if ( (cbIndex[i-1]+1) != cbIndex[i] ){BYTE cbNewIndex = cbIndex[i-1];for (BYTE j = (i-1); j<cbLessKindItem; j++) {cbIndex[j] = cbNewIndex+j-i+2;}break;}}if (i==0)		//只有一种组合,退出大循环break;		}elsecbIndex[cbLessKindItem-1]++;} while (true);

最后,假如能胡牌,再去判断是否有十三红等特殊牌型,就比较简单了。

简单的分享,给大家提供些思路,具体的算法实现大家可以自己试试,或者私信我

 

本文标签: 跑歪胡子c算法