Python 2 version of Michael Dawson's blackjack.py Program

This is a sightly revised version of Michael Dawson's blackjack.py from chapter 9 of Michael Dawson, "Python: Programming for the Absolute Beginner," Third Edition, Course Technology PTR, 2010, Cengage Learning, Boston, ISBN 978-1-4354-5500-9, to avoid problems when run under Python 2.

    1 # Blackjack
    2 # From 1 to 7 players compete against a dealer
    3 
    4 import cards, games2     
    5 
    6 class BJ_Card(cards.Card):
    7     """ A Blackjack Card. """
    8     ACE_VALUE = 1
    9 
   10     @property
   11     def value(self):
   12         if self.is_face_up:
   13             v = BJ_Card.RANKS.index(self.rank) + 1
   14             if v > 10:
   15                 v = 10
   16         else:
   17             v = None
   18         return v
   19 
   20 class BJ_Deck(cards.Deck):
   21     """ A Blackjack Deck. """
   22     def populate(self):
   23         for suit in BJ_Card.SUITS: 
   24             for rank in BJ_Card.RANKS: 
   25                 self.cards.append(BJ_Card(rank, suit))
   26     
   27 
   28 class BJ_Hand(cards.Hand):
   29     """ A Blackjack Hand. """
   30     def __init__(self, name):
   31         super(BJ_Hand, self).__init__()
   32         self.name = name
   33 
   34     def __str__(self):
   35         rep = self.name + ":\t" + super(BJ_Hand, self).__str__()  
   36         if self.total:
   37             rep += "(" + str(self.total) + ")"        
   38         return rep
   39 
   40     @property     
   41     def total(self):
   42         # if a card in the hand has value of None, then total is None
   43         for card in self.cards:
   44             if not card.value:
   45                 return None
   46         
   47         # add up card values, treat each Ace as 1
   48         t = 0
   49         for card in self.cards:
   50               t += card.value
   51 
   52         # determine if hand contains an Ace
   53         contains_ace = False
   54         for card in self.cards:
   55             if card.value == BJ_Card.ACE_VALUE:
   56                 contains_ace = True
   57                 
   58         # if hand contains Ace and total is low enough, treat Ace as 11
   59         if contains_ace and t <= 11:
   60             # add only 10 since we've already added 1 for the Ace
   61             t += 10   
   62                 
   63         return t
   64 
   65     def is_busted(self):
   66         return self.total > 21
   67 
   68 
   69 class BJ_Player(BJ_Hand):
   70     """ A Blackjack Player. """
   71     def is_hitting(self):
   72         response = games2.ask_yes_no("\n" + self.name + ", do you want a hit? (Y/N): ")
   73         return response == "y"
   74 
   75     def bust(self):
   76         print(self.name, "busts.")
   77         self.lose()
   78 
   79     def lose(self):
   80         print(self.name, "loses.")
   81 
   82     def win(self):
   83         print(self.name, "wins.")
   84 
   85     def push(self):
   86         print(self.name, "pushes.")
   87 
   88         
   89 class BJ_Dealer(BJ_Hand):
   90     """ A Blackjack Dealer. """
   91     def is_hitting(self):
   92         return self.total < 17
   93 
   94     def bust(self):
   95         print(self.name, "busts.")
   96 
   97     def flip_first_card(self):
   98         first_card = self.cards[0]
   99         first_card.flip()
  100 
  101 
  102 class BJ_Game(object):
  103     """ A Blackjack Game. """
  104     def __init__(self, names):      
  105         self.players = []
  106         for name in names:
  107             player = BJ_Player(name)
  108             self.players.append(player)
  109 
  110         self.dealer = BJ_Dealer("Dealer")
  111 
  112         self.deck = BJ_Deck()
  113         self.deck.populate()
  114         self.deck.shuffle()
  115 
  116     @property
  117     def still_playing(self):
  118         sp = []
  119         for player in self.players:
  120             if not player.is_busted():
  121                 sp.append(player)
  122         return sp
  123 
  124     def __additional_cards(self, player):
  125         while not player.is_busted() and player.is_hitting():
  126             self.deck.deal([player])
  127             print(player)
  128             if player.is_busted():
  129                 player.bust()
  130            
  131     def play(self):
  132         # deal initial 2 cards to everyone
  133         self.deck.deal(self.players + [self.dealer], per_hand = 2)
  134         self.dealer.flip_first_card()    # hide dealer's first card
  135         for player in self.players:
  136             print(player)
  137         print(self.dealer)
  138 
  139         # deal additional cards to players
  140         for player in self.players:
  141             self.__additional_cards(player)
  142 
  143         self.dealer.flip_first_card()    # reveal dealer's first 
  144 
  145         if not self.still_playing:
  146             # since all players have busted, just show the dealer's hand
  147             print(self.dealer)
  148         else:
  149             # deal additional cards to dealer
  150             print(self.dealer)
  151             self.__additional_cards(self.dealer)
  152 
  153             if self.dealer.is_busted():
  154                 # everyone still playing wins
  155                 for player in self.still_playing:
  156                     player.win()                    
  157             else:
  158                 # compare each player still playing to dealer
  159                 for player in self.still_playing:
  160                     if player.total > self.dealer.total:
  161                         player.win()
  162                     elif player.total < self.dealer.total:
  163                         player.lose()
  164                     else:
  165                         player.push()
  166 
  167         # remove everyone's cards
  168         for player in self.players:
  169             player.clear()
  170         self.dealer.clear()
  171         
  172 
  173 def main():
  174     print("\t\tWelcome to Blackjack!\n")
  175     
  176     names = []
  177     number = games2.ask_number("How many players? (1 - 7): ", low = 1, high = 8)
  178     for i in range(number):
  179         name = raw_input("Enter player name: ")
  180         names.append(name)
  181     print()
  182         
  183     game = BJ_Game(names)
  184 
  185     again = None
  186     while again != "n":
  187         game.play()
  188         again = games2.ask_yes_no("\nDo you want to play again?: ")
  189 
  190 
  191 main()
  192 raw_input("\n\nPress the enter key to exit.")
  193 
  194 
  195 
  196