2011-03-14 2 views
6

좋아, 그래서 내가 수석 프로젝트에 텍사스 ​​홀덤 AI를 만들고있다. 나는 gui 및 내기/거래 절차를 만들었지 만 누가 손을 determine 것인지 결정해야하는 부분에 도달했으며이를 해결할 수있는 최선의 방법을 모릅니다. 나는 파이썬 btw를 사용하고 있습니다. ATM에는 7 개의 컴퓨터 카드 중 하나, 7 개의 컴퓨터 카드 중 하나에 대해 2 개의 목록이 있습니다. 현재 모든 카드는 { 'Number': XX, 'Suit': x}와 같이 목록에 구조체로 저장됩니다. 숫자는 2-14, 수트는 1-4입니다. 이 방식에 접근하는 방법은 각 손 유형에 대해 가장 높은 숫자부터 시작하는 함수를 만드는 것입니다. 예 : self.CheckRoyal (playerCards)을 선택하고 수동으로 목록을 검토하여 왕실 플러시가 달성되었는지 평가합니다. 이것을하기 위해서는 수치 적으로 더 좋은 방법이 있어야합니다.텍사스 홀덤 핸드의 승자를 결정하는 알고리즘

+0

무엇이 문제입니까? – Gabe

+0

@UCLcajun : http://code.google.com/p/specialkpokereval/ 도움이 될 수 있습니다. – SK9

+0

파이썬에서 카드, 데크 등을 다루는 쉬운 라이브러리 : [https://github.com/worldveil/deuces](https://github.com/worldveil/deuces)]. – lollercoaster

답변

7

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

최상의 알고리즘은 크기 1백메가바이트의 조회 테이블에서 7 개 모습입니까? 이것이 내가 처음으로 제안한 것입니다. here. 또 다른 선임 프로젝트입니다. 간단하고 느리지 만, 그렇지 않으면 당신은 아마도 내가 알지 못하는 복잡한 조합론을보고있을 것입니다.

+0

3200 만 개의 항목을 가진 테이블을 생성하고 손을 기반으로하는 경로를 따르는 두 가지 방법에 대해 이야기하고 있습니까? 그렇다면 함수에 대한 포인터를 사용하기 때문에 파이썬에 어떻게 적용 할 것인가? – ULcajun

+0

@ULCajun : 위의 링크에 나열된 http://pokersource.sourceforge.net/에는 파이썬 바인딩이 있습니다. –

+2

덜 까다롭고 평가가 좋은 평가자는 http://code.google.com/p/specialkpokereval/입니다. 아래에 설명되어 있습니다. – SK9

0

Monte Carlo을 (내가 정확히 기억한다면) 당신이 얻을 것이다

+2

참조하는 용지가 훨씬 더 흥미로운 문제를 해결하려고합니다. 질문자는 알고리즘이 주어진 손의 승자를 결정하기를 원합니다 (예 : flush가 직선으로 뛰는 경우). – dfan

+2

몬테카를로는 수백만 개의 포커 게임을 시뮬레이션하여 다른 사람들과 비슷한 손재수를 찾는 데 사용됩니다. 그것은 OP가 원하는 알고리즘을 사용합니다 : –

+0

아, 네. 당신이 맞습니다! 분명히 질문을 너무 빨리 읽고 질문에 답한 것이 아니라 내 머리에 나타난 질문에 대답했습니다. – Ricky

2

ralu의 게시물에 사용 된 방법은 내가 본 가장 훌륭한 대안입니다. 저는이 방법을 제 자신의 프로젝트에서 사용했고, 매우 빠릅니다.

절벽 :

는 각각 별개의 포커 손을위한 하나 개의 값을 포함하는 테이블을 생성하기 위해, 몇 가지 전처리를 수행합니다. 테이블이 수작업으로 정렬되어 있는지 확인하십시오.

각 카드 값에는 상응하는 소수가 있습니다. 테이블은 손에있는 각 카드 - 값의 곱에 의해 인덱싱됩니다. 그래서 손 AAAAK의 가치를 찾기 위해, 당신은 주요 곱셈을 계산하고 테이블에 대한 인덱스로 사용 :

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

(자바 구문 죄송합니다).

이렇게하면 AAAAK은 KAAAA와 같은 손이며 5 차원 테이블이 필요하지 않습니다.

최상의 5 카드 패의 모든 조합을 선택할 수있는 7 장의 카드로 가야합니다. 가장 큰 값은 손의 실제 가치입니다.

다른 테이블을 사용하여 플래시합니다.

이 구현으로 낭비되는 셀이 많기 때문에 테이블이 꽤 잘게됩니다. 이 문제를 해결하기 위해 사전 처리 중에 맵을 생성 할 수 있습니다.이 맵은 큰 소수 값을 정수 값으로 매핑하고 대신 소스로 사용합니다.

+1

덜 까다로운 평가자와 최소한 좋은 평가자가 여기에 있습니다 : code.google.com/p/specialkpokereval. 또한이 페이지에서 내 대답에 설명되어 있습니다. 즐겨! – SK9

+0

흥미로운 것 같습니다. 공유 해줘서 고마워! –

1

준비가 완료된 텍사스 홀덤 7 및 5 카드 평가자의 예는 here이며 자세한 내용은 here입니다. 이는 실적에 도움이 될 수 있습니다. 모든 의견은 전자 메일 주소에서 환영합니다.

1
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) 
''' 
관련 문제