代码改变世界

斗地主之癞子牌型适配方法

2014-08-24 11:44  wiikii#  阅读(1368)  评论(0编辑  收藏  举报

场景:相信大家都知道,斗地主游戏中有一个新的模式--癞子玩法。因此我们的出牌的时候,N张正常的牌+M张癞子牌  就可能组合成多种牌型,这中间就涉及到了能否适配相应牌型和如何适配相应的牌型的问题。

 

输入:N张正常的牌,M张癞子牌 当然癞子牌不超过四张。

输出:R种结果,每种结果包含M张癞子牌所适配的正常牌值以及当前适配所代表的牌型。

 

言归正传,我们知道斗地主有如下几种牌型:

· 火箭:即双王(双鬼牌),什么牌型都可打,是最大的牌。
· 炸弹:四张同数值牌(如四个5)。除火箭和比自己大的炸弹外,什么牌型都可打。
· 单牌(一手牌):单个牌。
· 对牌(一手牌):数值相同的两张牌。
· 三张牌:数值相同的三张牌(如三个10)。
· 三带一手:数值相同的三张牌+ 一张单牌或一对牌。例如: 333+4或333+44
· 单顺:五张或更多的连续单牌(如:34567或678910J)。不包括2点和双王,不分花色。
· 双顺:三对或更多的连续对牌(如:334455、88991010JJ)。不包括2点和双王。
· 三顺:二个或更多的连续三张牌(如:333444、444555666777)。不包括2点和双王。也叫飞机不带翅膀。
· 飞机带翅膀。三顺+同数量的一手牌。例如:333444+69 或333444555+667799
· 四带二:四张牌+两手牌。例如:5555+3+8或 4444+55+77

像炸弹,单牌,双牌,对牌,三张牌,四带二这些牌型的适配在这里就不多说啦。这里着重讲的是 单顺,双顺,三顺,三带一及三带一的飞机,三带对以及三带对的飞机牌型适配。


前期处理:
  1 public static enum PokerSuit {
  2         DIAMOND("DIAMOND", 0), CLUB("CLUB", 1), HEART("HEART", 2), SPADE("SPADE", 3), BLACK("BLACK", 4), RED("RED", 5);//black与red是为大小王准备的
  3 
  4         private final String name;
  5         private final int value;
  6 
  7         PokerSuit(String name, int value) {
  8             this.name = name;
  9             this.value = value;
 10         }
 11 
 12         @Override
 13         public String toString() {
 14             return getName() + ":" + getValue();
 15         }
 16 
 17         public String getName() {
 18             return name;
 19         }
 20 
 21         public int getValue() {
 22             return value;
 23         }
 24 
 25         public static int getSize() {
 26             return 6;
 27         }
 28     }
 29 
 30     public static enum PokerRank {
 31         THREE("THREE", 3), FOUR("FOUR", 4), FIVE("FIVE", 5), SIX("SIX", 6), SEVEN("SEVEN", 7), EIGHT("EIGHT", 8), NINE("NINE", 9),
 32         TEN("TEN", 10), JACKY("JACKY", 11), QUEENE("QUEENE", 12), KING("KING", 13), ACE("ACE", 14), TWO("TWO", 15), JOKER("JOKER", 16);
 33 
 34         private final String name;
 35         private final int value;
 36 
 37         PokerRank(String name, int value) {
 38             this.name = name;
 39             this.value = value;
 40         }
 41 
 42         @Override
 43         public String toString() {
 44             return getName() + ":" + getValue();
 45         }
 46 
 47         public String getName() {
 48             return name;
 49         }
 50 
 51         public int getValue() {
 52             return value;
 53         }
 54 
 55         public static int getSize() {
 56             return 14;
 57         }
 58     }
 59 
 60     public static class PokerCard implements Comparable<PokerCard> {
 61         private int mIndex;
 62         private PokerSuit mSuit;
 63         private PokerRank mRank;
 64 
 65         PokerCard(int index, PokerSuit suit, PokerRank rank) {
 66             this.mIndex = index;
 67             this.mSuit = suit;
 68             this.mRank = rank;
 69         }
 70 
 71         public PokerSuit getSuit() {
 72             return mSuit;
 73         }
 74 
 75         public PokerRank getRank() {
 76             return mRank;
 77         }
 78 
 79         public int getIndex() {
 80             return mIndex;
 81         }
 82 
 83         @Override
 84         public String toString() {
 85             return "CARD[[" + getIndex() + "=" + mSuit.getName() +  ":" + mRank.getName() + "]]";
 86         }
 87 
 88         @Override
 89         public int compareTo(PokerCard another) {
 90             if(this.getRank().getValue() > another.getRank().getValue()) {
 91                 return -1;
 92             } else if(this.getRank().getValue() < another.getRank().getValue()) {
 93                 return 1;
 94             } else {
 95                 if(this.getSuit().getValue() > another.getSuit().getValue()) {
 96                     return -1;
 97                 } else if(this.getSuit().getValue() < another.getSuit().getValue()) {
 98                     return 1;
 99                 } else {
100                     return 0;
101                 }
102             }
103         }
104     }
输入的总牌数:PokerCard [] inputCards;

我们将输入的牌,除癞子外,全部解析成  单张的多少个(PokerRank singleSet[]),对子的多少个(PokerRank pairSet[]),三张的多少个(PokerRank threeSet[]),四张的多少个(PokerRank fourSet[]);癞子解析成癞子张数(PokerRank laiziSet[])。

新建二个辅助数组:
  int [] assistValue = new int [17]; (~,~,~,3,4,5,6,7,8,9,10,J,Q,K,A,2,JOKER)前面三个值是不用的。
  PokerRank[] sAssistRanks = {
null, null, null, PokerRank.THREE, PokerRank.FOUR, PokerRank.FIVE, PokerRank.SIX, PokerRank.SEVEN, PokerRank.EIGHT, PokerRank.NINE,
PokerRank.TEN, PokerRank.JACKY, PokerRank.QUEENE, PokerRank.KING, PokerRank.ACE, PokerRank.TWO, PokerRank.JOKER
};
 
顺子的适配(单顺,双顺,三顺):
顺子,必须是3~A之间的连续五个或五个以上的牌组成。所以我们可以根据牌的总数来确定是多少顺,然后在3~A之间循环判断一次就可以确定有多少种适配方式。
首先初始化assistValue:
// needLaiziCount 表示当前这张牌如果适配当前牌型的话,需要的癞子牌数。
// 单顺:needLaiziCount = 1
// 双顺:needLaiziCount = 2
// 三顺:needLaiziCount = 3
        
for(int i=0; i<assistValue.length; i++) {
    assistValue[i] = needLaiziCount;
}
然后根据singleSet, pairSet, threeSet, fourSet的值来填充assistValue的值对应值。
// 在配单顺的时候,不应该出现pairSet.length>0或者threeSet.length>0或者fourSet.length>0的情况。
// 同样在配双顺的时候,不应该出现threeSet.length>0或者fourSet.length>0的情况。
// 同样在配三顺的时候,不应该出现fourSet.length>0的情况。
// 在配单顺,双顺,三顺的时候也不应该出现JOKER,或者TWO的牌。
// 上述情况在配之前就应该做下判断,就可以直接跳过。
for(int i=0; i<singleSet.length; i++) {
    assistValue[singleSet[i].getValue()] = needLaiziCount - 1;
}
for(int i=0; i<pairSet.length; i++) {
    assistValue[pairSet[i].getValue()] = needLaiziCount - 2;
}
for(int i=0; i<threeSet.length; i++) {
    assistValue[threeSet[i].getValue()] = needLaiziCount - 3;
}
for(int i=0; i<fourSet.length; i++) {
    assistValue[fourSet[i].getValue()] = needLaiziCount - 4;
}
接下来就开始配牌啦:
        // 单顺:STRAIGHT_SINGLE_COUNT = 1;双顺:STRAIGHT_SINGLE_COUNT = 2;三顺:STRAIGHT_SINGLE_COUNT = 3;
        int straightLength = inputCards.length/STRAIGHT_SINGLE_COUNT;
        int startIndex = straightLength + 2;

        for(int i=startIndex; i<=PokerRank.ACE.getValue(); i++) {
            int valuesCount = 0;
            for(int j=i; j<i+straightLength; j++) {
                valuesCount += assistValue[j];
            }

            if(valuesCount != laiziSet.length) {
                continue; // 不可适配
            }

            // 开始适配
            List<PokerCard> replaceCards = new ArrayList<PokerCard>();
            for(int j=i; j<i+straightLength; j++) {
                for(int k=0; k<assistValue[j]; k++) {
                    // getPokerCard(PokerRank rank) 表示获取一个牌值为rank,花色与当前手牌中不重复的PokerCard
                    replaceCards.add(getPokerCard(sAssistRanks[j]));
                }
            }
            
            // 将本次适配的结果保存起来。
            sAllReplaceCards.add(replaceCards);
        }

OK到现在为止,单双三顺的牌就判断完毕(其中的一些细节部分需要读者自己去考虑)。

 

三带一以及三带一的飞机,三带对以及三带对的飞机牌型适配:

飞机去掉翅膀,就是一个三个的连子,而单个的三张,可以看成一个特殊的连子。因此我们首先适配飞机的部分,然后适配翅膀的部分。

预处理:如果是三带对及三带对的飞机,可以先将单张的配成对子。 当然,这几种牌型fourSet.length都不能大于0。

配飞机的方法同配顺子一样(大家琢磨琢磨)。

配翅膀就好办,只要不跟当前手中的牌重复就是啦。

这个就交给博友们自己思考吧。