9
\$\begingroup\$

Please review my game. I am learning Python now, so any suggestions are welcome. I am running this on a Linux machine, in Komodo and terminal.

#!/usr/bin/python
#encoding=utf8  
import random

# init global variables used for game
player = []
dealer = []

class Deck(object):
    def __init__(self,rank=[], suit=[], whole_deck=[], scrambled_deck=[]):
        self.rank = ['1','2','3','4','5','6','7','8','9','J','Q','K','A']
        self.suit = ['♠','♣','♥','♦︎︎']
        self.whole_deck = whole_deck
        self.scrambled_deck = scrambled_deck
        #construct deck from existing attributes
        def makedeck(self):
            self.whole_deck = []            
            i=0
            while i < 4:
                for r in self.rank:
                    self.whole_deck.append(r+self.suit[i])  
                i += 1
            return self.whole_deck
        self.whole_deck = makedeck(self)

    def draw_a_card(self):
        d=self.whole_deck.pop(random.choice(range(len(self.whole_deck))))
        return d

class Game(Deck):
    def __init__(self,credit=100, bet=10):
        Deck.__init__(self)
        self.credit = credit
        self.bet = bet
    def getcredit(self):
        while True:
            try:
                self.credit = int(raw_input("How many chips would you like to buy? "))
            except:
                print "You need to specify number"
                continue
            else:
                print str(self.credit) + ' Chips Purchased'
                print "Have fun!"
                break
        return self.credit
    def makebet(self):
        while True:
            try:
                self.bet = int(raw_input("How much do you want to bet? "))
            except:
                print "You need to specify number"
                continue
            if self.bet > self.credit:
                print 'You can\'t bet more than you have credit'
                continue
            else:
                print 'You are betting ' + str(self.bet) 
                break
    def deal(self, p_turn=0, d_turn=0):
        global dealer
        global player
        i = 0
        j = 0
        while i < p_turn:
            player.append(g.draw_a_card())
            i += 1
        while j < d_turn:
            dealer.append(g.draw_a_card())
            j += 1

def clear_screen():
    print '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'

####################Construct Cards####################
def makecard(dealer, player, hidden=0):
    card =   ['╔═══════╗\t','║ {}     ║\t','║       ║\t','║   {}   ║\t','║       ║\t','║     {} ║\t','╚═══════╝\t']
    h_card = ['╔═══════╗\t','║▓▓▓▓▓▓▓║\t' ,'║▒▒▒▒▒▒▒║\t','║▓▓▓▓▓▓▓║\t' ,'║▒▒▒▒▒▒▒║\t','║▓▓▓▓▓▓▓║\t' ,'╚═══════╝\t']

    global player_cards
    player_cards = ['' for i in player]
    c=0
    p=0
    while p < len(player):
        for c in range(0, len(card)):
            alter = [1,0,0,1,0,0,1]
            current_card=[player[p][:1], player[p][1:]]
            player_cards[p] += str(card[c]).format(current_card[alter[c]])
        player_cards[p] = player_cards[p].split("\t")
        p += 1

    global dealer_cards
    dealer_cards = ['' for i in dealer]
    c=0
    p=0 

    while p < len(dealer):
        for c in range(0, len(card)):
            alter = [1,0,0,1,0,0,1]
            current_card=[dealer[p][:1], dealer[p][1:]]
            dealer_cards[p] += str(card[c]).format(current_card[alter[c]])
        dealer_cards[p] = dealer_cards[p].split("\t")
        p += 1

    if  hidden == 1:
        dealer_cards[1]=''.join(h_card)
        dealer_cards[1] = dealer_cards[1].split("\t")

def printcards(dealer_cards, player_cards):
    for i in range(len(dealer_cards[0])):
        for j in range(len(dealer_cards)):
            print '{}'.format(dealer_cards[j][i]),
        print

    for i in range(len(player_cards[0])):
        for j in range(len(player_cards)):
            print '{}'.format(player_cards[j][i]),
        print
####################Construct Cards END####################

def makerank(who):
    i=0
    player_rank = []
    while i < len(who):
        player_rank.append(who[i][0])

        for ii in player_rank:
            player_rank = [10 if ii=='K' or ii=='Q' or ii=='J' else ii for ii in player_rank]

        for ii in player_rank:        
            if ii == 'A':
                tmp_suit = player_rank.pop()
    #            tmp_suit = player_rank.pop(player_rank.index(ii))            
                if sum([ int(x) for x in player_rank]) <= 10:
                    tmp_suit = 11
                    player_rank.append(tmp_suit)
                elif sum([ int(x) for x in player_rank]) > 10:
                    tmp_suit = 1
                    player_rank.append(tmp_suit)
        i += 1         

    player_rank = [ int(x) for x in player_rank ]
    return sum(player_rank)

def newdeck(mindecksize):
    d = Deck()
    if len(g.whole_deck) < mindecksize: 
        g.whole_deck = d.whole_deck

def cards(z):
    if z == 0:
        makecard(dealer, player, 0)
        printcards(dealer_cards, player_cards)
    elif z == 1:
        makecard(dealer, player, 1)
        printcards(dealer_cards, player_cards)

def winlose(who):
    if who == 'p':
        g.credit = g.credit + g.bet
        print 'You win!'
        print 'Your credit is: ' + str(g.credit)
    elif who == 'd':
        g.credit = g.credit - g.bet
        print 'Dealer wins!'
        print 'Your credit is: ' + str(g.credit)       
    elif who == 'dr':
        print 'Push'
        print 'Your credit is: ' + str(g.credit)               

def dealerplay():
    while makerank(dealer) < 17:
        g.deal(0,1)

#Debug functions, can be removed at any time

def debug_printset():
    i=0
    while i < len(player):
        print player[i]
        i += 1

# Functions end here
g = Game()

def play():
    d = Deck()
    global g
    global player
    global dealer
    i = 0
    clear_screen()
    g.getcredit()
    while True: #Whole game loop
        player = []
        dealer = []      
        g.deal(2,2)
        i += 1
        clear_screen()
        g.makebet()
        cards(1)
        newdeck(5)
        #########################Is it blackjack?#########################
        if makerank(player) == 21: 
            if i == 1 and makerank(dealer) != 21:
                print 'Blackjack!'
                winlose('p')
                break
            elif i == 1 and makerank(dealer) == 21:
                winlose('dr')
                break
        #########################Is it blackjack? END#########################        
        while True: #hand loop              
            player_input = str(raw_input("Press 'h' to hit, or 's' to stand \n"))
            if player_input == 'h':
                newdeck(1)
                g.deal(1)
                if makerank(player) > 21:
                    clear_screen()
                    cards(0)
                    print 'Busted!'
                    winlose('d')
                    break
                elif makerank(player) < 21:
                    clear_screen()
                    cards(1)
                    continue
                elif makerank(player) == 21:
                    clear_screen()
                    cards(0)
                    dealerplay()
                    if makerank(dealer) == 21:
                        winlose('dr')
                        break
                    elif makerank(dealer) < 21:
                        winlose('p')
                        break

            elif player_input == 's':
                dealerplay()
                clear_screen()
                cards(0)
                if  makerank(dealer) > 21:
                    winlose('p')
                    break
                elif makerank(dealer) > makerank(player):
                    winlose('d')
                    break
                elif makerank(dealer) < makerank(player):
                    winlose('p')
                    break
                elif makerank(dealer) == makerank(player):
                    winlose('dr')
                    break
###############Gameover?####################
        if g.credit == 0:
            print "Game Over"
            exit()  
###############Play again?##################            
        while True: 
            print len(g.whole_deck)
            print 'press \'d\' to deal again, or \'c\' to close'
            deal = str(raw_input())
            if deal == 'd':
                i -= 1
                break
            elif deal == 'c':
                print 'Game ended. Your remaining credit is: ' + str(g.credit)
                exit()
            else:
                print "Incorrect input, please input 'd' to Deal again, 'c' to close the game"
                continue
###############Play again? END##################            

play()
\$\endgroup\$
1
  • \$\begingroup\$ This is the coolest freaking thing I've ever seen in my life. \$\endgroup\$
    – papasmurf
    Nov 29, 2016 at 12:39

2 Answers 2

3
\$\begingroup\$

In general, I think you did a good job overall. That is, getting the game logic to run without serious issue. There were a couple of errors that caused the game to not run correctly. One was if the player gets a blackjack the game exits without prompting the user for further action:

LOGIC

while True: #Whole game loop
    player = []
    dealer = []      
    .....
    if makerank(player) == 21: 
        if i == 1 and makerank(dealer) != 21:
            print 'Blackjack!'
            winlose('p')
            break
        elif i == 1 and makerank(dealer) == 21:
            winlose('dr')
            break       
    while True: #hand loop              
    ..........

I shortened the code up here but you can see since you do your check outside of "hand-loop" and call break you will exit the program. This goes for if there is a push on blackjack too.

Exhausting the deck, where you only reset the deck if the player hits. This is because of how you are drawing cards by calling .pop.

.....
if player_input == 'h':
    newdeck(1)

.....
def draw_a_card(self):
    d=self.whole_deck.pop(random.choice(range(len(self.whole_deck))))
    return d

If you keep standing on every turn you will eventually get an IndexError. You could just call random.choice on the deck and not have to worry about it while using some other condition to reset the deck:

def draw_a_card(self):
    return random.choice(self.whole_deck)

STYLE

There are some style issues here too. These usually come down to personal preferences but if you are having others read your code its best to stay close to pep8 which is a general consesus for helpful style choices.

OOP

You also make use of global variables quite a bit. Usually using globals are considered bad practice. What you could do is think about where each one of these functions and variables would logically make sense to be grouped together. Then create a class to encapsulate the variables and functions, adhering to good OOP principles. You do this to a certain degree but you could further it by adding in the globals too:

class Game(Deck):
    def __init__(self,credit=100, bet=10):
        Deck.__init__(self)
        self.credit = credit
        self.bet = bet
        # make an attribute for hands
        self.player = []
        self.dealer = []

CODE DUPLICATION

You also have several spots of code duplication. Keeping with DRY (don't repeat yourself) you could use a loop or recursion to cut down on some of this:

def printcards(dealer_cards, player_cards):
    for i in range(len(dealer_cards[0])):
        for j in range(len(dealer_cards)):
            print '{}'.format(dealer_cards[j][i]),
        print

    for i in range(len(player_cards[0])):
        for j in range(len(player_cards)):
            print '{}'.format(player_cards[j][i]),
        print

Where you could pass in a list or any other kind of iterable and loop for them:

def printcards(hands): # where hands have both player and dealer cards
    for hand in hands:
        for i in range(len(hand[0]):
        .....

There are places where you are using counters too where iterating over the list itself or a range would be fine:

p=0
...
while p < len(dealer):
    for c in range(0, len(card)):
        alter = [1,0,0,1,0,0,1]
        current_card=[dealer[p][:1], dealer[p][1:]]
        dealer_cards[p] += str(card[c]).format(current_card[alter[c]])
    dealer_cards[p] = dealer_cards[p].split("\t")
    p += 1

would be:

for p in range(len(dealer)):
    for c in range(0, len(card)):
        alter = [1,0,0,1,0,0,1]
        current_card=[dealer[p][:1], dealer[p][1:]]
        dealer_cards[p] += str(card[c]).format(current_card[alter[c]])
    dealer_cards[p] = dealer_cards[p].split("\t")

STANDARD LIBRARY

One of the great things about Python is that there are a rich collection of libraries/modules already made by really intelligent people. Use this to your advantage and get to know different modules and what they offer:

self.whole_deck = []            
i=0
while i < 4:
    for r in self.rank:
        self.whole_deck.append(r+self.suit[i])  
    i += 1

becomes:

from itertools import product
.....
return [''.join(c) for c in product(self.rank, self.suit)]

Where you can use itertools.product to create a cartesian product

I re-wrote your program by creating a different game-loop and making a couple of different classes:

#!/usr/bin/python
#encoding=utf8  

import random


card =   ['╔═══════╗\t',
          '║ {}     ║\t',
          '║       ║\t',
          '║   {}   ║\t',
          '║       ║\t',
          '║     {} ║\t',
          '╚═══════╝\t'
         ]

h_card = ['╔═══════╗\t',
          '║▓▓▓▓▓▓▓║\t',
          '║▒▒▒▒▒▒▒║\t',
          '║▓▓▓▓▓▓▓║\t',
          '║▒▒▒▒▒▒▒║\t',
          '║▓▓▓▓▓▓▓║\t',
          '╚═══════╝\t'
         ]

def make_rank(hand):
    hand_rank = 0
    hand_str = ''.join(card[0] for card in hand)
    for card in hand_str:
        if card.isdigit():
            hand_rank += int(card)
        else:
            hand_rank += 10 if card in 'KQJ' else 11
    if hand_rank > 21 and 'A' in hand_str:
        hand_rank -= 10
    return hand_rank

def make_cards(hand, hidden=False):
    cards = [''.join(card).format(*c).split('\t') for c in hand]
    if hidden:
        cards[1] = ''.join(h_card).split('\t')
    return cards

def print_cards(hands, hidden=False):
    for pos, hand in enumerate(hands):
        cards = make_cards(hand, (not pos) & hidden)
        for card in zip(*cards):
            print(''.join(card))
        print

def clear_screen(func):
    def wrapper(*args, **kwargs):
        print '\n' * 66
        return func(*args)
    return wrapper

@clear_screen
def get_credit():
    while True:
        try:
            credit = int(raw_input("How many chips would you like to buy? "))
        except:
            print "You need to specify number"
            continue
        print '{} Chips Purchased'.format(credit)
        print "Have fun!"
        break
    return credit

@clear_screen
def make_bet(player):
    while True:
        try:
            bet = int(raw_input("How much do you want to bet? "))
        except:
            print "You need to specify number"
            continue
        if bet > player.credit:
            print 'You can\'t bet more than you have credit'
            continue
        print 'You are betting {}'.format(bet) 
        break
    player.credit -= bet
    return bet

def deal_again(option=''):
    if option == 'c':
        return False
    elif option == 'd':
        return True
    if option:
        print 'Incorrect input, please input "d" to Deal again, "c" to close the game'
    print 'press \'d\' to deal again, or \'c\' to close'
    option = raw_input()
    return deal_again(option)

class Deck(object):

    def __init__(self,rank=[], suit=[], whole_deck=[], scrambled_deck=[]):
        self.rank = ['2','3','4','5','6','7','8','9','J','Q','K','A']
        self.suit = ['♠','♣','♥','♦︎︎']
        self.whole_deck = whole_deck
        self.scrambled_deck = scrambled_deck
        def makedeck(self):
            return [(r, s, r) for r in self.rank for s in self.suit]
        self.whole_deck = makedeck(self)

    def draw_a_card(self):
        return random.choice(self.whole_deck)


class Player(object):

    def __init__(self, dealer):
        self.hand = []
        self.credit = 0
        self.dealer = dealer


class Dealer(object):

    def __init__(self, bet=10):
        self.deck = Deck()
        self.bet = bet
        self.hand = []
        self.table_hands = [self.hand]

    def add_player(self, player):
        self.table_hands.append(player)

    @property
    def clear_cards(self):
        self.table_hands = [[] for hand in self.table_hands]

    def deal_hand(self):
        self.clear_cards
        self.deal(self.table_hands, 2)

    def deal(self, hands, turn):
        for _ in range(turn):
            for hand in hands:
                hand.append(self.deck.draw_a_card())

    def winlose(self, who):
        payout = 0
        if who == 'p':
            print 'You win!'
            payout = self.bet * 2
        elif who == 'd':
            print 'Dealer wins!'
        else:
            print 'Push'
            payout = self.bet
        return payout

    @clear_screen
    def dealer_play(self, rank):
        dealer_hand = self.table_hands[0]
        if rank > 16:
            return rank
        self.deal([dealer_hand], 1)
        rank = make_rank(dealer_hand)
        print_cards(self.table_hands)
        return self.dealer_play(rank)

    @clear_screen
    def player_play(self, rank, option=''):
        player_hand = self.table_hands[1]
        if option == 'h':
            self.deal([player_hand], 1)
            rank = make_rank(player_hand)
        print_cards(self.table_hands, True)
        if rank >= 21 or option == 's':
            return rank
        player_input = raw_input('Press "h" to hit, or "s" to stand \n')
        return self.player_play(rank, player_input)


def play_hand(dealer, player):
    dealer.deal_hand()
    print_cards(dealer.table_hands, True)
    d_rank, p_rank = map(make_rank, dealer.table_hands)
    msg = ''
    if p_rank == 21 or d_rank == 21:
        msg = 'BlackJack!'
        winner = 'p' if p_rank == 21 and d_rank != 21 else \
                 'd' if p_rank != 21 else 'dr'
    else:
        p_rank = dealer.player_play(p_rank)
        if p_rank > 21:
            msg = 'Bust'
            winner = 'd'
        else:
            d_rank = dealer.dealer_play(d_rank)
            if d_rank > 21 or p_rank <= 21 and d_rank < p_rank:
                winner = 'p'
            elif p_rank < 21 and d_rank > p_rank:
                winner = 'd'
            else:
                winner = 'dr'
    clear_screen(lambda:None)()
    if msg:
        print msg
    player.credit += dealer.winlose(winner)
    print_cards(dealer.table_hands)
    print 'Your credit is: {}'.format(player.credit)

def take_seat_at_table():
    dealer = Dealer()
    player = Player(dealer)
    PLAYING = True
    player.credit = get_credit()
    dealer.add_player(player.hand)
    while PLAYING:
        dealer.bet = make_bet(player)
        play_hand(dealer, player)
        if player.credit == 0:
            print 'Game Over'
            PLAYING = False
        else:
            PLAYING = deal_again()
    print 'Game ended. Your remaining credit is: {}'.format(player.credit)

if __name__ == '__main__':
    take_seat_at_table()
\$\endgroup\$
1
  • \$\begingroup\$ This is the best review of my work ever, school university or anything else. It's amazing that you took so much time. Thanks a lot \$\endgroup\$
    – G.Gabunia
    Dec 4, 2016 at 9:54
2
\$\begingroup\$

As noted in a similar question, the deck should be constructed using a list comprehension. There is no rank "1", but there should be a "10".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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