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