Began versioning.
authorTravis Hoppe <travis.hoppe@gmail.com>
Mon, 6 Dec 2010 20:29:39 +0000 (15:29 -0500)
committerW. Trevor King <wking@drexel.edu>
Mon, 6 Dec 2010 20:29:39 +0000 (15:29 -0500)
DECK.py [new file with mode: 0644]
GAMEPLAY.py [new file with mode: 0644]
PBOT.py [new file with mode: 0644]
combinations.py [new file with mode: 0644]
p1.py [new file with mode: 0755]
p2.py [new file with mode: 0755]

diff --git a/DECK.py b/DECK.py
new file mode 100644 (file)
index 0000000..cff2f71
--- /dev/null
+++ b/DECK.py
@@ -0,0 +1,152 @@
+from random import randint, choice, seed
+from combinations import *
+
+'''
+Cards in the deck is stored as (n,m), where n is the rank number 
+n=[0,12], m=[0,3], with n defined to be the card value and m as the suit. 
+
+ Examples: 
+ (0,1)  is a two of hearts, 
+ (11,0) is a king of spades,
+ (12,3) is an Ace of clubs
+'''
+
+DECK = dict().fromkeys(range(52))
+for c in DECK: DECK[c] = (c/4,c%4)
+
+''' Fisher-Yates shuffle from Knuth '''
+def shuffle(DECK):
+    for n in range(1,52)[::-1]:
+        k = randint(0,n-1)
+        DECK[k], DECK[n] = DECK[n], DECK[k]
+
+'''
+To determine which five card hand is stronger according to standard poker
+rules, each hand is given a score and a pair rank. The score cooresponds to
+the poker hand (Straight Flush=8, Four of a Kind=7, etc...). The pair score
+is used to break ties of score. The pair score creates a set of pairs then
+rank sorts within each set. Examples:
+
+Hand A: 7575K
+Hand B: 885A5
+
+Pair Score A: [ (2, [7,5]), (1, [K]) ]
+Pair Score B: [ (2, [8,5]), (1, [A]) ]
+
+Hand A: AKQJ2
+Hand B: 2345A
+
+Pair Score A: [ (1, [A,K,Q,J,2]) ]
+Pair Score B: [ (1, [A,5,4,3,2]) ]
+'''
+
+def pairScore(cards):
+    P = dict().fromkeys(cards,0)
+    for c in cards: P[c] += 1
+    V = sorted(set(P.values()))[::-1]
+    return [[v,sorted([c for c in set(P) if P[c]==v])[::-1]] for v in V]
+
+def top_pairScore(P1,P2):  # Only compare to cards with same score!
+    for p1, p2 in zip(P1,P2):
+        if   p1[1]>p2[1]: return  1
+        elif p1[1]<p2[1]: return -1
+    return 0
+
+def isFullHouse(P0): return P0==(3,2)
+def isPair4(P0):     return P0==(4,1)
+def isPair3(P0):     return P0==(3,1)
+def isdoublePair2(P0,P1): return P0==(2,1) and len(P1[0])==2
+def isPair2(P0,P1):       return P0==(2,1) and len(P1[0])==1
+def isFlush(suit): return len(set(suit)) == 1
+def isSt(cards)  :  # Handles the low Ace as well
+    cards = sorted(cards)
+    diff  = [(x2-x1) for x1,x2 in zip(cards,cards[1:])]
+    return len([x for x in diff if x==1])==4 or cards==[0,1,2,3,12]
+
+def handScore(H):
+    cards,suit = zip(*H)
+    P     = pairScore(cards)
+    P0,P1 = zip(*P)
+
+    score = 0
+    S,F   = isSt(cards), isFlush(suit)
+
+    if    isPair2(P0,P1)      : score = 1   
+    elif  isdoublePair2(P0,P1): score = 2
+    elif  isPair3(P0)         : score = 3
+    elif S and not F          : score = 4
+    elif F and not S          : score = 5
+    elif isFullHouse(P0)      : score = 6
+    elif isPair4(P0)          : score = 7
+    elif S and F              : score = 8
+    return [score, P]
+
+
+'''
+ To determine who wins a poker hand, given two players hole cards, each player 
+ determines the best hand they can make within their 7 choose 5 = 21 possible five 
+ card combinations. Then each players best hands are put against each other. 1, -1 
+ or 0 is returned if player A wins, B wins, or ties respectively. 
+'''
+
+# This function prevents call xunqiueCombinations a bajillon times
+HAND_COMBOS = list(xuniqueCombinations(range(7),5))
+def pull_hand(H): 
+    for L in HAND_COMBOS: yield [H[n] for n in L]
+
+def top_hand_reduce(P1,P2):
+    H1, A = P1
+    H2, B = P2
+    if   A[0]>B[0]: return  H1,A
+    elif A[0]<B[0]: return  H2,B
+    pairscore = top_pairScore(A[1],B[1])
+    if   pairscore > 0: return H1,A
+    elif pairscore < 0: return H2,B
+    return H1,A
+
+def top_hand_score(A,B):
+    A.score,B.score = handScore(A.score), handScore(B.score)
+    if   A.score[0]>B.score[0]: return  1
+    elif A.score[0]<B.score[0]: return -1
+    return top_pairScore(A.score[1],B.score[1])
+
+def HOLDEM_score(A, B, board): 
+    A.score = reduce(top_hand_reduce,[(H,handScore(H)) for H in pull_hand(A.hole+board)])[0]
+    B.score = reduce(top_hand_reduce,[(H,handScore(H)) for H in pull_hand(B.hole+board)])[0]
+    return top_hand_score(A,B)
+
+# ***************************************
+# Returns the hand in a human readable format (pretty-print)
+def pp_hand(cards):
+    S = ''
+    for c in cards:
+        if   c[0]==8:  S+='T'
+        elif c[0]==9:  S+='J'
+        elif c[0]==10: S+='Q'
+        elif c[0]==11: S+='K'
+        elif c[0]==12: S+='A'
+        else         : S+=str(c[0]+2)
+        
+        if   c[1]==0 : S+='s'
+        elif c[1]==1 : S+='d'
+        elif c[1]==2 : S+='c'
+        else         : S+='h'
+        S += ' '
+    return S
+
+def pp_score(A):
+    try:
+        S = ''
+        if A.score[0] == 0: S = 'HighCard '
+        elif A.score[0] == 1: S = 'Pair '
+        elif A.score[0] == 2: S = 'TwoKind '
+        elif A.score[0] == 3: S = 'ThreeKind '
+        elif A.score[0] == 4: S = 'Straight '
+        elif A.score[0] == 5: S = 'Flush '
+        elif A.score[0] == 6: S = 'FullHouse '
+        elif A.score[0] == 7: S = 'FourKind '
+        elif A.score[0] == 8: S = 'StFlush '
+        S += str(A.score[1])
+        return S
+    except:
+        return "INCOMPLETE"
diff --git a/GAMEPLAY.py b/GAMEPLAY.py
new file mode 100644 (file)
index 0000000..101f249
--- /dev/null
@@ -0,0 +1,235 @@
+from DECK import *
+GLOBAL_TXT = []
+
+# ***************************************
+from os import system, kill
+from popen2 import popen2,popen3
+class player:
+    def __init__(self, name='', brain=''):
+        self.cash  = 0
+        self.owe   = 0
+        self.option= True
+        self.hole  = []
+        self.IN, self.OUT = 0,0
+        self.brain = brain.strip()
+        self.name  = name+' '
+        self.FOLD  = False
+        self.score = 0
+        self.GP_BUFFER = []
+        
+        if self.brain:
+            self.OUT, self.IN = popen2("./"+self.brain)
+
+    def die(self): 
+        self.IN.write("END\n"); self.IN.flush()
+        system("pkill -9 "+self.name)
+
+    def bet(self, wager, other_player):
+        wager = int(wager)
+        if wager > self.cash: wager  = self.cash
+        if wager < 0:         wager  = 0
+        if wager-self.owe > other_player.cash: 
+            wager = other_player.cash
+        self.cash -= wager
+        self.owe  -= wager
+        self.owe   = self.owe if self.owe < 0 else 0
+        GP("ACTION Player ", self.name, " bets: ", wager)
+        return wager
+    
+    def record_info(self, pot, min_raise, flop, turn, river, other_player):
+        global GLOBAL_TXT
+        self.IN.write("INFO NAME " + str(self.name) + '\n'); self.IN.flush()
+        self.IN.write("INFO ONAME " + str(other_player.name) + '\n'); self.IN.flush()
+        self.IN.write("INFO STACK " + str(self.cash) + '\n'); self.IN.flush()
+        self.IN.write("INFO OSTACK " + str(other_player.cash) + '\n'); self.IN.flush()
+        self.IN.write("INFO POT " + str(pot) + '\n'); self.IN.flush()
+        self.IN.write("INFO MINRAISE " + str(min_raise)+'\n'); self.IN.flush()
+        self.IN.write("INFO OWE " + str(self.owe)  + '\n'); self.IN.flush()
+        self.IN.write("INFO HOLE " + pp_hand(self.hole) + '\n'); self.IN.flush()
+        self.IN.write("INFO FLOP " + pp_hand(flop) + '\n'); self.IN.flush()
+        self.IN.write("INFO TURN " + pp_hand(turn) + '\n'); self.IN.flush()
+        self.IN.write("INFO RIVER " + pp_hand(river) + '\n'); self.IN.flush()
+        self.GP_FLUSH(GLOBAL_TXT)
+
+    def GP_FLUSH(self, TXT):
+        for g in TXT:
+            if g not in self.GP_BUFFER:
+                self.GP_BUFFER.append(g)
+                self.IN.write(g + "\n")
+                self.IN.flush()
+
+    def human_play(self, pot, min_raise, flop, turn, river, other_player):
+        if GLOBAL_TXT: print GLOBAL_TXT[-1]
+        print "[", pp_hand(self.hole), "] Board: ",
+        print "[", pp_hand(flop), pp_hand(turn), pp_hand(river), "]"
+        print "POT: ", pot, " OWE: ", self.owe, " MIN_RAISE: ", min_raise,
+        print "   |   ", "STACK: ", self.cash, "Opp. STACK: ", other_player.cash
+        return raw_input("What do you want to bet, " + self.name + ": ")
+
+    def decide(self, pot, min_raise, flop, turn, river, other_player,endgame=False):
+
+        in_bet = ''
+
+        if not self.brain:
+            in_bet = self.human_play(pot,min_raise, flop, turn, river, other_player)
+
+        else:
+            self.record_info(pot,min_raise,flop,turn,river,other_player)
+            self.IN.write("MOVE \n"); self.IN.flush()
+            in_bet = self.OUT.readline().strip()
+            
+        if endgame: return False
+
+        if in_bet.isalpha(): 
+            in_bet = in_bet.upper()
+            if   in_bet == 'A': in_bet = self.cash
+            elif in_bet == 'C': in_bet = self.owe
+            else              : self.FOLD = True
+        else:
+            try   : in_bet = int(in_bet)
+            except: self.FOLD = True
+
+        if self.FOLD: return False
+
+        if in_bet >= self.cash or in_bet == self.owe or in_bet >= min_raise:
+            out_bet  = self.bet(in_bet, other_player)
+            other_player.owe += out_bet
+            return out_bet
+
+        # Bets between whats owed and min_raise are considered a call
+        if in_bet >= self.owe and in_bet < min_raise:
+            out_bet  = self.bet(self.owe, other_player)
+            other_player.owe += out_bet
+            return out_bet
+
+        # Illegal bets will be counted as folds!
+        GP("ACTION Player ", self.name, "bets ", str(in_bet), " illegally and FOLDS. Valid bets are ", str(self.owe), " or anything >= ", min_raise)
+        self.FOLD = True
+        return False
+
+
+def GP(*S): GLOBAL_TXT.append(''.join(map(str,S)))
+def returnGP(): return GLOBAL_TXT
+     
+def hand_judgement(A,B, board, pot):
+    if    A.FOLD: 
+        GP("ACTION Player ", A.name, " folds" )
+        GP("ACTION Player ", B.name, " wins: ", pot)
+        B.cash += pot
+        return GLOBAL_TXT
+
+    elif  B.FOLD:
+        GP("ACTION Player ", B.name, "folds")
+        GP("ACTION Player ", A.name, "wins: ", pot)
+        A.cash += pot
+        return GLOBAL_TXT
+    S = HOLDEM_score(A, B, board)
+
+    if   S == 1: 
+        GP("ACTION Player ", A.name, "wins: ", pot)
+        A.cash += pot
+        return GLOBAL_TXT
+
+    elif S ==-1: 
+        GP( "ACTION Player ", B.name, "wins: ", pot)
+        B.cash += pot
+        return GLOBAL_TXT
+
+    elif S == 0:
+        GP("ACTION Split pot ")
+        split_pot, carry = pot/2, pot%2
+        A.cash += split_pot + carry
+        B.cash += split_pot
+        return GLOBAL_TXT
+
+def checkFOLD(A,B):
+    if   A.FOLD or B.FOLD: return True
+    return False
+
+def betting_round(P,pot,min_raise,flop,turn,river):
+    for player in P: 
+        player.option = True
+
+    while((P[0].owe>0 or P[1].owe>0) or (P[0].option or P[1].option) and (P[0].cash)):
+        action  = P[0].decide(pot, min_raise, flop,turn,river, P[-1])
+        P[0].option = False
+
+        if action >= min_raise: min_raise = 2*action
+        if checkFOLD(P[0],P[1]): return pot
+
+        pot += action
+        P = [P[-1]] + P[:-1] # Cycle the player order
+    return pot
+
+
+def gameplay(DECK, A, B, smallB, ):
+    global GLOBAL_TXT
+
+    # Shuffle the deck and deal two cards to each player
+    shuffle(DECK)
+
+    A.hole = [DECK[n] for n in xrange(2)  ]
+    B.hole = [DECK[n] for n in xrange(2,4)]
+    board  = [DECK[n] for n in xrange(4,9)]
+
+    flop, turn, river = '', '', ''
+    GLOBAL_TXT        = []
+
+    pot   = 0
+    A.owe = 2*smallB
+    B.owe = smallB
+    A.FOLD, B.FOLD = False, False
+
+    # Handle the case if a player can't post the blinds completely, ALL_IN
+    if    B.owe > B.cash:
+        pot  += B.bet(B.owe, A)
+        pot  += A.bet(pot,   B)
+        return hand_judgement(A,B,board,pot)
+    elif  A.owe > A.cash:
+        pot += A.bet(A.owe,  B)
+        pot += B.bet(pot,    A)
+        return hand_judgement(A,B,board,pot)
+
+    # Both players can post the blinds
+    pot += A.bet(A.owe, B)
+    pot += B.bet(B.owe, A)
+
+    # PRE-FLOP ACTION - Player B is small blind, first to act
+    B.owe        += smallB
+    A.owe        -= smallB
+    min_raise     = smallB*4
+    play_order    = [B,A]
+    pot = betting_round(play_order,pot,min_raise,flop,turn,river)
+    if checkFOLD(A,B): return hand_judgement(A,B,board,pot)
+    if not A.cash or not B.cash: return hand_judgement(A,B,board,pot)
+
+    # FLOP ACTION - Player A is now first to act
+    flop          = board[:3]
+    GP( "ACTION FLOP ", pp_hand(flop) )
+    A.owe, B.owe  = 0, 0
+    min_raise     = smallB*2
+    play_order    = [A,B]
+    pot = betting_round(play_order,pot,min_raise,flop,turn,river)
+    if checkFOLD(A,B): return hand_judgement(A,B,board,pot)
+    if not A.cash or not B.cash: return hand_judgement(A,B,board,pot)
+
+    # TURN ACTION
+    turn          = board[3:4]
+    GP( "ACTION TURN ", pp_hand(turn) )
+    A.owe, B.owe  = 0, 0
+    play_order    = [A,B]
+    pot = betting_round(play_order,pot,min_raise,flop,turn,river)
+    if checkFOLD(A,B): return hand_judgement(A,B,board,pot)
+    if not A.cash or not B.cash: return hand_judgement(A,B,board,pot)
+
+    # RIVER ACTION
+    river         = board[4:5]
+    GP( "ACTION RIVER ", pp_hand(river))
+    A.owe, B.owe  = 0, 0
+    play_order    = [A,B]
+    pot = betting_round(play_order,pot,min_raise,flop,turn,river)
+    if checkFOLD(A,B): return hand_judgement(A,B,board,pot)
+    
+    # SHOWDOWN!
+    return hand_judgement(A,B,board,pot)
diff --git a/PBOT.py b/PBOT.py
new file mode 100644 (file)
index 0000000..fdcb384
--- /dev/null
+++ b/PBOT.py
@@ -0,0 +1,63 @@
+import popen2
+from DECK import *
+from GAMEPLAY import *
+global GLOBAL_TXT
+#import psyco; psyco.full()
+
+start_stack = 1000
+blinds      = [1,2,4,8,16,25,37,50]
+hand_clock  = 20
+
+# Command line arguments
+import sys
+
+try   : A = player(sys.argv[1],sys.argv[1]) 
+except: A = player('Meg')
+try   : B = player(sys.argv[2], sys.argv[2])
+except: B = player('Jack')
+try   : tournament_N = int(sys.argv[3])
+except: tournament_N = 1
+
+P = [A,B]
+for n in xrange(tournament_N):
+    A.cash = start_stack
+    B.cash = start_stack
+
+    hand_count,b = 0, 0
+
+    while A.cash > 0 and B.cash > 0:
+        hand_count += 1
+
+        if hand_count % hand_clock==0: 
+            b = b+1 if b < len(blinds)-1 else len(blinds)-1
+            
+        GLOBAL_TXT = gameplay(DECK, P[0],P[1], blinds[b])
+
+        # Allow the players to view the end result
+        board = [DECK[n] for n in xrange(4,9)]
+        flop,turn,river = board[:3], board[3:4], board[4:5]
+        if not A.FOLD and not B.FOLD:
+            GP("INFO ",A.name, pp_hand(A.hole), pp_score(A), ' ',A.cash)
+            GP("INFO ",B.name, pp_hand(B.hole), pp_score(B), ' ',B.cash)
+        GP("INFO GAMEOVER ", hand_count)
+        for p in P: 
+            if p.brain: 
+                p.GP_FLUSH(returnGP())
+                p.GP_BUFFER = []
+          
+        # Human player text
+        if not A.brain or not B.brain: # or True:
+            for g in GLOBAL_TXT: print g
+            print "INFO BOARD ", pp_hand([DECK[n] for n in xrange(4,9)])
+
+        P = [P[-1]] + P[:-1] # Cycle the player order
+
+    if A.cash: print A.name, hand_count
+    else     : print B.name, hand_count
+
+for p in P: 
+    if p.brain: p.die() # Kill the players!
+
+
+
+        
diff --git a/combinations.py b/combinations.py
new file mode 100644 (file)
index 0000000..14251cf
--- /dev/null
@@ -0,0 +1,26 @@
+from __future__ import generators
+
+def xcombinations(items, n):
+    if n==0: yield []
+    else:
+        for i in xrange(len(items)):
+            for cc in xcombinations(items[:i]+items[i+1:],n-1):
+                yield [items[i]]+cc
+
+def xuniqueCombinations(items, n):
+    if n==0: yield []
+    else:
+        for i in xrange(len(items)):
+            for cc in xuniqueCombinations(items[i+1:],n-1):
+                yield [items[i]]+cc
+            
+def xselections(items, n):
+    if n==0: yield []
+    else:
+        for i in xrange(len(items)):
+            for ss in xselections(items, n-1):
+                yield [items[i]]+ss
+
+def xpermutations(items):
+    return xcombinations(items, len(items))
+
diff --git a/p1.py b/p1.py
new file mode 100755 (executable)
index 0000000..7b9e762
--- /dev/null
+++ b/p1.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+import sys
+import os
+
+IN, OUT = sys.stdin, sys.stdout
+
+FOUT  = open("test_anyAce.txt",'a')
+allin = False
+
+while True:
+   std_in = IN.readline().strip()
+   if std_in:
+        FOUT.write(std_in+'\n')
+
+        std_in = std_in.split(' ')
+        try   : 
+            tag  = std_in[0]
+            type = std_in[1]
+            data = std_in[2:]
+        except: 
+            #FOUT.write(str(std_in))
+            tag  = std_in[0]
+
+        #FOUT.write(tag+' '+type+' '+str(data)+' '+'\n')
+        if tag == "END" : FOUT.close(); exit()
+        if tag == "MOVE": 
+            # You must return a valid bet here
+            if allin: OUT.write("A\n")
+            else    : OUT.write("f\n")
+
+            OUT.flush()
+            allin = False
+
+        elif tag == "INFO" and type == "HOLE":
+            # Handle new information
+            # Save info to a file and go all in on any Ace
+            h1, h2 = data[-1], data[-2]
+            #FOUT.write(h1+h2+'\n')
+
+            if h1[0]=='A' or h2[0]=='A':  allin = True
diff --git a/p2.py b/p2.py
new file mode 100755 (executable)
index 0000000..6377776
--- /dev/null
+++ b/p2.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+import sys
+import os
+
+IN, OUT = sys.stdin, sys.stdout
+
+FOUT  = open("test_anyPair.txt",'w')
+allin = False
+
+while True:
+   std_in = IN.readline().strip()
+   if std_in:
+        std_in = std_in.split(' ')
+        try   : 
+            tag  = std_in[0]
+            type = std_in[1]
+            data = std_in[2:]
+        except: 
+            FOUT.write(str(std_in))
+            tag  = std_in[0]
+
+        #FOUT.write(tag+' '+type+' '+str(data)+' '+'\n')
+
+        if tag == "MOVE": 
+            # You must return a valid bet here
+            if allin: OUT.write("A\n")
+            else    : OUT.write("f\n")
+            OUT.flush()
+            allin = False
+
+        elif tag == "INFO" and type == "HOLE":
+            # Handle new information
+            # Save info to a file and go all in on any pair
+            h1, h2 = data[-1], data[-2]
+
+            if h1[0] == h2[0] and h1[0]=="A":  allin = True
+
+                
+            
+
+            
+        
+
+
+
+