Add docstrings and doctests to pbot.py + Python cleanups.
[poker.git] / table.py
index 98f6b44f25b4bc96bf6b893687048d64cab02f39..bea4513304a54e2b5b6bb4ff4b65651c5cafc2fb 100644 (file)
--- 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
     [<BufferedPlayer P3>]
     """
-    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))