Java实现的麻将胡牌算法

胡牌:

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




你可能感兴趣的:(java,game)