Began versioning.
[poker.git] / DECK.py
1 from random import randint, choice, seed
2 from combinations import *
3
4 '''
5 Cards in the deck is stored as (n,m), where n is the rank number 
6 n=[0,12], m=[0,3], with n defined to be the card value and m as the suit. 
7
8  Examples: 
9  (0,1)  is a two of hearts, 
10  (11,0) is a king of spades,
11  (12,3) is an Ace of clubs
12 '''
13
14 DECK = dict().fromkeys(range(52))
15 for c in DECK: DECK[c] = (c/4,c%4)
16
17 ''' Fisher-Yates shuffle from Knuth '''
18 def shuffle(DECK):
19     for n in range(1,52)[::-1]:
20         k = randint(0,n-1)
21         DECK[k], DECK[n] = DECK[n], DECK[k]
22
23 '''
24 To determine which five card hand is stronger according to standard poker
25 rules, each hand is given a score and a pair rank. The score cooresponds to
26 the poker hand (Straight Flush=8, Four of a Kind=7, etc...). The pair score
27 is used to break ties of score. The pair score creates a set of pairs then
28 rank sorts within each set. Examples:
29
30 Hand A: 7575K
31 Hand B: 885A5
32
33 Pair Score A: [ (2, [7,5]), (1, [K]) ]
34 Pair Score B: [ (2, [8,5]), (1, [A]) ]
35
36 Hand A: AKQJ2
37 Hand B: 2345A
38
39 Pair Score A: [ (1, [A,K,Q,J,2]) ]
40 Pair Score B: [ (1, [A,5,4,3,2]) ]
41 '''
42
43 def pairScore(cards):
44     P = dict().fromkeys(cards,0)
45     for c in cards: P[c] += 1
46     V = sorted(set(P.values()))[::-1]
47     return [[v,sorted([c for c in set(P) if P[c]==v])[::-1]] for v in V]
48
49 def top_pairScore(P1,P2):  # Only compare to cards with same score!
50     for p1, p2 in zip(P1,P2):
51         if   p1[1]>p2[1]: return  1
52         elif p1[1]<p2[1]: return -1
53     return 0
54
55 def isFullHouse(P0): return P0==(3,2)
56 def isPair4(P0):     return P0==(4,1)
57 def isPair3(P0):     return P0==(3,1)
58 def isdoublePair2(P0,P1): return P0==(2,1) and len(P1[0])==2
59 def isPair2(P0,P1):       return P0==(2,1) and len(P1[0])==1
60 def isFlush(suit): return len(set(suit)) == 1
61 def isSt(cards)  :  # Handles the low Ace as well
62     cards = sorted(cards)
63     diff  = [(x2-x1) for x1,x2 in zip(cards,cards[1:])]
64     return len([x for x in diff if x==1])==4 or cards==[0,1,2,3,12]
65
66 def handScore(H):
67     cards,suit = zip(*H)
68     P     = pairScore(cards)
69     P0,P1 = zip(*P)
70
71     score = 0
72     S,F   = isSt(cards), isFlush(suit)
73
74     if    isPair2(P0,P1)      : score = 1   
75     elif  isdoublePair2(P0,P1): score = 2
76     elif  isPair3(P0)         : score = 3
77     elif S and not F          : score = 4
78     elif F and not S          : score = 5
79     elif isFullHouse(P0)      : score = 6
80     elif isPair4(P0)          : score = 7
81     elif S and F              : score = 8
82     return [score, P]
83
84
85 '''
86  To determine who wins a poker hand, given two players hole cards, each player 
87  determines the best hand they can make within their 7 choose 5 = 21 possible five 
88  card combinations. Then each players best hands are put against each other. 1, -1 
89  or 0 is returned if player A wins, B wins, or ties respectively. 
90 '''
91
92 # This function prevents call xunqiueCombinations a bajillon times
93 HAND_COMBOS = list(xuniqueCombinations(range(7),5))
94 def pull_hand(H): 
95     for L in HAND_COMBOS: yield [H[n] for n in L]
96
97 def top_hand_reduce(P1,P2):
98     H1, A = P1
99     H2, B = P2
100     if   A[0]>B[0]: return  H1,A
101     elif A[0]<B[0]: return  H2,B
102     pairscore = top_pairScore(A[1],B[1])
103     if   pairscore > 0: return H1,A
104     elif pairscore < 0: return H2,B
105     return H1,A
106
107 def top_hand_score(A,B):
108     A.score,B.score = handScore(A.score), handScore(B.score)
109     if   A.score[0]>B.score[0]: return  1
110     elif A.score[0]<B.score[0]: return -1
111     return top_pairScore(A.score[1],B.score[1])
112
113 def HOLDEM_score(A, B, board): 
114     A.score = reduce(top_hand_reduce,[(H,handScore(H)) for H in pull_hand(A.hole+board)])[0]
115     B.score = reduce(top_hand_reduce,[(H,handScore(H)) for H in pull_hand(B.hole+board)])[0]
116     return top_hand_score(A,B)
117
118 # ***************************************
119 # Returns the hand in a human readable format (pretty-print)
120 def pp_hand(cards):
121     S = ''
122     for c in cards:
123         if   c[0]==8:  S+='T'
124         elif c[0]==9:  S+='J'
125         elif c[0]==10: S+='Q'
126         elif c[0]==11: S+='K'
127         elif c[0]==12: S+='A'
128         else         : S+=str(c[0]+2)
129         
130         if   c[1]==0 : S+='s'
131         elif c[1]==1 : S+='d'
132         elif c[1]==2 : S+='c'
133         else         : S+='h'
134         S += ' '
135     return S
136
137 def pp_score(A):
138     try:
139         S = ''
140         if A.score[0] == 0: S = 'HighCard '
141         elif A.score[0] == 1: S = 'Pair '
142         elif A.score[0] == 2: S = 'TwoKind '
143         elif A.score[0] == 3: S = 'ThreeKind '
144         elif A.score[0] == 4: S = 'Straight '
145         elif A.score[0] == 5: S = 'Flush '
146         elif A.score[0] == 6: S = 'FullHouse '
147         elif A.score[0] == 7: S = 'FourKind '
148         elif A.score[0] == 8: S = 'StFlush '
149         S += str(A.score[1])
150         return S
151     except:
152         return "INCOMPLETE"