8
\$\begingroup\$
import random
from random import choice
import os
import time
import texttable
class Stack():
 def __init__(self, replay, winnings):
 self.start_amount = 100
 self.stored_bet = 0
 self.stored_end = 0
 self.replay = replay
 if self.replay == 0:
 self.stored_end = 0 #needs to be initialized
 else:
 self.stored_end = self.stored_end + winnings
 def load_account(self):
 self.stored_end = self.start_amount
 def account(self, begin, change):
 end = float(begin) + float(change)
 self.stored_end = end # store class variable??
 return(begin, change, end)
 def bet_test(self, miss_type):
 # collect's bet and check input
 # miss_type should start as 0
 possible_bets = ['5', '10', '15', '20', '25']
 while True:
 print "\nWhat is your bet? (5, 10, 15, 20, 25)"
 bet = raw_input(' >')
 if bet in possible_bets:
 bet = int(bet)
 if self.replay == 1:
 begin = self.stored_end
 self.stored_bet = int(bet)
 break
 else:
 if bet > self.stored_end:
 print "You don't have enough for that bet."
 time.sleep(2)
 else:
 begin = self.stored_end
 self.stored_bet = bet
 break
class DECK(): 
 def __init__(self):
 suite = ('Spades', 'Hearts', 'Diamonds', 'Clubs')
 rank = ('2', '3', '4', '5', '6', '7', '8', '9', '10', "Jack", "Queen", "King", "Ace")
 self.full_deck = {}
 n = 0
 i = 0
 for n in range(6):
 for s in suite:
 for r in rank:
 self.full_deck[i + n] = "%s of %s" % (r, s)
 i += 1
 n += 1
 self.values = {'Ace':11, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10, 'Jack': 10, 'Queen': 10, 'King': 10}
 self.test_hand1 = {0: 'Ace of Spades', 1: 'Jack of Clubs' } # for testing
 self.test_hand2 = {0: 'Ace of Clubs', 1: '10 of Diamonds' } # for testing
 self.test_hand3 = {0: '8 of Spades', 1: '8 of Hearts'} # for testing splits
 self.hand_dict = {} # this will be a dict of all the hand instances created by Hand.
 self.incomplete_hands = {} # hands yet to be played
 self.compltete_hands = {} # hands done being played
 # re-split hands and counters
 self.split_counter = 0
 def hand_table(self):
 # show table of hands
 hands_dict = {}
 bets_dict = {}
 points_dict = {}
 for hand in Hand.instances:
 hands_dict.setdefault(hand.name, hand.hand_a)
 bets_dict.setdefault(hand.name, hand.bet)
 points_dict.setdefault(hand.name, hand.hand_points)
 first_cards = []
 second_cards = []
 third_cards = []
 fourth_cards = []
 fifth_cards = []
 bets_list = []
 points_list = []
 for hand in hands_dict:
 first_cards.append(hands_dict[hand].get(0))
 second_cards.append(hands_dict[hand].get(1))
 third_cards.append(hands_dict[hand].get(2))
 fourth_cards.append(hands_dict[hand].get(3))
 fifth_cards.append(hands_dict[hand].get(4))
 bets_list.append("Bet is %r" % bets_dict[hand])
 points_list.append("Points are %r" % points_dict[hand])
 print "\n" 
 header = hands_dict.keys()
 table = texttable.Texttable()
 table.header(header)
 table.add_rows([first_cards, second_cards, third_cards, fourth_cards, fifth_cards, points_list, bets_list], header = False)
 print table.draw()
 def dhos(self): 
 # dealer hit or stick
 os.system("clear")
 #show table of hands
 self.hand_table()
 print " _ " * 10
 game.deck.d_hand.show_hand()
 time.sleep(1.5)
 dpoints, d_raw_points = game.deck.d_hand.points()
 if dpoints < 17:
 new_card = game.deck.full_deck.pop(random.choice(game.deck.full_deck.keys()))
 print new_card
 hl = len(game.deck.d_hand.hand_a.keys()) # hl is hand length
 game.deck.d_hand.hand_a[hl] = new_card # insert new hard into the given hand
 self.dhos()
 else:
 game.deck.d_hand.hand_points = dpoints
 game.end_game()
 def deal(self):
 # deal two cards each to dealer and player
 dhand = {}
 phand = {}
 for i in range (2):
 phand[i] = game.deck.full_deck.pop(random.choice(game.deck.full_deck.keys()))
 dhand[i] = game.deck.full_deck.pop(random.choice(game.deck.full_deck.keys()))
# actual hand 
 # creat instance of Hand for player's starting hand
 self.start_hand = Hand(phand, game.deck.full_deck, 0, "Opening Hand")
 # start_hand instance is now a member of the deck object.
 # creat instance of Hand for dealer's starting hand
 self.d_hand = Hand(dhand, game.deck.full_deck, 0, "Dealer")
 # d_hand instance is now a member of the deck object.
 def split_algo(self, hand):
 print hand.hand_a
 # old cards
 card1 = hand.hand_a[0]
 card2 = hand.hand_a[1]
 print card1
 print card2
 # new cards
 card_a = self.full_deck.pop(random.choice(self.full_deck.keys()))
 card_b = self.full_deck.pop(random.choice(self.full_deck.keys()))
 print card_a
 print card_b
 #two new hands
 new_hand_a = {0: card1, 1: card_a}
 new_hand_b = {0: card2, 1: card_b}
 if game.deck.split_counter == 0:
 self.split_1 = Hand(new_hand_a, game.deck.full_deck, 0, 'Split 1')
 self.split_2 = Hand(new_hand_b, game.deck.full_deck, 0, 'Split 2')
 # load bets: equal to bet on first hand
 self.split_1.bet = game.deck.start_hand.bet
 self.split_2.bet = game.deck.start_hand.bet
 split_hands_dict = {0: self.split_1, 1: self.split_2}
 # find the staring hand 'test split' in this case and delete it
 for x in range(len(Hand.instances)):
 if Hand.instances[x-1].name == 'Opening Hand':
 del Hand.instances[x-1]
 game.deck.split_counter += 1
 return split_hands_dict
class Hand():
 instances = [] # used to keep track of hand instances
 def __init__(self, hand_a, play_deck, split_count, name): # hand_a for hand actual
 Hand.instances.append(self)
 self.hand_a = hand_a # the actual hand when instance created
 # self.play_deck = play_deck # need to move this to deck class
 self.split_count = split_count
 self.name = name
 self.hand_points = 0
 self.raw_points = 0
 self.aces = 0
 self.testy = self
 self.status = 'incomplete'
 self.bet = 0
 self.stack_change = 0
 self.outcome = ''
 self.black_jack = 0
 def show_hand(self):
 print "%r hand is:" % self.name
 for i in range(len(self.hand_a.keys())):
 print self.hand_a[i]
 print "\n"
 def show_card(self, key):
 return self.hand_a[key]
 def points(self): 
 ln = len(self.hand_a.keys())
 tpoints = 0
 # add up all the cards with aces = 11
 self.aces = 0
 for i in range(ln):
 card, rank = self.card_and_rank(self.hand_a[i])
 # add up all the aces
 if rank == 'Ace':
 self.aces += 1
 # deck is an object of game
 tpoints = game.deck.values[rank] + tpoints
 raw_points = tpoints
 # check to see if there are aces in the hand.
 if self.aces > 0:
 for n in range(self.aces):
 # check to see if the total points are more than 21.
 if tpoints > 21:
 # subtract 10 points for each ace.
 tpoints = tpoints - 10 
 return (tpoints, raw_points)
 def card_and_rank(self, card):
 z = card.split()
 rank = z[0]
 return (card, rank)
 def split_test(self):
 card_a, rank_a = self.card_and_rank(self.hand_a[0])
 card_b, rank_b = self.card_and_rank(self.hand_a[1])
 val_a = game.deck.values[rank_a]
 val_b = game.deck.values[rank_b]
 if self.bet * 2 > stack.stored_end:
 print "You don't have enough cash to split."
 time.sleep(2)
 return 'no'
 elif game.deck.split_counter == 1:
 return 'no'
 elif val_a == val_b:
 return 'split'
 else:
 return 'no'
 def hos(self): 
 # hos = hit or stick
 os.system("clear") 
 self.hand_points, self.raw_points = self.points()
 print "\n"
 # show dealer's hand
 print "Dealer's up card is:\n%r\n" % game.deck.d_hand.show_card(0)
 # show starting hand
 self.show_hand()
 self.softness(self.hand_points, self.raw_points) 
 print "\nYou're bet is $%d." % stack.stored_bet
 print "Your stack is (less your current bet) $%d" % (stack.stored_end - stack.stored_bet)
 x = self.black_jack_check()
 if x == 'black_jack':
 self.black_jack = 1
 answer = 'stick'
 self.check_hand(answer)
 if self.split_test() == 'split': 
 answers = ['hit', 'stick', 'double', 'split']
 while True:
 print "\nWould you like to 'hit', 'stick', 'double' down or 'split'?"
 if self.hand_points == 21:
 answer = 'stick'
 print "You have 21!"
 time.sleep(.5)
 break
 else:
 answer = raw_input(' >')
 if answer in answers:
 print "end of hos func"
 break
 else:
 answers = ['hit', 'stick', 'double']
 while True:
 print "\nWould you like to 'hit', 'stick' or 'double'?"
 if self.hand_points == 21:
 answer = 'stick'
 print "You have 21!"
 time.sleep(.5)
 break
 else:
 answer = raw_input(' >')
 if answer in answers:
 print "end of hos func"
 break
 self.check_hand(answer)
 def check_hand(self, answer):
 print "\nYou said %r." % answer
 # hit
 if answer == 'hit':
 self.hit()
 if self.hand_points > 21:
 self.status = 'completed'
 print "Your busted"
 print "need to go to end game"
 self.status = 'completed'
 return 'done'
 hl = len(self.hand_a.keys())
 if hl == 5:
 self.status = 'completed'
 return 'done' 
 self.hos()
 # stick
 elif answer == 'stick':
 self.status = 'completed'
 print "need to call dhos and end the game"
 # double
 elif answer == 'double':
 t_bet = stack.stored_bet # temp bet
 t_end = stack.stored_end # temp stack balance (before bet)
 t_bal = t_end - t_bet # what would the stack be if bet subtracted?
 if t_bet > t_bal:
 print "You don't have enough to doule down."
 print "need to continue with hand - should check this when asking the question."
 raw_input("Please hit enter to continue")
 else:
 print "\nYou doubled down."
 stack.stored_bet = stack.stored_bet * 2
 self.bet = self.bet * 2
 print "Your bet is now $%d." % stack.stored_bet
 self.hit()
 self.show_hand()
 self.status = 'completed'
 time.sleep(3)
 # split
 elif answer == 'split':
 if game.deck.split_counter == 0:
 split_hands_dict = game.deck.split_algo(game.deck.start_hand)
 ln = len(split_hands_dict)
 for x in range(ln):
 split_hands_dict[x].hos()
 else:
 print "Sorry, but no more splits."
 time.sleep(2)
 else:
 print "something went horribly wrong."
 exit(0) 
 def softness(self, points, raw_points):
 # returns soft hand if hand is soft
 if self.aces > 0:
 nap = raw_points - (self.aces * 11) # = non-ace - points
 if nap + 11 < 21:
 print "\n%s has a soft %d" % (self.name, points)
 print " _ " * 10 
 else: 
 print "%s has %d points. a" % (self.name, points) 
 print " _ " * 10 
 else:
 print "%s has %d points." % (self.name, points)
 print " _ " * 10
 def black_jack_check(self):
 # print " _ " * 10
 # print "\n"
 zz = {}
 for i in range(len(self.hand_a.keys())):
 card, rank = self.card_and_rank(self.hand_a[i])
 # print "card is %r and rank is %r" % (card, rank)
 zz[i] = rank
 i += 1
 if zz[0] == 'Ace' and zz[1] == "Jack":
 # print "zz[0] is %r and zz[1] is %r a " % (zz[0], zz[1])
 print "\nBlack Jack! You Win!\n"
 time.sleep(1.5)
 return "black_jack"
 elif zz[0] == 'Jack' and zz[1] == 'Ace':
 # print "zz[0] is %r and zz[1] is %r b" % (zz[0], zz[1])
 print "\nBlack Jack! You Win!\n"
 time.sleep(1.5)
 return "black_jack"
 else:
 return 0
 def hit(self):
 print "hit method"
 k = len(game.deck.full_deck.keys())
 print k
 new_card = game.deck.full_deck.pop(random.choice(game.deck.full_deck.keys()))
 # hl = len(game.deck.start_hand.hand_a.keys()) # hl is hand length
 hl = len(self.hand_a.keys()) # hl is hand length
 print hl
 # add new card tto hand
 self.hand_a[hl] = new_card # insert new hard into hand of the given instance.
 # add new opints to self.hand_points
 self.hand_points, self.raw_points = self.points()
class Game():
 def __init__(self):
 self.hand_dict = {} # this will be a dict of all the hand instances created by Hand.
 self.incomplete_hands = [] # hands yet to be played
 self.compltete_hands = {} # hands done being played
 def collect_hands(self):
 # loops trough Hand.instances and unpacks into indivudual hands
 print "\nbegin collect_hands\n"
 i = 0
 for handy in Hand.instances:
 self.hand_dict.setdefault(handy.name, handy.hand_a)
 i += 1
 print self.hand_dict
 print "\n"
 for player in self.hand_dict:
 print player
 n = 0
 for n in range(len(self.hand_dict[player])):
 print self.hand_dict[player][n]
 n += 1
 print "print card rows"
 # list of rows
 first_cards = []
 second_cards = [] 
 third_cards = []
 fourth_cards = []
 fifth_cards = []
 list_of_rows_raw = [first_cards, second_cards, third_cards, fourth_cards, fifth_cards]
 for x in self.hand_dict:
 first_cards.append(self.hand_dict[x][0])
 for y in self.hand_dict:
 second_cards.append(self.hand_dict[y][1])
 table = texttable.Texttable()
 header = self.hand_dict.keys()
 table.header(header)
 table.add_rows([first_cards, second_cards], header=False)
 print table.draw()
 def end_game(self):
 # check to see how each completed hand did against the dealer
 dpoints = self.deck.d_hand.hand_points
 for hand in Hand.instances:
 hand.stack_change = 0
 if hand.status == 'completed':
 # print "hand is %r" hand.name
 print "hand points are %r" % hand.hand_points
 print "dpoints are %r" % dpoints
 print "bet is %r" % hand.bet
 if hand.hand_points == 21:
 if hand.black_jack == 1:
 hand.stack_change = hand.stack_change + ((float(3)/2) * hand.bet)
 hand.outcome = "Black Jack!!"
 else:
 hand.stack_change += hand.bet
 hand.outcome = "Win"
 elif hand.hand_points > 21:
 hand.stack_change -= hand.bet
 hand.outcome = "Lose"
 elif dpoints > 21: 
 hand.stack_change += hand.bet
 hand.outcome = "Win"
 elif hand.hand_points < dpoints:
 hand.stack_change -= hand.bet
 hand.outcome = "Lose"
 elif hand.hand_points > dpoints:
 hand.stack_change += hand.bet
 hand.outcome = "Win" 
 elif hand.hand_points == dpoints:
 hand.stack_change + 0
 hand.outcome = "Push" 
 else:
 print "not sure what to tell you"
 exit(0)
 self.check_score()
 def check_score(self):
 #check to see if player has money to play again
 total_winnings = 0
 for hand in Hand.instances:
 total_winnings = total_winnings + hand.stack_change
 if stack.stored_end + total_winnings < 0:
 print "You're out of cash. Goodbye!"
 exit(0)
 else:
 stack.stored_end += total_winnings
 self.replay(0, total_winnings)
 def replay(self, miss_type, total_winnings):
 # show table of hands
 os.system("clear")
 print "Your hands: \n"
 hands_dict = {}
 bets_dict = {}
 outcomes_dict = {}
 points_dict = {}
 for hand in Hand.instances:
 hands_dict.setdefault(hand.name, hand.hand_a)
 bets_dict.setdefault(hand.name, hand.bet)
 outcomes_dict.setdefault(hand.name, hand.outcome)
 points_dict.setdefault(hand.name, hand.hand_points)
 first_cards = []
 second_cards = []
 third_cards = []
 fourth_cards = []
 fifth_cards = []
 bets_list = []
 outcomes_list = []
 points_list = []
 for hand in hands_dict:
 first_cards.append(hands_dict[hand].get(0))
 second_cards.append(hands_dict[hand].get(1))
 third_cards.append(hands_dict[hand].get(2))
 fourth_cards.append(hands_dict[hand].get(3))
 fifth_cards.append(hands_dict[hand].get(4))
 bets_list.append("Bet is %r" % bets_dict[hand])
 points_list.append("Points are %r" % points_dict[hand])
 outcomes_list.append(outcomes_dict[hand])
 header = hands_dict.keys()
 table = texttable.Texttable()
 table.header(header)
 table.add_rows([first_cards, second_cards, third_cards, fourth_cards, fifth_cards, points_list, bets_list, outcomes_list], header = False)
 print table.draw()
 print "\n"
 self.deck.d_hand.show_hand()
 print "Dealer's points are %r." % self.deck.d_hand.hand_points
 print "\nYou won $%r" % total_winnings
 print "Your stack is now $%r\n" % stack.stored_end
 if stack.stored_end <=0:
 print "You're out of cash. Better luck next time!"
 exit(0)
 while True:
 print "\nWould you like to play again?"
 a = raw_input(' >')
 if a == 'no':
 print "Thanks for playing."
 print "You ended up with $%d\n" % stack.stored_end
 exit(0)
 elif a =='yes':
 hil = len(Hand.instances) # hil = hand instances length
 for x in range(hil):
 del Hand.instances[x-1]
 time.sleep(1)
 game.play_game(1, total_winnings) 
 break 
 else:
 print "just 'yes' or 'no' please."
 time.sleep(1)
 def load_inc_hands(self):
 # load first opening hand into incomplete hadns dict.
 # remove dealer hand 
 lnth = len(Hand.instances)
 for x in range(lnth):
 if Hand.instances[x-1].name == 'Dealer':
 del Hand.instances[x-1]
 lnth = len(Hand.instances)
 for z in range(lnth):
 self.incomplete_hands.append(Hand.instances[z].name)
 def play_game(self, replay, total_winnings):
# start
 self.deck = DECK()
 if replay == 1:
 self.stack = Stack(1 , total_winnings)
 os.system("clear")
 print "Let's play!\n"
 print " _ " * 10
 begin = stack.stored_end # need this here for when game is replayed
 begin, change, end = stack.account(0, begin) #laod account func with initical balance
 print "You have $%d in your stack.\n" % end
 print " _ " * 10
 time.sleep(0.5)
 play_deck = self.deck.full_deck
# bet
 stack.bet_test(0)
 bet = stack.stored_bet
# deal
 self.deck.deal()
# attach the bet to the starting hand
 self.deck.start_hand.bet = bet
 os.system("clear")
 print "deck has %r cads" % len(play_deck.keys())
 print "play_game method of Game class\n" 
 # load incomeplete hands dict
 self.load_inc_hands()
# go thorugh each hand and hit or stick
 for hand in Hand.instances:
 if hand.status == 'incomplete':
 print "you're stuck in the hos loop"
 hand.hos() 
# go to dealer's hand 
 self.deck.dhos()
 exit(0)
if __name__ == '__main__':
 game = Game()
 stack = Stack(0, 0)
 stack.load_account()
 game.play_game(0, 0)

First, if there's a better way to get the code in here than copy and paste, please let me know. Checking all the indents in python is a bit of a pain.

Second, please rip up this code! I'm trying to learn Python on my own and need all the feedback I can get. Please comment on everything: best practices, conventions...etc. Any and all comments welcome.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 30, 2012 at 19:43
\$\endgroup\$
2
  • \$\begingroup\$ about DECK() class; it is convention to use CapWords for class names. \$\endgroup\$ Commented Aug 30, 2012 at 19:55
  • 1
    \$\begingroup\$ You should either remove from random import choice or replace all instances of random.choice with choice \$\endgroup\$ Commented Dec 23, 2013 at 23:56

2 Answers 2

11
\$\begingroup\$
  1. Python convention is to name classes with CamelCase, not ALL_CAPS
  2. Don't use 1 and 0 for boolean values, use True and False
  3. Don't mix UI and logic. You should have some classes/functions that implement the rules for the game, and completely different classes/functions for the user interface.
  4. Avoid the use of mutable class variable like Hand.instances
  5. Your classes don't seem to have a defined identity. Your Stack class seems like it should represent a stack of chips. But it also seems to be keeping track of past history a little bit? I'm really not quite sure what the replay logic is doing.
  6. The rules of the game seem to be strewn all over the code. Look at the number of places that have 21.

Let's take a detailed look at one function:

def bet_test(self, miss_type):
 # collect's bet and check input
 # miss_type should start as 0
 possible_bets = ['5', '10', '15', '20', '25']

Constant lists like this should be global constants.

 while True:
 print "\nWhat is your bet? (5, 10, 15, 20, 25)"
 bet = raw_input(' >')
 if bet in possible_bets:
 bet = int(bet)

I don't like reusing variables for different things. i.e. the text and number version of the bet. I'd store them in different names.

 if self.replay == 1:

Use if self.replay: for boolean values

 begin = self.stored_end

You set begin, but you don't seem to do anything with it...

 self.stored_bet = int(bet)

bet is already an int

 break
 else:
 if bet > self.stored_end:

Why not use an elif here to force,

 print "You don't have enough for that bet."
 time.sleep(2)
 else:
 begin = self.stored_end
 self.stored_bet = bet
 break

You've done this twice. You should rearrange the code so that it happens only once.

Here's my reworking of that function:

def bet_test(self, miss_type):
 # collect's bet and check input
 # miss_type should start as 0
 possible_bets = range(5, min(25, self.stored_end+1), 5)
 while True:
 print "\nWhat is your bet? (%s)" % (", ".join(possible_bets))
 try:
 bet = int(raw_input(' >'))
 except ValueError:
 pass # make them try again
 else:
 if bet in possible_bets:
 self.stored_bet = bet
 break
 self.stored_bet = bet
answered Aug 31, 2012 at 2:06
\$\endgroup\$
0
7
\$\begingroup\$

Some other points:

  • from random import choice, but you only ever use random.choice not choice.
  • You often use something like: full_deck.pop(random.choice(full_deck.keys())). Rather than this, consider using a list rather than a dictionary and using random.shuffle to shuffle the pack at the start. Then it's just a case of popping one.
  • You're holding the hands as dictionaries as well, for no purpose other than indexing them with 0, 1 etc. Don't forget that you can use lists for this!

  • In Deck.init:

    n = 0
    i = 0
    for n in range(6):
     for s in suite:
     for r in rank:
     self.full_deck[i + n] = "%s of %s" % (r, s)
     i += 1
     n += 1
    

    n seems to be part of some leftover code, n = 0 and n += 1 are redundant, its values are directly taken from the range object. For that matter, I don't know why that loop is there at all.

  • Use full variable/function names. There's nothing wrong with using dealer_hit_or_stick as a name rather than dhos.

  • For asking questions, since you're picking from a defined list of responses, separate it into a function, which will simplify your code a bit. get_player_response( question, options )

  • Try to avoid using strings as return values (e.g. in split_test) - there's always a possibility of a hard-to-track spelling mistake. If you can't use a boolean return, make up some constants for them. This also applies to get_player_response - if you pass a dictionary (string:constant) as the options, you can test against the keys and return the value.

  • In the place where you're using split_test (which should really be renamed is_split_allowed), you can avoid the repeated 21 test with a bit of rearrangement.

  • Printing the hands should be put into a separate function (avoid repeated code!)

answered Aug 31, 2012 at 7:09
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.