【问题标题】:Algorithm to determine the winner of a Texas Hold'em Hand确定德州扑克手牌获胜者的算法
【发布时间】:2011-03-14 00:03:11
【问题描述】:

好的,所以我正在为我的高级项目制作德州扑克 AI。我已经创建了 gui 和投注/交易程序,但我已经到了需要确定谁赢了这手牌的部分,而且我不知道解决这个问题的最佳方法。我正在使用 python 顺便说一句。 ATM 我有 2 个列表,一个用于 7 个玩家卡,一个用于 7 个计算机卡。目前所有的牌都以结构体的形式存储在列表中,如 {'Number': XX, 'Suit': x},其中 number 为 2-14,suit 为 1-4。我打算解决这个问题的方法是为每种手型创建一个函数,从最高的开始。例如。 self.CheckRoyal(playerCards),并手动浏览列表并评估是否实现了皇家同花顺。必须有更好的数字方式来做到这一点。

【问题讨论】:

标签: python artificial-intelligence poker


【解决方案1】:

http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup

您将获得的最佳算法是在大小为 100 MB 的查找表中查找 7 次(如果我没记错的话)

【讨论】:

  • 您是在谈论生成包含 3200 万个条目的表格并根据您的手遵循路径的二加二方法吗?如果是这样,我将如何使其适应 python,因为它使用函数指针?
  • @ULcajun:上面链接中列出的pokersource.sourceforge.net 具有python 绑定。
  • 这里提供了要求不高且至少是优秀的评估者:code.google.com/p/specialkpokereval。下面也有描述。
  • 非常好的文章!除了有价值的见解,这也是一本非常有趣的读物!
  • 这里似乎还保留着一些历史。 web.archive.org/web/20131109140408/http://…
【解决方案2】:
import itertools
from collections import Counter

# gets the most common element from a list
def Most_Common(lst):
    data = Counter(lst)
    return data.most_common(1)[0]



# gets card value from  a hand. converts A to 14,  is_seq function will convert the 14 to a 1 when necessary to evaluate A 2 3 4 5 straights
def convert_tonums(h, nums = {'T':10, 'J':11, 'Q':12, 'K':13, "A": 14}):
    for x in xrange(len(h)):

        if (h[x][0]) in nums.keys():

            h[x] = str(nums[h[x][0]]) + h[x][1]

    return h


# is royal flush
# if a hand is a straight and a flush and the lowest value is a 10 then it is a royal flush
def is_royal(h):
    nh = convert_tonums(h)
    if is_seq(h):
        if is_flush(h):
            nn = [int(x[:-1]) for x in nh]
            if min(nn) == 10:
                return True

    else:
        return False


# converts hand to number valeus and then evaluates if they are sequential  AKA a straight  
def is_seq(h):
    ace = False
    r = h[:]

    h = [x[:-1] for x in convert_tonums(h)]


    h = [int(x) for x in h]
    h = list(sorted(h))
    ref = True
    for x in xrange(0,len(h)-1):
        if not h[x]+1 == h[x+1]:
            ref =  False
            break

    if ref:
        return True, r

    aces = [i for i in h if str(i) == "14"]
    if len(aces) == 1:
        for x in xrange(len(h)):
            if str(h[x]) == "14":
                h[x] = 1

    h = list(sorted(h))
    for x in xrange(0,len(h)-1):
        if not h[x]+1 == h[x+1]:

            return False
    return True, r

# call set() on the suite values of the hand and if it is 1 then they are all the same suit
def is_flush(h):
    suits = [x[-1] for x in h]
    if len(set(suits)) == 1:
        return True, h
    else:
        return False


# if the most common element occurs 4 times then it is a four of a kind
def is_fourofakind(h):
    h = [a[:-1] for a in h]
    i = Most_Common(h)
    if i[1] == 4:
        return True, i[0]
    else:
        return False


# if the most common element occurs 3 times then it is a three of a kind
def is_threeofakind(h):
    h = [a[:-1] for a in h]
    i = Most_Common(h)
    if i[1] == 3:
        return True, i[0]
    else:
        return False


# if the first 2 most common elements have counts of 3 and 2, then it is a full house
def is_fullhouse(h):
    h = [a[:-1] for a in h]
    data = Counter(h)
    a, b = data.most_common(1)[0], data.most_common(2)[-1]
    if str(a[1]) == '3' and str(b[1]) == '2':
        return True, (a, b)
    return False

# if the first 2 most common elements have counts of 2 and 2 then it is a two pair
def is_twopair(h):
    h = [a[:-1] for a in h]
    data = Counter(h)
    a, b = data.most_common(1)[0], data.most_common(2)[-1]
    if str(a[1]) == '2' and str(b[1]) == '2':
        return True, (a[0], b[0])
    return False


#if the first most common element is 2 then it is a pair
# DISCLAIMER: this will return true if the hand is a two pair, but this should not be a conflict because is_twopair is always evaluated and returned first 
def is_pair(h):
    h = [a[:-1] for a in h]
    data = Counter(h)
    a = data.most_common(1)[0]

    if str(a[1]) == '2':
        return True, (a[0]) 
    else:
        return False

#get the high card 
def get_high(h):
    return list(sorted([int(x[:-1]) for x in convert_tonums(h)], reverse =True))[0]

# FOR HIGH CARD or ties, this function compares two hands by ordering the hands from highest to lowest and comparing each card and returning when one is higher then the other
def compare(xs, ys):
  xs, ys = list(sorted(xs, reverse =True)), list(sorted(ys, reverse = True))

  for i, c in enumerate(xs):
    if ys[i] > c:
        return 'RIGHT'
    elif ys[i] < c:
        return 'LEFT'

  return "TIE"


# categorized a hand based on previous functions
def evaluate_hand(h):

    if is_royal(h):
        return "ROYAL FLUSH", h, 10
    elif is_seq(h) and is_flush(h) :
        return "STRAIGHT FLUSH", h, 9 
    elif is_fourofakind(h):
        _, fourofakind = is_fourofakind(h)
        return "FOUR OF A KIND", fourofakind, 8
    elif is_fullhouse(h):
        return "FULL HOUSE", h, 7
    elif is_flush(h):
        _, flush = is_flush(h)
        return "FLUSH", h, 6
    elif is_seq(h):
        _, seq = is_seq(h)
        return "STRAIGHT", h, 5
    elif is_threeofakind(h):
        _, threeofakind = is_threeofakind(h)
        return "THREE OF A KIND", threeofakind, 4
    elif is_twopair(h):
        _, two_pair = is_twopair(h)
        return "TWO PAIR", two_pair, 3
    elif is_pair(h):
        _, pair = is_pair(h)
        return "PAIR", pair, 2 
    else:
        return "HIGH CARD", h, 1



#this monster function evaluates two hands and also deals with ties and edge cases
# this probably should be broken up into separate functions but aint no body got time for that
def compare_hands(h1,h2):
    one, two = evaluate_hand(h1), evaluate_hand(h2)
    if one[0] == two[0]:

        if one[0] =="STRAIGHT FLUSH":

            sett1, sett2 = convert_tonums(h1), convert_tonums(h2)
            sett1, sett2 = [int(x[:-1]) for x in sett1], [int(x[:-1]) for x in sett2]
            com = compare(sett1, sett2)

            if com == "TIE":
                return "none", one[1], two[1]
            elif com == "RIGHT":
                return "right", two[0], two[1]
            else:
                return "left", one[0], one[1]

        elif one[0] == "TWO PAIR":

            leftover1, leftover2 = is_twopair(h1), is_twopair(h2)
            twm1, twm2 = max([int(x) for x in list(leftover1[1])]), max([int(x) for x in list(leftover2[1])])
            if twm1 > twm2:
                return "left", one[0], one[1]
            elif twm1 < twm2:
                return "right", two[0], two[1]


            if compare(list(leftover1[1]), list(leftover2[1])) == "TIE":
                l1 = [x[:-1] for x in h1 if x[:-1] not in leftover1[1]]
                l2 = [x[:-1] for x in h2 if x[:-1] not in leftover2[1]]
                if int(l1[0]) == int(l2[0]):
                    return "none", one[1], two[1]
                elif int(l1[0]) > int(l2[0]):
                    return "left", one[0], one[1]
                else:
                    return "right", two[0], two[1]
            elif compare(list(leftover1[1]), list(leftover2[1]))  == "RIGHT":
                return "right", two[0], two[1]
            elif  compare(list(leftover1[1]), list(leftover2[1]))  == "LEFT":
                return "left", one[0], one[1]


        elif one[0] == "PAIR":
            sh1, sh2 = int(is_pair(h1)[1]), int(is_pair(h2)[1])
            if sh1 == sh2:

                c1 = [int(x[:-1]) for x in convert_tonums(h1) if not int(sh1) == int(x[:-1])]
                c2 = [int(x[:-1]) for x in convert_tonums(h2) if not int(sh1) == int(x[:-1])]
                if compare(c1, c2) == "TIE":
                    return "none", one[1], two[1]
                elif compare(c1, c2) == "RIGHT":
                    return "right", two[0], two[1]
                else:
                    return "left", one[0], one[1]




            elif h1 > h2:
                return "right", two[0], two[1]
            else:
                return "left", one[0], one[1]

        elif one[0] == 'FULL HOUSE':

            fh1, fh2 =  int(is_fullhouse(h1)[1][0][0]), int(is_fullhouse(h2)[1][0][0])
            if fh1 > fh2:
                return "left", one[0], one[1]
            else:
                return "right", two[0], two[1]
        elif one[0] == "HIGH CARD":
            sett1, sett2 = convert_tonums(h1), convert_tonums(h2)
            sett1, sett2 = [int(x[:-1]) for x in sett1], [int(x[:-1]) for x in sett2]
            com = compare(sett1, sett2)
            if com == "TIE":
                return "none", one[1], two[1]
            elif com == "RIGHT":
                return "right", two[0], two[1]
            else:
                return "left", one[0], one[1]



        elif len(one[1]) < 5:
            if max(one[1])  == max(two[1]):
                return "none", one[1], two[1]
            elif max(one[1]) > max(two[1]):
                return "left", one[0], one[1]
            else:
                return "right", two[0], two[1]
        else:
            n_one, n_two = convert_tonums(one[1]), convert_tonums(two[1])
            n_one, n_two = [int(x[:-1]) for x in n_one], [int(x[:-1]) for x in n_two]

            if max(n_one)  == max(n_two):
                return "none", one[1], two[1]
            elif max(n_one) > max(n_two):
                return "left", one[0], one[1]
            else:
                return "right", two[0], two[1]
    elif one[2] > two[2]:
        return "left", one[0], one[1]
    else:
        return "right", two[0], two[1]



'''
a = ['QD', 'KD', '9D', 'JD', 'TD'] 
b = ['JS', '8S', 'KS', 'AS', 'QS']
print compare_hands(a,b)
'''

【讨论】:

  • 看起来很好很清晰。但是 hand1 和 hand2 参数的例子是什么?应该如何将卡片发送到函数以进行比较?
【解决方案3】:

ralu 帖子中使用的方法是迄今为止我见过的最好的替代方法。我在自己的项目中使用了这种方法,而且速度非常快。

悬崖:

进行一些预处理,以生成一个表格,其中包含每个不同扑克手的一个值。确保表格按手力排序。

每个卡值都有一个对应的素值。该表由手中每个牌值的乘积来索引。因此,要找到手牌 AAAAK 的值,您需要计算素数乘法并将其用作表格的索引:

int prime = getPrime(hand); // Calculates A.getPrime()...*K.getPrime();
int value = table[prime];

(对不起 java 语法)。

这样一来,AAAAK 与 KAAAA 是同一手牌,不需要 5 点牌桌。

请注意,您需要通过您可以选择的7张牌中最好的5张牌的所有组合,找到最大的值,这就是手牌的真正价值。

您使用不同的表进行刷新。

表格变得非常强大,因为这个实现有很多浪费的单元格。为了解决这个问题,您可以在预处理期间创建一个映射,将大素数映射到整数值,并将其用作您的源。

【讨论】:

  • 这里提供了要求不高且至少是优秀的评估者:code.google.com/p/specialkpokereval。我在此页面上的回答中也进行了描述。享受吧!
  • 看起来很有趣。感谢分享!
【解决方案4】:

可以在here 找到现成的德州扑克 7 和 5 牌评估器的示例,并进一步解释 here。这可能会帮助您提高性能。欢迎在其中找到的电子邮件地址提供所有反馈。

【讨论】:

    【解决方案5】:

    Monte Carlo?这是我看到here 的第一个建议。这是另一个高级项目。简单而缓慢,但除此之外,您可能正在查看一些我不会假装知道太多的复杂组合。

    【讨论】:

    • 您引用的论文试图解决一个更有趣的问题。提问者只是想要一个算法来确定给定手牌的赢家(例如,同花击败顺子)。
    • 蒙特卡洛用于通过模拟数以百万计的扑克游戏来找到一手牌对抗其他手牌的近似胜率。它使用了 OP 想要的算法:)
    • 啊,是的。你是对的!显然我读得太快了,回答了出现在我脑海中的问题,而不是被问到的问题……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多