斗地主之癞子牌型适配方法
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。
配飞机的方法同配顺子一样(大家琢磨琢磨)。
配翅膀就好办,只要不跟当前手中的牌重复就是啦。
这个就交给博友们自己思考吧。