博客
这个方法是在python的一个德州库(PyPokerEngine)里看到的,并不是原创,但是原方法bug特别的多,直接导致牌局的胜负判断错误,所以我对原方法进行了一些改进,但是原方法的思想非常厉害,所以值得推荐一番。
原库的思想是这样的:
int有32位,扑克牌最大的牌是K,值是13,需要用4位来表示,所以利用高16位表示牌型(如一对,两对,顺子等)值,用低16位表示关键牌的值(如一对中,对子的牌大小)。
[牌型大小(16bit)] [关键牌1(4bit)] [关键牌2(4bit)] [手牌1(4bit)] [手牌1(4bit)]
比如 A K Q 5 5,这副牌,可以使用二进制 0000 0000 0000 0001 0000 0101 0001 1101表示。
其中,高16位 0000 0000 0000 0001 表示一对,如果是两对则用 0000 0000 0000 0010 表示,这样的好处是,牌型大的牌无需比较牌面的大小,两对行成的int永远大于一对。其中低16位中的前8位表示的是牌中关键牌的值,这里 0000 0101 表示的是一对5中的5,低16位中的后8位表示的是手牌的大小,我这里假设A K是手牌,所以值为 0001 1101。
乍一看原库的思想的确很妙,不管是什么牌型直接用一个int数字就能表示并且轻易的比较,但是在有的牌局中,这个方法却出现了致命的bug。
在牌型关键牌平局的时候,只能比手牌,这样的比较肯定是错误的。在德州的规则中,如果两名玩家都是一对5,那么需要比较剩下的3张最大的牌,这个时候在原库思想中比的是玩家手牌,但是当桌面上5张牌就是最大的牌的时候,双方应该是平局,手牌不应该左右牌局的胜负方。比如这样一局牌:
公共牌:A K Q J J
玩家1手牌:10 9
玩家2手牌:8 7
这局牌在正确的规则中,应该是平局。但是在原库的代码中,判定玩家1获胜。所以需要修改一下原库的部分算法。
高16位的算法依然保持不变:
public final int HIGH_CARD = 0; // 高牌
public final int ONE_PAIR = 1 << 8; // 一对
public final int TWO_PAIR = 1 << 9; // 两对
public final int THREE_CARD = 1 << 10; // 三张
public final int STRAIGHT = 1 << 11; // 顺子
public final int FLUSH = 1 << 12; // 同花
public final int FULL_HOUSE = 1 << 13; // 葫芦
public final int FOUR_CARD = 1 << 14; // 四张
public final int STRAIGHT_FLUSH = 1 << 15; // 同花顺
public final int ROYAL_FLUSH = 1 << 16; // 皇家同花顺
重点就是接下来的每一种牌型的后16位写法,全部都不需要保存手牌了。
同花顺:只需要保存同花顺中最小牌在后16位就可以了,比如 10 J Q K A,那么只需要保存10,就能判断同花顺的大小。
四张:保存相同的4张牌的牌的大小和剩下最大的那张牌的大小,比如 A A A A K,那么只需要保存 A K 0 0。
葫芦:保存3张相同牌的牌大小和另外一对的牌大小,比如 Q Q Q J J,那么保存的就是Q J 0 0。
顺子:同同花顺存储方法。
同花:5张牌,最大的是A(用14表示),最多5张牌加起来最大的数字是69(并不可能在同花中出现),用8位二进制完全可以存起来,那么同花就保存最大5张牌的和。
三张:依次保存的是,三张相同牌的牌大小,剩下两张最大的牌大小,如Q Q Q J 9,那么按顺序存的是Q J 9 0。
两对:依次保存的是,较大的一对的牌大小,较小的一对的牌大小,剩下最大的牌的大小,如 Q Q J J 10,那么按顺序保存的 Q J 10 0。
一对:依次保存的是,一对的牌大小,剩下3张最大的牌的大小,如 Q Q J 9 8,那么保存的是 Q J 9 8。
高牌:同同花存储方法。
以三张举例,对应的代码如下:
// 搜索卡牌列表中是否存在三张,并返回三张的牌面大小,不存在返回-1
private static int searchThreeCard(List<Card> cards) {
int bestRank = -1;
Map<Integer, List<Card>> rankGroup = cards.stream().collect(Collectors.groupingBy(Card::getRank, Collectors.toList()));
for (Integer rank : rankGroup.keySet()) {
List<Card> cardList = rankGroup.get(rank);
if (cardList.size() >= 3 && rank > bestRank) {
bestRank = rank;
}
}
return bestRank;
}
// 判断存在三张之后,返回三张所对应的牌力int值
if (GameUtil.searchThreeCard(cards) != -1) {
int threeCard = GameUtil.searchThreeCard(cards) << 4;
List<Integer> topCardRank = getTopCardRank(2, cards, threeCard >> 4);
if (topCardRank == null) {
return (THREE_CARD | threeCard) << 8;
}
return ((THREE_CARD | threeCard) << 8) | ((topCardRank.get(0) << 4) | topCardRank.get(1));
}
这样就能够在使用最小内存和最快的比较速度的情况下,判断出哪一位玩家获胜了。