From 0748479044dc40f3e84f67473d7cfa82e72c4dac Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Tue, 7 Dec 2010 11:43:02 -0500 Subject: [PATCH] Add docstrings and doctests to pbot.py + Python cleanups. Also fix a number of bugs that a fixed pbot.py turned up in deck.py and table.py. --- PBOT.py | 63 ------------------------------------------------ deck.py | 4 +++- pbot.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ table.py | 54 ++++++++++++++++++++++++----------------- 4 files changed, 108 insertions(+), 86 deletions(-) delete mode 100644 PBOT.py create mode 100755 pbot.py diff --git a/PBOT.py b/PBOT.py deleted file mode 100644 index fdcb384..0000000 --- a/PBOT.py +++ /dev/null @@ -1,63 +0,0 @@ -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/deck.py b/deck.py index 4f1ca92..364f0e7 100644 --- a/deck.py +++ b/deck.py @@ -12,7 +12,7 @@ FACE_CARDS = ['ten', 'jack', 'queen', 'king', 'ace'] """Ordered list of face cards. """ -DECK = dict([(c/4, c%4) for c in range(52)]) +DECK = [(c/4, c%4) for c in range(52)] """Cards in the deck are stored as (n,m), where `n` is the rank number `n=[0,12]`, and `m` is the suit `m=[0,3]`. @@ -20,6 +20,8 @@ Examples: (0,1) is a two of hearts, (11,0) is a king of spades, (12,3) is an ace of clubs + +>>> DECK[:5] """ diff --git a/pbot.py b/pbot.py new file mode 100755 index 0000000..af7a3fb --- /dev/null +++ b/pbot.py @@ -0,0 +1,73 @@ +#!/usr/bin/python + +"""Run a poker championship. +""" + +from table import Player, Blinds, Table +from deck import DECK + + +def run(players, blinds, start_stack, hand_limit, tournaments, verbose=False): + for n in xrange(tournaments): + for player in players: + player.cash = start_stack + table = Table(deck=DECK, players=players, blinds=blinds, + verbose=verbose) + while len(table.players) > 1 and table.hand_count < hand_limit: + table.play_round() + # keep bots up to date, so they can think in parallel + for player in players: + if player.brain: + player.log_flush(table.log) + if len(table.players) == 1: + print "INFO WINNER: Player %s" % table.players[0] + else: + assert table.hand_count >= hand_limit + print "INFO Time expired" + for player in table.players: + print "INFO Tie: Player %s" % player + + +if __name__ == '__main__': + from optparse import OptionParser + + usage = '%prog [options] PLAYER PLAYER ...' + epilog = ('`PLAYER`s should be name,brain tuples, e.g. (`a,p1.py`). ' + 'Give just the name for a human playing from the command line.') + p = OptionParser(usage=usage, epilog=epilog) + p.add_option('-s', '--start-stack', dest='stack', default=1000, + type='int', help='start stack (%default)') + p.add_option('-b', '--blinds', dest='blinds', + default='1,2,4,8,16,25,37,50', + help='blind schedule (%default)') + p.add_option('-c', '--hand-clock', dest='clock', default=20, type='int', + help='hands between blind increments (%default)') + p.add_option('-l', '--hand-limit', dest='limit', default=5000, type='int', + help='hand limit (-1 for unlimited play, %default') + p.add_option('-t', '--tournaments', dest='tournaments', default=1, + type='int', help='number of tournaments (%default)') + p.add_option('-v', '--verbose', dest='verbose', action='store_true', + help='print the log as it is written') + + options,args = p.parse_args() + + blinds = Blinds(blinds=[int(x) for x in options.blinds.split(',')], + hand_clock=options.clock) + + players = [] + for arg in args: + fields = [x.strip() for x in arg.rsplit(',', 1)] + name = fields[0] + if len(fields) == 1: + brain = None + else: + brain = fields[1] + players.append(Player(name=name, brain=brain)) + + try: + run(players=players, blinds=blinds, start_stack=options.stack, + hand_limit=options.limit, tournaments=options.tournaments, + verbose=options.verbose) + finally: # kill the bots + for p in players: + p.kill() diff --git a/table.py b/table.py index 98f6b44..bea4513 100644 --- a/table.py +++ b/table.py @@ -88,10 +88,14 @@ class Player (object): self.owe = min(self.owe, 0) return wager - def _log_flush(self, stream, log): + def log_flush(self, log): """Show the brain any new log entries.""" + if self._proc: + stream = self._proc.stdin + else: + stream = sys.stdout new_index = len(log) - for line in LOG[self._next_log_index:new_index]: + for line in log[self._next_log_index:new_index]: stream.write(line + '\n') stream.flush() self._next_log_index = new_index @@ -107,17 +111,17 @@ class Player (object): self._proc.stdin.write('INFO POT %s\n' % str(pot)) self._proc.stdin.write('INFO MINRAISE %s\n' % str(min_raise)) self._proc.stdin.write('INFO OWE %s\n' % str(self.owe)) - self._proc.stdin.write('INFO HOLE %s\n' % pp_hand(self.hole)) - self._proc.stdin.write('INFO FLOP %s\n' % pp_hand(flop)) - self._proc.stdin.write('INFO TURN %s\n' % pp_hand(turn)) - self._proc.stdin.write('INFO RIVER %s\n' % pp_hand(river)) + for phase,h in [('HOLE', self.hole), ('FLOP', flop), + ('TURN', turn), ('RIVER', river)]: + if h: + self._proc.stdin.write('INFO %s %s\n' % (phase, pp_hand(h))) self._proc.stdin.flush() - self._log_flush(self._proc.stdin, log) + self.log_flush(log) def _human_play(self, active_players, pot, min_raise, flop, turn, river, log): """Ask the human at `sys.stdin`/`sys.stdout` for guidance.""" - self._log_flush(sys.stdout, log) + self.log_flush(log) 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, @@ -266,10 +270,11 @@ class Table (object): >>> t.players [] """ - def __init__(self, deck=None, players=None, blinds=None): + def __init__(self, deck=None, players=None, blinds=None, verbose=False): self.deck = deck self.players = players # list of surviving players self.blinds = blinds + self.verbose = verbose self.dealer = 0 # index of player acting as dealer (dealer button) self.hand_count = 0 self.dead_players = [] # list of players in the order they died @@ -289,6 +294,12 @@ class Table (object): index = index % len(self.players) return self.players[index:]+self.players[:index] + def _log(self, msg): + """Add a message to the table log.""" + self.log.append(msg) + if self.verbose: + print msg + def play_round(self, shuffle=random.shuffle): self.deal(shuffle=shuffle) self.ante_up() @@ -297,17 +308,17 @@ class Table (object): if self.hand_complete(): return self.judge() # FLOP ACTION self.flop = self.board[:3] - self.log.append('ACTION FLOP %s' % pp_hand(self.flop)) + self._log('ACTION FLOP %s' % pp_hand(self.flop)) self.betting_round() if self.hand_complete(): return self.judge() # TURN ACTION self.turn = self.board[3:4] - self.log.append('ACTION TURN %s' % pp_hand(self.turn)) + self._log('ACTION TURN %s' % pp_hand(self.turn)) self.betting_round() if self.hand_complete(): return self.judge() # RIVER ACTION self.river = self.board[4:5] - self.log.append('ACTION RIVER %s' % pp_hand(self.river)) + self._log('ACTION RIVER %s' % pp_hand(self.river)) self.betting_round() if self.hand_complete(): return self.judge() # SHOWDOWN! @@ -322,7 +333,7 @@ class Table (object): self.board = [self.deck[n] for n in xrange(2*N,2*N+5)] # common cards for i,player in enumerate(self.players): player.new_hand() - self.log.append('INFO CHIPCOUNT Player %s %s' + self._log('INFO CHIPCOUNT Player %s %s' % (player, player.cash)) player.hole = [self.deck[n] for n in xrange(2*i, 2*(i+1))] player.hand = SevenChooseFiveHand(player.hole+self.board) @@ -356,7 +367,7 @@ class Table (object): self.per_player_pot = max(self.per_player_pot, player.pot) string = 'ACTION Player %s (pot %s) %s' % (player, player.pot, verb) if append_cost: string += ' %d' % contribution - self.log.append(string) + self._log(string) return contribution def betting_round(self, first_round=False): @@ -391,9 +402,9 @@ class Table (object): action = player.decide( active_players=[player]+active+all_in, pot=self.pot, min_raise=min_raise, flop=self.flop, turn=self.turn, - river=self.river) + river=self.river, log=self.log) except IllegalBet, e: - self.log.append(( + self._log(( 'ACTION Player %s bets %s illegally and FOLDS. ' 'Valid bets are %d or anything >= %d') % (e.player, e.bet, e.player.owe, min_raise)) @@ -446,11 +457,11 @@ class Table (object): self.pay_players(winners, losers) # allow the players to view the end result flop,turn,river = self.board[:3], self.board[3:4], self.board[4:5] - self.log.append('INFO board %s' % pp_hand(self.board)) + self._log('INFO board %s' % pp_hand(self.board)) for p in runcp: - self.log.append('INFO Player %s hole: %s best: %s cash: %d' + self._log('INFO Player %s hole: %s best: %s cash: %d' % (p, pp_hand(p.hole), p.hand.pp_score(), p.cash)) - self.log.append('INFO GAMEOVER %d' % self.hand_count) + self._log('INFO GAMEOVER %d' % self.hand_count) # prepare for the next round dead = [p for p in self.players if p.cash == 0] @@ -465,10 +476,9 @@ class Table (object): self.dealer = (self.dealer + 1) % len(self.players) next_dealing_player = self.players[self.dealer] for p in dead: - self.log.append('INFO Player %s died after hand %d' + self._log('INFO Player %s died after hand %d' % (p, self.hand_count)) self.players.remove(p) - p.kill() self.dealer = self.players.index(next_dealing_player) def pay_players(self, winners, losers): @@ -497,4 +507,4 @@ class Table (object): self.per_player_pot -= pot for name,take in winnings.items(): if take > 0: - self.log.append('ACTION Player %s wins: %s' % (name, take)) + self._log('ACTION Player %s wins: %s' % (name, take)) -- 2.26.2