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.

\$\endgroup\$
  • \$\begingroup\$ about DECK() class; it is convention to use CapWords for class names. \$\endgroup\$ – yasar Aug 30 '12 at 19:55
  • 1
    \$\begingroup\$ You should either remove from random import choice or replace all instances of random.choice with choice \$\endgroup\$ – Timtech Dec 23 '13 at 23:56
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
\$\endgroup\$
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!)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.