前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >这是一道算法题:如何比较徳州扑克牌大小?(附带python实现)

这是一道算法题:如何比较徳州扑克牌大小?(附带python实现)

原创
作者头像
HullQin
发布2023-07-26 10:55:23
2.2K0
发布2023-07-26 10:55:23
举报
文章被收录于专栏:教你做小游戏教你做小游戏

问题描述

每个玩家有2张牌,公共牌有5张牌,共计7张牌。比牌时,每个玩家要找到自己组成最大牌型的5张,跟其他玩家的最大牌型比大小。

如果我们能给每5张牌算一个分数,分数越大,牌越大,问题就迎刃而解了。

定义手牌

德州扑克中,牌一共有52张,没有大小王。我们用0-51表示52张不同的扑克牌。其中0-51这52个数字表示牌的ID。

以下代码用python实现。

根据ID计算花色

花色有4种,刚好可用最后2位表示4种花色。

代码语言:javascript
复制
def get_card_suit_id(card_id):
    return card_id & 3

根据ID计算数字

ID最后2位是花色,那么右移2位,剩下的信息就是牌的数字了。

代码语言:javascript
复制
def get_card_num_id(card_id):
    return card_id >> 2

核心思路

核心问题,每个玩家要在7张牌中找到5张,用排列组合算法,遍历一遍所有组合,取最大的分数即可。

输入:7张牌的ID。 输出:该玩家最大牌型的分数。

分数 = (牌型, 牌型分)

此外,因为德州扑克的计算规则,是先看牌型,再看数字。所以,我们可以用二维表示分数:(牌型, 该牌型的分数)

比大小时,先比牌型,若牌型一样大,再比后面的分数。

由于皇家同花顺是同花顺的一种,所以牌型有9种,从小到大用0-8表示。

各牌型分

每个牌型比大小的方式不一样,牌型分的计算方式有差别。

  • 8 同花顺:比最大的牌即可。牌型分就是最大牌的数字大小
  • 7 四条:牌型分需要先比四条的数字,再比单牌的数字。所以牌型分是个二维的结构:(四条的数字, 单牌的数字)
  • 6 葫芦:牌型分是个二维的结构:(三条的数字, 一对的数字)
  • 5 同花:牌型分是个五维的结构,从大到小排列的5个牌的数字
  • 4 顺子:牌型分就是最大牌的数字大小
  • 3 三条:牌型分是个三维的结构:(三条的数字, 较大单牌的数字, 较小单牌的数字)
  • 2 两对:牌型分是个三维的结构:(较大对子的数字, 较小对子的数字, 单牌的数字)
  • 1 一对:牌型分是个三维的结构:(对子的数字, 最大单牌的数字, 第二大单牌的数字, 第三大单牌的数字)
  • 0 高牌:牌型分是个五维的结构,从大到小排列的5个牌的数字

排列组合

组合算法不用自己实现,各个编程语言都有库。

这就是根据7张牌,计算max_typemax_value的逻辑。其中get_type()get_value()是我们后面需要实现的。

代码语言:javascript
复制
from itertools import combinations

def get_max_score(seven_cards):
    max_type = -1
    res = []
    for cards in combinations(seven_cards, 5):
        t, v = get_type(cards)
        if t > max_type:
            max_type = t
            res = [(t, v, cards)]
        elif t == max_type:
            res.append((t, v, cards))
    max_value = get_value(*res[0])
    for r in res[1:]:
        value = get_value(*r)
        if value > max_value:
            max_value = value
    return max_type, max_value

这里介绍一个小小的优化点:我们遍历时,并不会计算所有的5张牌组合的max_type和max_value,因为可以先把max_type算出来,得到最大的type后,只需要计算最大type的max_value,省了一些没有必要的计算。

当然由于遇到顺子时,可以很快得到max_value,所以会在get_type时就算出来。

get_type

代码语言:javascript
复制
def get_type(five_cards):
    straight, straight_value = is_straight(five_cards)
    flush = is_flush(five_cards)
    if flush and straight:
        return 8, straight_value
    if flush:
        return 5, None  # sorted([get_card_num_id(c) for c in five_cards], reverse=True)
    if straight:
        return 4, straight_value
    nums = [0] * 13
    for card in five_cards:
        nums[get_card_num_id(card)] += 1
    max_freq = max(nums)
    if max_freq == 1:
        return 0, None  # sorted([get_card_num_id(c) for c in five_cards], reverse=True)
    if max_freq == 4:
        return 7, None  # [nums.index(4), nums.index(1)]
    if max_freq == 3:
        if 2 in nums:
            return 6, None  # [nums.index(3), nums.index(2)]
        return 3, None
    # max_freq == 2
    if nums.count(2) == 1:
        return 1, None
    return 2, None

其中用到了这2个函数:

是否同花 is_flush

代码语言:javascript
复制
def is_flush(five_cards):
    s = get_card_suit_id(five_cards[0])
    for c in five_cards[1:]:
        if s != get_card_suit_id(c):
            return False
    return True

是否顺子 is_straight

代码语言:javascript
复制
def is_straight(five_cards):
    nums = sorted([get_card_num_id(c) for c in five_cards])
    if nums == [0, 1, 2, 3, 12]:
        return True, 3
    n4 = nums[4]
    for i, n in enumerate(nums[:4]):
        if n4 - n != 4 - i:
            return False, 0
    return True, n4

get_value

代码语言:javascript
复制
def get_value(t, v, five_cards):
    if v is not None:
        return v
    if t in (0, 5):
        return sorted((get_card_num_id(c) for c in five_cards), reverse=True)
    nums = [0] * 13
    for card in five_cards:
        nums[get_card_num_id(card)] += 1
    return sorted(list(set(get_card_num_id(c) for c in five_cards)), reverse=True, key=lambda x: (nums[x] << 5) + x)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题描述
  • 定义手牌
    • 根据ID计算花色
      • 根据ID计算数字
      • 核心思路
        • 分数 = (牌型, 牌型分)
          • 各牌型分
          • 排列组合
          • get_type
            • 是否同花 is_flush
              • 是否顺子 is_straight
              • get_value
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档