Java实现的麻将胡牌算法

3 篇文章 0 订阅

胡牌:

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

麻将牌占一个字节,高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;
		}

		@Override
		public String toString() {
			return "KindTypeData [kezhiCnt=" + kezhiCnt + ", orderCnt=" + orderCnt + "]";
		}
		
		
	}
}




  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值