胡牌:
平胡:1对将及4副(顺子/刻子)
对对胡:4副刻子+1对将组成的胡牌
七小对:全部牌都是1对
门清:全部牌都是自己摸的,没有碰和暗杠
全求人:全部碰或明杠,手上只剩一张牌,并且是点炮胡,不能自摸
清一色:全部都是一色的平胡(包含万、条、筒、字)
七大对:有4张一样的牌且没杠,其余牌都是对子
豪华大七对:有至少两个4张一样的牌,其余牌都是对子
定义麻将牌:
public class MajiangCard extends Card{
protected byte card;
public MajiangCard(){
}
public MajiangCard(byte card){
this.card = card;
}
public MajiangCard(byte cardValue, byte cardType){
this.card = (byte) (cardValue + (cardType<<4));
}
public boolean reqValid(){
return card > 0;
}
public byte getCard() {
return card;
}
public void setCard(byte card) {
this.card = card;
}
@Override
public String toString() {
return MJUtil.mjToStr(this);
}
public byte reqType(){
byte value = (byte)(card>>4);
return value;
}
public byte reqValue(){
byte type = (byte)(0x0F & card);
return type;
}
}
/**
* 判断 正常的胡牌类型
* @param isSelfCard 是否自摸牌
* @param cpCards 手牌
* @param gangCards 扛牌
* @param touchCards 碰牌
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static int judgeNormalHuType(boolean isSelfCard, List cpCards,
List gangCards, List touchCards){
List tmpCards = MJCommonFuncs.cpList(cpCards);
Map numMap = MJCommonFuncs.transCardsToMap(tmpCards);
Map numMapCp = (Map) MJCommonFuncs.cpMap((Map,?>)(Map)numMap);
//先判断是否是豪华7大对
int fourPairNum = 0;
int cardSerValue = MJCommonFuncs.filtCardNum(numMapCp, 4);
while(cardSerValue != 0){
fourPairNum++;
numMapCp.remove(cardSerValue);
cardSerValue = MJCommonFuncs.filtCardNum(numMapCp, 4);
}
fourPairNum += gangCards.size();
int ret = MJConst.HUTYPE_DISABLE;
if(fourPairNum >= 2 && touchCards.size() == 0){
//其他是对子
boolean isHu = true;
for(int cnt : numMapCp.values()){
if(cnt != 2){
//不是豪华7大对
isHu = false;
break;
}
}
if(isHu){
//胡了
ret |= MJConst.HUTYPE_HAOHUA;
return ret;
}
}
//判断是否是7大对
if(fourPairNum >= 1 && touchCards.size() == 0){
//其他是对子
boolean isHu = true;
for(int cnt : numMapCp.values()){
if(cnt != 2){
//不是7大对
isHu = false;
break;
}
}
if(isHu){
//胡了
ret |= MJConst.HUTYPE_7BIG_PAIR;
return ret;
}
}
//判断是否是全求人
if(cpCards.size() == 2 && !isSelfCard){
MajiangCard card = (MajiangCard)cpCards.get(0);
MajiangCard mc = (MajiangCard)cpCards.get(1);
if(isSameCard(mc, card)){
//判断杠是否明杠
boolean isAllVisibleGang = true;
for(MJGangStep mjgang : gangCards){
if(mjgang.getGangType() == MJConst.GANGTYPE_DISABLE){
isAllVisibleGang = false;
break;
}
}
if(isAllVisibleGang){
ret |= MJConst.HUTYPE_QQR;
//还有可能是对对胡
}
}
}
//判断是否是7小对
if(touchCards.size() == 0){
boolean isHu = true;
for(int cnt : numMapCp.values()){
if(cnt != 2){
//不是7小对
isHu = false;
break;
}
}
if(isHu){
//胡了
ret |= MJConst.HUTYPE_7SMALL_PAIR;
return ret;
}
}
//判断是否是对对胡
numMapCp.clear();
numMapCp = (Map) MJCommonFuncs.cpMap((Map,?>)(Map)numMap);
cardSerValue = MJCommonFuncs.filtCardNum(numMapCp, 3);
int threePairNum = 0;
while(cardSerValue != 0){
threePairNum++;
numMapCp.remove(cardSerValue);
cardSerValue = MJCommonFuncs.filtCardNum(numMapCp, 3);
}
threePairNum += touchCards.size();
threePairNum += gangCards.size();
if(threePairNum == 4 && numMapCp.size() == 1){
ret |= MJConst.HUTYPE_DDH;
//对对胡,不可能是平胡
return ret;
}
//判断是否是平胡
boolean pinghu = PingHuAnalyser.doAnalysing(tmpCards);
if(pinghu){
ret |= MJConst.HUTYPE_PH;
}
return ret;
}
除了平胡类型外,其他的胡牌类型都很简单,无非是判断每种牌的数据。平胡类型就复杂些了
/**
* 平胡分析器
* @author skymr
*
*/
public class PingHuAnalyser {
public static boolean doAnalysing(List cards){
Map numMap = MJCommonFuncs.transCardsToMap(cards);
List tmpList = new ArrayList();
try{
//找出所有对子的可能
int cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, 2);
while(cardSerValue != 0){
tmpList.add(cardSerValue);
numMap.remove(cardSerValue);
cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, 2);
}
if(tmpList.size() == 0){
return false;
}
for(int twoNumCard : tmpList){
List tmpCards = MJCommonFuncs.cpList(cards);
MJCommonFuncs.removeByNum(tmpCards, twoNumCard, 2);
// System.out.println("踢除对子后:"+tmpCards);
//踢除对子后找刻子
if(couldHu(tmpCards)){
tmpCards.clear();
return true;
}
tmpCards.clear();
}
return false;
}finally{
numMap.clear();
tmpList.clear();
}
}
/**
* 去掉一个对子后,是否是能胡牌的类型
* @param cards
* @return
*/
private static boolean couldHu(List cards){
//分案
List> allKindsList = partKinds(cards);
for(List cardList : allKindsList){
if(cardList.size() % MJConst.KEZHI_CARD_NUM != 0){
return false;
}
}
List> allDataList = new ArrayList>();
for(List cardList : allKindsList){
// System.out.println("cardList:" +cardList);
List dataList = kindTypeConform(cardList);
// System.out.println("这一案组成顺子或该子:"+dataList);
if(dataList == null){
return false;
}
if(dataList.size() == 0){
return false;
}
allDataList.add(dataList);
}
return couldPolicyHu(allDataList);
}
/**
* 分案,把每案的牌区分出来
* @param cardList
* @return
*/
private static List> partKinds(List cardList){
List> ret = new ArrayList>();
MajiangCard lastCard = null;
List list = null;
for(MajiangCard card : cardList){
if(lastCard == null){
lastCard = card;
list = new ArrayList();
ret.add(list);
}
if(card.reqType() == lastCard.reqType()){
lastCard = card;
list.add(card);
}
else{
lastCard = card;
list = new ArrayList();
ret.add(list);
list.add(card);
}
}
return ret;
}
/**
* 是否可以胡牌
* @param allDataList 所有案牌的顺刻子数据
* @return
*/
private static boolean couldPolicyHu(List> allDataList){
if(allDataList.size() == 1){
for(KindTypeData ktd : allDataList.get(0)){
if(ktd.getOrderCnt() > 0 ){
//至少有一个顺子
return true;
}
}
}
//2是胡牌案的数量
else if(allDataList.size() >= 2){
for(List list : allDataList){
for(KindTypeData ktd : list){
if(ktd.getOrderCnt() > 0){
return true;
}
}
}
}
return false;
}
/**
* 将同一案的牌组合成刻子或顺子
* @param cards
* @return
*/
private static List kindTypeConform(List cards){
List ret = new ArrayList();
Map numMap = MJCommonFuncs.transCardsToMap(cards);
//先找刻子
int cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, MJConst.KEZHI_CARD_NUM);
//刻子牌
List kezhiValueList = new ArrayList();
while(cardSerValue != 0){
kezhiValueList.add(cardSerValue);
numMap.remove(cardSerValue);
cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, MJConst.KEZHI_CARD_NUM);
}
if(kezhiValueList.size() > 0){
//有刻子牌
List> sublist = MJCommonFuncs.getSubset(kezhiValueList);
for(List list : sublist){
List cpCards = MJCommonFuncs.cpList(cards);
MJCommonFuncs.removeByCardValue(cpCards, list, MJConst.KEZHI_CARD_NUM);
//取得顺子数量
int cardsCnt = cpCards.size();
int orderCnt = getOrderCnt(cpCards);
if(orderCnt != cardsCnt / MJConst.ORDER_CARD_NUM){
cpCards.clear();
continue;
}
KindTypeData ktd = new KindTypeData();
ktd.setOrderCnt(orderCnt);
ktd.setKezhiCnt(list.size());
ret.add(ktd);
}
sublist.clear();
}
else{
//没有刻子牌
List cpCards = MJCommonFuncs.cpList(cards);
int cardsCnt = cpCards.size();
int orderCnt = getOrderCnt(cpCards);
if(orderCnt != cardsCnt / MJConst.ORDER_CARD_NUM){
cpCards.clear();
return ret;
}
KindTypeData ktd = new KindTypeData();
ktd.setOrderCnt(orderCnt);
ret.add(ktd);
}
kezhiValueList.clear();
numMap.clear();
return ret;
}
/**
* 取得顺子数量
* @param cardList
* @return
*/
private static int getOrderCnt(List cardList){
if(cardList.size() < MJConst.ORDER_CARD_NUM){
return 0;
}
if(cardList.get(0).reqType() == MJUtil.CARD_TYPE_WORD){
//字,没有顺
return 0;
}
int ret = 0;
while(removeOrderCards(cardList)){
ret ++;
}
return ret;
}
/**
* 去掉一个顺子牌
* @param cardList
* @return 是否去除成功
*/
private static boolean removeOrderCards(List cardList){
int len = cardList.size();
if(len < MJConst.ORDER_CARD_NUM){
return false;
}
MajiangCard last = cardList.remove(len - 1);
int cnt = 2;
for(int i = len - 2; i >= 0; i--){
if(cardList.get(i).reqValue() + 1 == last.reqValue()){
last = cardList.remove(i);
cnt --;
if(cnt == 0){
return true;
}
continue;
}
if(cardList.get(i).reqValue() == last.reqValue()){
continue;
}
else{
return false;
}
}
return false;
}
/**
* 同一案牌中的类型数据
* @author skymr
*
*/
private final static class KindTypeData{
//刻子数量
private int kezhiCnt;
//顺子数量
private int orderCnt;
//剩下的散牌数量
private int leftCardNum;
public int getKezhiCnt() {
return kezhiCnt;
}
public void setKezhiCnt(int kezhiCnt) {
this.kezhiCnt = kezhiCnt;
}
public int getOrderCnt() {
return orderCnt;
}
public void setOrderCnt(int orderCnt) {
this.orderCnt = orderCnt;
}
public int getLeftCardNum() {
return leftCardNum;
}
public void setLeftCardNum(int leftCardNum) {
this.leftCardNum = leftCardNum;
}
@Override
public String toString() {
return "KindTypeData [kezhiCnt=" + kezhiCnt + ", orderCnt=" + orderCnt + "]";
}
}
}