6
\$\begingroup\$

Challenge:

In the last stage of a two players Texas hold 'em, given a two-card hand and five cards on table, determine your probability to win versus an opponent by the standard ranking of poker hands.

Input:

Seven cards (sdtin or arguments). The first two cards are your hand while the last five are on the table. Each card will be a two letter string of the form RS where R is rank and S is suit. The ranks range from 2-9, T for ten, and J, Q, K, and A for Jack, Queen, King, and Ace respectively. The suits are H, D, C, S for Hearts, Diamonds, Clubs, and Spades respectively.

Output:

The probability to win (stdout or return): from '0.00' to '1.00'.

Input to Output Examples:

AH 7C  KH QH JH TH 6C -> 1.00

Explanation: You have a royal flush, 100% winning chances.

9H 7C  KH QH JH TH 6C -> 0.96

Explanation: You have a straight flush, your opponent can only win with an AH, 95.6% winning chances.

Rules and Clarifications:

  • There are 52 different cards. You can refer to Texas hold 'em to get the idea.
  • Your opponent has 2 unknown cards (out of 45 left). That's 990 possibilities with equal probability.
  • You are free to decide if Ties count or don't count in the calculation of the probability.
  • As this is code golf, the shortest answer wins.
\$\endgroup\$
  • \$\begingroup\$ How should a potential tie be handled in the calculation? And given that you're asking about probabilities, it would be clearer if you made explicit your prior that the opponent holds each of the 990 possible pairs with equal probability. \$\endgroup\$ – Peter Taylor Jul 9 '14 at 7:06
  • \$\begingroup\$ @PeterTaylor I made "ties" free so you can shorten your code appropriately. \$\endgroup\$ – Cœur Jul 9 '14 at 7:15
  • \$\begingroup\$ @Cœur - I just want to give you a kudos on the challenge... I've spent about 2.5 hours so far trying to solve this. If I do solve it I think I'll need gzip to make it small! ;-) \$\endgroup\$ – scunliffe Jul 13 '14 at 14:31
  • \$\begingroup\$ @scunliffe I was thinking of simplifying the challenge by only outputting 0 if you have less than 50% chances winning, or 1 if you have more than 50% chances winning, then I realized some people would hardcode the combinations somehow, compacting it with a hash-table maybe. \$\endgroup\$ – Cœur Jul 15 '14 at 12:13
1
\$\begingroup\$

Python (not golfed)

import itertools as t

values = '23456789TJQKA'
suits = 'HDCS'

def value(c):
    return values.find(c[0])

def suit(c):
    return c[1]

def amounts(h):
    vals = map(value, h)
    return [vals.count(v) for v in set(vals)]

def score(h):
    vals = sorted(map(value, h))
    return sum(len(values)**i * v for i, v in enumerate(vals))

def straightFlush(h):
    return straight(h) and flush(h)

def fourOfAKind(h):
    return 4 in amounts(h)

def fullHouse(h):
    return 3 in amounts(h) and 2 in amounts(h)

def flush(h):
    return len(set(map(suit, h))) == 1

def straight(h):
    vals = sorted(map(value, h))
    if vals[0] == 0 and vals[-1] == len(values) - 1: #if there's a two and an ace
        vals = vals[:-1] #then remove the ace
    return map(lambda x: x + 1, vals[:-1]) == vals[1:]

def threeOfAKind(h):
    return 3 in amounts(h)

def twoPair(h):
    return amounts(h).count(2) == 2

def pair(h):
    return 2 in amounts(h)

def highCard(h):
    return True

def compare(h1, h2): #return True if h1 wins, False otherwise
    funcs = [straightFlush, fourOfAKind, fullHouse, flush, straight, threeOfAKind, twoPair, pair, highCard]
    for f in funcs:
        r1, r2 = f(h1), f(h2)
        if r1 and not r2:
            return True
        elif r2 and not r1:
            return False
        elif r1 and r2:
            s1, s2 = score(h1), score(h2)
            if s1 > s2:
                return True
            elif s2 > s1:
                return False
    return False #tie case

def bestHand(cards):
    hands = list(t.combinations(cards, 5))
    bestHand = hands[0]
    for h in hands:
        if compare(h, bestHand):
            bestHand = h
    return bestHand

visible = input().split()
table = tuple(visible[2:])
visible = set(visible)
deck = set([c[0] + c[1] for c in t.product(values, suits)]) - visible

hand = bestHand(visible)

wins = 0
total = 0
for c1 in deck:
    for c2 in deck:
        if c1 == c2:
            break
        otherHand = bestHand(table + (c1, c2))
        if compare(hand, otherHand):
            wins += 1
        total += 1

print '%.2f' % (float(wins) / total)

#use examples
# input "AH 7C  KH QH JH TH 6C" --> 1.00
# input "9H 7C  KH QH JH TH 6C" --> 0.96

Could obviously be immensely shorter but there's not much competition yet. I prefer it this way; simple and readable. It counts ties as losses.

\$\endgroup\$
  • \$\begingroup\$ Does this correctly break a tie when both players have a straight one with ace low and the other with anything higher than 5? I think the second players wins in this case. \$\endgroup\$ – nutki Jan 5 '15 at 10:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.