Java实现的麻将胡牌算法

来源:互联网 发布:域名邮箱和企业邮箱 编辑:程序博客网 时间:2024/05/07 12:09

胡牌:

平胡: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;}@Overridepublic 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;}}

麻将牌占一个字节,高4位为类型(1-4为万条筒字),低4位为数据(1-9)

/** * 判断 正常的胡牌类型 * @param isSelfCard 是否自摸牌 * @param cpCards 手牌 * @param gangCards 扛牌 * @param touchCards 碰牌 * @return */@SuppressWarnings({ "unchecked", "rawtypes" })public static int judgeNormalHuType(boolean isSelfCard, List<MajiangCard> cpCards,List<MJGangStep> gangCards, List<MJPengStep> touchCards){List<MajiangCard> tmpCards = MJCommonFuncs.cpList(cpCards);Map<Integer, Integer> numMap = MJCommonFuncs.transCardsToMap(tmpCards);Map<Integer, Integer> numMapCp = (Map<Integer, Integer>) 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<Integer, Integer>) 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<MajiangCard> cards){Map<Integer, Integer> numMap = MJCommonFuncs.transCardsToMap(cards);List<Integer> tmpList = new ArrayList<Integer>();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<MajiangCard> 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<MajiangCard> cards){//分案List<List<MajiangCard>> allKindsList = partKinds(cards);for(List<MajiangCard> cardList : allKindsList){if(cardList.size() % MJConst.KEZHI_CARD_NUM != 0){return false;}}List<List<KindTypeData>> allDataList = new ArrayList<List<KindTypeData>>();for(List<MajiangCard> cardList : allKindsList){//System.out.println("cardList:" +cardList);List<KindTypeData> 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<List<MajiangCard>> partKinds(List<MajiangCard> cardList){List<List<MajiangCard>> ret = new ArrayList<List<MajiangCard>>();MajiangCard lastCard = null;List<MajiangCard> list = null;for(MajiangCard card : cardList){if(lastCard == null){lastCard = card;list = new ArrayList<MajiangCard>();ret.add(list);}if(card.reqType() == lastCard.reqType()){lastCard = card;list.add(card);}else{lastCard = card;list = new ArrayList<MajiangCard>();ret.add(list);list.add(card);}}return ret;}/** * 是否可以胡牌 * @param allDataList 所有案牌的顺刻子数据 * @return */private static boolean couldPolicyHu(List<List<KindTypeData>> 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<KindTypeData> list : allDataList){for(KindTypeData ktd : list){if(ktd.getOrderCnt() > 0){return true;}}}}return false;}/** * 将同一案的牌组合成刻子或顺子 * @param cards * @return */private static List<KindTypeData> kindTypeConform(List<MajiangCard> cards){List<KindTypeData> ret = new ArrayList<KindTypeData>();Map<Integer, Integer> numMap = MJCommonFuncs.transCardsToMap(cards);//先找刻子int cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, MJConst.KEZHI_CARD_NUM);//刻子牌List<Integer> kezhiValueList = new ArrayList<Integer>();while(cardSerValue != 0){kezhiValueList.add(cardSerValue);numMap.remove(cardSerValue);cardSerValue = MJCommonFuncs.filtCardMinNum(numMap, MJConst.KEZHI_CARD_NUM);}if(kezhiValueList.size() > 0){//有刻子牌List<List<Integer>> sublist = MJCommonFuncs.getSubset(kezhiValueList);for(List<Integer> list : sublist){List<MajiangCard> 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<MajiangCard> 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<MajiangCard> 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<MajiangCard> 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;}@Overridepublic String toString() {return "KindTypeData [kezhiCnt=" + kezhiCnt + ", orderCnt=" + orderCnt + "]";}}}




原创粉丝点击