德州扑克解法(2)

最近又做了一个新的德州扑克算法,和之前的还略有不同,
之前的德州扑克算法规则为:玩家手中的两张私有牌必须参与到牌型组合中去,
也就是公共牌最多选择3张,这次做的又不一样了:限度放宽,可以5张牌全部来自于公共牌,
换个角度,也就是从2张私有牌和5张公共牌组合成的7张牌中选择一个最优解.

解法分析

最简单的解法自然是穷举,但是逻辑也很恶心,而且运算量也比较大,PASS掉。

我想到的一种抽丝拨茧的方法是:

  1. 对卡组进行降序排序
  2. 遍历卡组,记录如下特征:最长顺子长度以及起始值、
    3张相同牌的集合、2张相同牌的集合、4张相同牌的值、牌的类型数目
  3. 接下来就是判断过程了
  4. 判断是否有4张相同的牌,如果有,那么在剩下的牌中选张最大的组成四条
  5. 如果可以同花色,且最长顺子长度大于4,判断是否可以组成(皇家)同花顺
  6. 如果有3张相同的牌, 而且同时存在另外一套3张相同的牌或者存在2张相同的牌,
    那么牌型是葫芦
  7. 如果可以同花色,那么牌型是同花
  8. 如果最大顺子长度大于4,那么可以是顺子
  9. 如果存在3张相同的牌,再找两张大牌组成三条
  10. 如果有多组2张相同的牌,那么再找一张最大牌组成两对
  11. 如果只有一组2张相同的牌,那么再找3张最大的牌组成对子
  12. 最后就只可能是高牌了

示例代码

class TaxasPlayerV2(object):
    def __init__(self, cds):
        self.cards = cds
        self.cards.sort(cmp=GameBase.get_card_cmp_func(1), reverse=True)

    def find_best_solution(self):
        max_seq_num = 1
        seq_start_val = self.cards[0].value
        pre = None
        # pairs 和 thirds 存的是该对子或三张的牌值
        pairs = []
        thirds = []
        four = None
        same_count = 1
        card_types = [[], [], [], [], []]
        for card in self.cards:
            val = card.value
            card_types[card.type].append(val)
            if pre:
                if pre == val:
                    # 对子情况
                    same_count += 1
                    if same_count == 2:
                        pairs.append(val)
                    elif same_count == 3:
                        pairs.pop()
                        thirds.append(val)
                    elif same_count == 4:
                        four = val
                        break
                else:
                    # 与前面一张牌值不同
                    same_count = 1
                    if max_seq_num < 5:
                        # 没有组成序列
                        diff = pre - val
                        if diff == 1 or diff == -12:
                            # 顺子情况, 3 -> 2 or A -> k
                            max_seq_num += 1
                        else:
                            # 无法构成序列时重置数据
                            seq_start_val = val
                            max_seq_num = 1
            pre = val
        if four:
            # 找到最大的单牌
            for item in self.cards:
                if item.value != four:
                    return TaxasSolution(ResultType.Four, (four, item.value))
        same_color = None
        for card_list in card_types:
            if len(card_list) > 4:
                same_color = card_list
                break
        # same_color = [card1, card2, ...]
        if same_color and max_seq_num > 4:
            # 判断是否是同花, 相同颜色的已经排好队在same_color中了
            idx = 1
            ng_count = 0
            header = pre = same_color[0] if same_color[0] > 1 else 14
            while idx < len(same_color):
                val = same_color[idx] if same_color[idx] > 1 else 14
                if pre - val == 1:
                    ng_count += 1
                else:
                    ng_count = 1
                    header = val
                if ng_count == 4:
                    break
                idx += 1
                pre = val
            if ng_count >= 4:
                return TaxasSolution(ResultType.RoyalFlush if header == 14 else ResultType.Flush, (header,))
        if thirds:
            if len(thirds) == 2:
                return TaxasSolution(ResultType.Gourd, (thirds[0], thirds[1]))
            if pairs:
                return TaxasSolution(ResultType.Gourd, (thirds[0], pairs[0]))
        if same_color:
            return TaxasSolution(ResultType.SameColor, same_color[:5])
        if max_seq_num > 4:
            return TaxasSolution(ResultType.Sequence, (seq_start_val,))
        if thirds:
            thirds_val = thirds[0]
            flag = [thirds_val]
            for item in self.cards:
                if item.value != thirds_val:
                    flag.append(item.value)
                    if len(flag) == 3:
                        break
            # 再找两个散牌
            return TaxasSolution(ResultType.Three, flag)
        if len(pairs) > 1:
            pair_tag = [pairs[0], pairs[1]]
            for item in self.cards:
                val = item.value
                if val not in pair_tag:
                    pair_tag.append(val)
                    break
            # 使用前两对, 再找一个大散牌
            return TaxasSolution(ResultType.TwoPair, pair_tag)
        elif len(pairs) == 1:
            pair_tag = [pairs[0]]
            for item in self.cards:
                val = item.value
                if val != pairs[0]:
                    pair_tag.append(val)
                    if len(pair_tag) == 4:
                        break
            return TaxasSolution(ResultType.SinglePair, pair_tag)
        return TaxasSolution(ResultType.HighCard, [self.cards[i].value for i in range(0, 5)])