Python Forum
More of my learning
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
More of my learning
#1
Took a break while I worked a bunch of overtime, but had a rare day off yesterday. Decided to continue my learning. Read a bit about python classes. The only use for classes that popped into my head was playing cards, so I threw together a simple game of "blackjack". Hesitate to call it that as its just player vs dealer, no betting, no soft hits for dealer, etc. 

I'm sure your going to find LOTS of things I can do better, don't need at all, or just flat out screwed up, but I'm very new at all of this and I'm trying. Any pointers will be greatly appreciate

import random
import time


# Card images

cardpic=['''
|---|
| A |
|---|''','''
|---|
| 2 |
|---|''','''
|---|
| 3 |
|---|''',''' 
|---|
| 4 |
|---|''','''
|---|
| 5 |
|---|''','''
|---|
| 6 |
|---|''','''
|---|
| 7 |
|---|''','''
|---|
| 8 |
|---|''','''
|---|
| 9 |
|---|''','''
|---|
|10 |
|---|''','''
|---|
| J |
|---|''','''
|---|
| Q |
|---|''','''
|---|
| K |
|---|''']



# Class to give card values

class Cards:
    def __init__(self, card, value, draw):
        self.card = card
        self.value = value
        self.draw = draw


# Build the deck
        
deck=[]
deck.append(Cards("TWO", 2, 1))
deck.append(Cards("THREE", 3, 2))
deck.append(Cards("FOUR", 4, 3))
deck.append(Cards("FIVE", 5, 4))
deck.append(Cards("SIX", 6, 5))
deck.append(Cards("SEVEN", 7, 6))
deck.append(Cards("EIGHT", 8, 7))
deck.append(Cards("NINE", 9, 8))
deck.append(Cards("TEN", 10, 9))
deck.append(Cards("JACK", 10, 10))
deck.append(Cards("QUEEN", 10, 11))
deck.append(Cards("KING", 10, 12))
deck.append(Cards("ACE", 11, 0))


# Variables
    
player=[]
playertotal=0
cpu=[]
cputotal=0


# Pick a player card
    
def playerhit():
    global playertotal
    global player
    deal=random.randint(0,12)
    car=deck[deal].card
    num=deck[deal].value
    image=deck[deal].draw
    player.append(Cards(car, num, image))
    playertotal=playertotal+num


# Pick a dealer card

def cpuhit():
    global cputotal
    global cpu
    deal=random.randint(0,12)
    car=deck[deal].card
    num=deck[deal].value
    image=deck[deal].draw
    cpu.append(Cards(car, num, image))
    cputotal=cputotal+num


# Draw the cards on screen

def table():
    for i in range(len(player)):
        print(cardpic[player[i].draw])
    print("_____")
    for i in range(len(cpu)):
        print(cardpic[cpu[i].draw])    
    print("\n" * 5)


# Reset the game

def start():
    print("\n" * 5)
    global player
    global cpu
    global playertotal
    global cputotal
    playertotal=0
    cputotal=0
    player=[]
    cpu=[]        
    playerhit()
    playerhit()
    cpuhit()
    cpuhit()   


# Adjust player ACES when busted

def checkplayeraces():
    global playertotal
    for i in player:
        if i.value == 11:
            i.value = 1
            playertotal=playertotal-10


# Adjust dealer ACES when busted

def checkcpuaces():
    global cputotal
    for i in cpu:
        if i.value == 11:
            i.value = 1
            cputotal=cputotal-10


# Decide winner

def cpustay():
    if cputotal > playertotal:
        print()
        print("You Lose")
        print()
        play()
    elif cputotal == playertotal:
        print()
        print("Draw")
        print()
        play()
    elif cputotal < playertotal:
        print()
        print("You Win")
        print()
        play()


# Dealers turn
        
def cputurn():
    ai=True
    while ai==True:
        time.sleep(2)
        if cputotal > 21:
            checkcpuaces()
        if cputotal > 21:    
            print()
            print("Dealer Busts, You Win!")
            print()
            play()
            ai=False
        if cputotal < 17:
            cpuhit()
            table()
        else:
            cpustay()
            table()
            ai=False


# Deal & players turn

def play():
    start()
    run=True
    while run==True:
        table()
        if input()=="h":
            playerhit()
            if playertotal > 21:
                checkplayeraces()
            if playertotal > 21:
                table()
                print()
                print("Bust! You Lose")
                print()
                play()
        else:
            cputurn()


# Begin

play()
Reply
#2
There's a lot to be said about your code, but I'll just mention a few points here:
  • Using globals for state is bad, this is exactly what classes are for
  • You should use __str__ for the string representation of a class, instead of storing it as an attribute with a weird workaround
  • The way you're using for loops suggests you should take a look at nedbat's Loop Like A Native
  • A lot of your code could be simplified significantly, by slightly changing your approach, and learning a few additional things about python and/or its standard library. For example:
        deal=random.randint(0,12)
        car=deck[deal].card
        num=deck[deal].value
        image=deck[deal].draw
        player.append(Cards(car, num, image))
    could be simplified to:
        player.append(random.choice(deck))
Also,the code is a bit hard to follow,and I'm sure there are other things which could be improved.
Although, if I were you, I would address these things first and then post again for further advice.
Reply
#3
The way you're using a class is really just as a sexier dict.  In other languages, it'd be a nice little struct/typedef.  Classes are (in my opinion) best used to bundle functionality together.  If you'll notice, your playerhit() and cpuhit() functions are almost exactly the same, except the values they use to make a decision are different.  Which means I'd probably start just by changing those two functions to instead be a single function.  

Also, deck has instances of Card, but instead of re-using those instances (that you already have, right there!), you create brand new Card instances.  ...why?

I don't think the Card should be the thing you need a class for, I think a player is what you need a class for.  Something like:
#...this maybe should be called Hand, since it doesn't clear itself...
class Player(object):
    def __init__(self):
        # maybe keep track of how many chips they start with?
        self.cards = []

    def current_total(self):
        total = 0
        for card in self.cards:
            total += card.value

        return total

    def hit(self, new_card):
        self.cards.append(new_card)

    def should_hit(self, dealer_showing=0):
        total = self.current_total()
        # not actually basic strategy...
        if dealer_showing <= 12:
            return total <= 11
        return total <= 16
Then you'd have two or more instances of Player, which each keep track of whatever cards they're dealt and use that information to decide for themselves if they should keep getting more cards:
players = [Player() for _ in range(5)] # it's full table, why not
dealer = Player()
dealer.hit(pick_a_card())
for player in players:
    player.hit(pick_a_card())

dealer_total = dealer.current_total()
for player in players:
    while player.should_hit(dealer_total):
        player.hit(pick_a_card())
    player_total = player.current_total()

while dealer.should_hit():
    dealer.hit(pick_a_card())
dealer_total = dealer.current_total()

for player in players:
    if player_total > dealer_total:
        # winner!
    # don't forget to handle busts... and the dealer's hidden card
Now, let's talk about those card pics... They're really all the same, except you change a single character.  I hate (and you should too!) seeing the same thing over and over.  If you find yourself doing the copy-paste dance, there's probably a better way to do it.  And there is.  In this instance, probably string formatting, with a base of what all cards look like, using a placeholder for the actual value of the card.  Maybe...
>>> card_template = '''
... |---|
... | {0:2}|
... |---|'''

>>> cards = [str(i) for i in range(2, 11)] + list('AKQJ')
>>>
>>> cards
['2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'K', 'Q', 'J']
>>> for card in cards:
...   print(card_template.format(card))
...
Which would give you...
Output:
|---| | 2 | |---| --snip-- |---| | 10| |---| |---| | A | |---| |---| | K | |---| --snip--
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020