Script de blackjack en Python

Oct 23 2020

Récemment, j'ai eu un test python et, malheureusement, j'ai échoué. Je vais refaire mon test et le professeur m'a donné un conseil pour travailler plus efficacement et plus propre. Pour pratiquer cela, j'ai fait un jeu de blackjack il y a environ 2 semaines avec python et je lui ai envoyé pour vérifier. il a encore répondu et mon test est la semaine prochaine. Quelqu'un peut-il jeter un coup d'œil et peut-être signaler les choses qui doivent être améliorées? s'il vous plaît, je veux vraiment réussir ce test.

import itertools
import random as rd
from time import sleep as s

#making 3 decks with playing cards and assign them 2 to 14
cards1 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))
cards2 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))
cards3 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))

#combine the 3 decks to make 1
cards = list(cards1+cards2+cards3)

#shuffle deck
rd.shuffle(cards)

def blackjack(cards):
    money = 10
    while True:
        print('you have', money, 'money')
        bet = int(input('select amount to bet: \n'))
        if money < bet:
            print('you dont have that much money....')
        else:
            playing = True
            
            #draw first card and remove it from the deck
            fcard = rd.choice(cards)
            cards.remove(fcard)
            first_point, first_name = fcard
            
            #check if first card is 11 points or more (to change back to 10 points unless it's ace)
            if first_point == 11:
                first_point = 10
                first_name = str('Jack'+' of '+first_name)
            elif first_point == 12:
                first_point = 10
                first_name = str('Queen'+' of '+first_name)
            elif first_point == 13:
                first_point = 10
                first_name = str('King'+' of '+first_name)
            elif first_point == 14:
                first_point = 11
                first_name = str('Ace'+' of '+first_name)
            
            #show the first drawn card
            print(first_point, first_name)
            s(0.7)
            
            #draw second card and remove it from the deck
            scard = rd.choice(cards)
            cards.remove(scard)
            second_point, second_name = scard
            
            #checking second card for the same
            if second_point == 11:
                second_point = 10
                second_name = str('Jack'+' of '+second_name)
            elif second_point == 12:
                second_point = 10
                second_name = str('Queen'+' of '+second_name)
            elif second_point == 13:
                second_point = 10
                second_name = str('King'+' of '+second_name)
            elif second_point == 14:
                second_point = 11
                second_name = str('Ace'+' of '+second_name)            
            
            #show second card
            print(second_point, second_name)
            s(0.7)
            points = first_point + second_point
            
            #check if first 2 cards make a blackjack
            if points == 21:
                print('Blackjack!')
                bet *= 2
                print('you won', bet, 'money')
                money += bet
                playing = False
                
            print(points, 'points out of 21')
            if money == 0:
                print('you are broke!')
                exit()
            
            #after the first 2 cards i need to determine if the player wants more cards
            while playing:
                card = input('press enter to draw a card or type x to stop')
                if card != 'x':
                    a = rd.choice(cards)
                    x, y = a
                    #going through the same checking system as the first 2 cards
                    if x == 11:
                        y = str('Jack'+' of '+second_name)
                        x = 10
                    elif x == 12:
                        y = str('Queen'+' of '+second_name)
                        x = 10
                    elif x == 13:
                        y = str('King'+' of '+second_name)
                        x = 10
                    elif x == 14:
                        y = str('Ace'+' of '+second_name)
                        x = 11
                    print(x, y)
                    s(0.7)
                    cards.remove(a)
                    points += x
                    
                    if points > 21:
                        print('BUST')
                        points = 0
                        playing = False
                        
                #if the player has x as input the player stops drawing
                elif card == 'x':
                    playing = False
                print(points, 'points')
                
            #let the dealer do the same card drawing
            result = dealer_draw(cards)
            print('you scored: ', points, '\n', 'the bank scored: ', result)
            s(0.7)
            #compare obtained points with the dealer's points
            if points > result:
                print('you win!')
                money += bet
            elif points == result:
                print('draw')
            elif points < result:
                print('you lose')
                money -= bet
            elif points == 0 and result == 0:
                print('you lose')
                money -= bet
    

def dealer_draw(cards):
    #2 empty prints to maintain clear overview
    print()
    print()
    
    a = 0
    
    #first 2 cards (same as for the player until.....)
    cd1 = rd.choice(cards)
    cards.remove(cd1)
    points_first, name_first = cd1
    if points_first == 11:
        name_first = str('Jack'+' of '+name_first)
        points_first = 10
    elif points_first == 12:
        name_first = str('Queen'+' of '+name_first)
        points_first = 10
    elif points_first == 13:
        name_first = str('King'+' of '+name_first)
        points_first = 10
    elif points_first == 14:
        name_first = str('Jack'+' of '+name_first)
        points_first = 11
    
    print(points_first, name_first)
    s(0.7)
    
    cd2 = rd.choice(cards)
    cards.remove(cd2)
    points_second, name_second = cd2
    if points_second == 11:
        name_second = str('Jack'+' of '+name_second)
        points_second = 10
    elif points_second == 12:
        name_second = str('Queen'+' of '+name_second)
        points_second = 10
    elif points_second == 13:
        name_second = str('King'+' of '+name_second)
        points_second = 10
    elif points_second == 14:
        name_second = str('Ace'+' of '+name_second)
        points_second = 11
    
    print(points_second, name_second)
    s(0.7)
    
    #..... here (scroll up)
    
    full_points = points_first + points_second
    a += full_points
    
    #have the minimal bank draw set at 16
    
    while a < 16:
        print("bank's total = ", a)
        s(0.7)
        draw = rd.choice(cards)
        cards.remove(draw)
        add_number, full_name = draw
        if add_number == 11:
            full_name = str('Jack'+' of '+full_name)
            add_number = 10
        elif add_number == 12:
            full_name = str('Queen'+' of '+full_name)  
            add_number = 10
        elif add_number == 13:
            full_name = str('King'+' of '+full_name)  
            add_number = 10
        elif add_number == 14:
            full_name = str('Ace'+' of '+full_name)  
            add_number = 11
            
        print(add_number, full_name)
        s(0.7)
        a += add_number
        print("bank's total = ", a)
        s(0.7)
    #check if bank scored more than 21 and if so, return 0
    if a > 21:
        return 0
    else:
        return a
        
blackjack(cards)        

Tous les commentaires sont les bienvenus, mais gardez à l'esprit que c'est mon premier langage de programmation et que j'ai encore beaucoup à apprendre. Merci!

Réponses

5 AryanParekh Oct 25 2020 at 03:39

Je suis désolé mais mes connaissances en jeux de cartes sont rouillées. Veuillez me corriger si quelque chose ne va pas!

Toujours attraper une entrée invalide

Supposons que l'utilisateur va entrer quelque chose, qui lui est demandé à partir de cette ligne de code

print('you have', money, 'money')
bet = int(input('select amount to bet: \n'))

sélectionnez le montant à parier:

Maintenant, si l'utilisateur est entré accidentellement E . Dans ce cas, votre programme échouera car il attend une entrée sous la forme d'un entier. C'est pourquoi vous devez toujours détecter les entrées non valides en utilisant Try and Except en Python

try: 
    bet = int(input("select amount to be: ")) 
except Exception: 
    print("Invalid input! Please enter a number\n")

De cette façon, si l'utilisateur a entré

sélectionnez le montant à être: j'aime python

Cela donnerait à l'utilisateur

Entrée invalide! Veuillez saisir un nombre

Ne pas quitter après une entrée invalide / incorrecte

Dans votre programme, si l'utilisateur entre un pari supérieur à l'argent dont il dispose, le programme s'arrête simplement. Il ne jouera plus, pourquoi cela devrait-il arriver?

Vous devez demander à l'utilisateur de saisir à nouveau une entrée valide, afin que toute erreur de sa part n'entraîne pas l'arrêt immédiat du programme.

while True:
    try: 
        bet = int(input("select amount to be: ")) 
    except Exception: 
        print("Invalid input! Please enter a number\n")
        continue

    if bet > money:
       print("Bet placed higher than balance!")
       continue
    break

La meilleure chose à faire maintenant est de déplacer cela vers une fonction distincte appelée take_input(), afin que votre blackjack()fonction puisse rester propre, et maintenant la saisie est facile

bet = take_input()

Oui, vous avez écrit quelques lignes de code supplémentaires. Mais maintenant, vous savez que votre programme fera ce qu'il faut lorsque des exceptions se produisent.

Simplifier le code - 1

first_name = str('Jack'+' of '+first_name)

Est le même que

first_name = "Jack of " + first_name

Vous n'avez pas besoin de convertir en strtant que tel first_nameest déjà une chaîne.

La même chose s'applique aux lignes suivantes que j'ai extraites de votre code.

first_name = str('Queen'+' of '+first_name)
first_name = str('King'+' of '+first_name)
first_name = str('Ace'+' of '+first_name)

Évitez les nombres magiques

Prenons cet exemple

if first_point == 11:
    first_point = 10
    first_name = str('Jack'+' of '+first_name)
elif first_point == 12:
    first_point = 10
    first_name = str('Queen'+' of '+first_name)
elif first_point == 13:
    first_point = 10
    first_name = str('King'+' of '+first_name)
elif first_point == 14:
    first_point = 11
    first_name = str('Ace'+' of '+first_name)

10, 11, 12... sont appelés nombres magiques . J'ai dû réfléchir pour comprendre ce qu'ils faisaient ici jusqu'à ce que je comprenne enfin qu'il s'agissait de cartes.

Le bon moyen de gérer cela est d'utiliser les énumérations de Python .

from enum import Enum

class Card(Enum):
    jack = 11
    queen = 12
    king = 13
    ....

Corrigez les valeurs si elles sont fausses.

Maintenant, votre fil if-else semble tellement plus clair pour le lecteur

if first_point == Card.jack.value:
    ...

elif first_point == Card.queen.value:
    ...

elif first_point = Card.king.value:
   ....

Un autre point positif est que si vous voulez changer la valeur du roi de xà y. Souhaitez-vous aller dans les centaines d'endroits pour trouver où vous pourriez avoir utilisé une constante numérique dans le contexte du roi?

ici, vous pouvez simplement régler king.valuece que vous voulez.

import sleep as s

s(0.5)Cela m'avait aussi confus au début, je devais découvrir ce sque cela signifiait. sn'a pas du tout de sens, cela déroute tous ceux qui lisent votre code. Cependant, sleepcela implique clairement que vous voulez ... Dormir! Utilisez toujours des noms significatifs

Diviser le travail en fonctions

Actuellement, votre blackjack()fonction est encombrée de nombreuses tâches qui devraient être déplacées vers leurs propres fonctions. Tout comme nous avons déplacé la procédure d'entrée dans une take_input()fonction distincte , vous pouvez créer de nombreuses fonctions significatives comme draw_new_card()celle qui peut renvoyer une nouvelle carte à partir du jeu.

Votre code est-il DRY ou WET

Excusez ma connaissance des jeux de cartes

Vous avez la procédure

  1. dessiner une carte
  2. vérifier si la carte est de >=11 points
  3. imprimer le point et le nom

Alors pourquoi répéter la même chose pour la deuxième carte? Vous avez écrit la même chose deux fois. Une fois pour la première carte, et ensuite pour la seconde. vous vous êtes répété. Le meilleur moyen est de prendre en compte la répétition dans une fonction. Pour que tout ce que vous ayez à faire est

def new_card():
    card = draw_new_card()
    point, name = card
    process_card(point, name)
    return point, name

# in the blackjack  function #

first_point, first_name = new_card()
print(first_point, first_name)
sleep(0.5)

second_point, second_name = new_card()
print(second_point, second_name)

......

Vous pouvez voir que l'utilisation des fonctions a beaucoup aidé.

4 Anonymous Oct 25 2020 at 05:43

Conventions de nommage

Juste pour renforcer le point soulevé par @Aryan Parekh: n'utilisez pas d'abréviations sans signification, par exemple:

import random as rd
from time import sleep as s

Il n'y a aucun avantage, vous avez rendu le code plus difficile à lire et à comprendre. Donc: utiliser au random.choice(cards)lieu de: rd.choice(cards). random.choice est explicite.

Un bon code doit être intuitif, cela commence par des conventions de dénomination appropriées. Même si vous êtes paresseux, vous devriez utiliser des noms plus longs et plus descriptifs, votre IDE devrait quand même avoir une saisie semi-automatique.

Vous avez des variables comme a, cd2, x, y qui me rappellent les spaghettis Basic des années 80. Je suis totalement nul dans les jeux de cartes, donc je ne peux pas beaucoup commenter l'algo mais je peux commenter le code.

Heureusement, vous avez mis quelques commentaires.

Cohérence

Vous utilisez la fonction de choix plusieurs fois mais avec des noms de variables très différents:

a = rd.choice(cards)
x, y = a

et ensuite:

draw = rd.choice(cards)
cards.remove(draw)
add_number, full_name = draw

Je pense que plus de cohérence est nécessaire ici. Si vous réutilisez certaines instructions, vous pouvez aussi bien utiliser les mêmes noms de variables ailleurs ou au moins vous en tenir à certains modèles de dénomination qui ont du sens. draw est un nom qui a du sens. Mais add_number ressemble vraiment à un nom de fonction, donc je l'appellerais card_number ou quelque chose comme ça (même si vous utilisez effectivement cette variable pour incrémenter une autre valeur).

Répétition

Il y a une répétition dans votre code, par exemple:

#making 3 decks with playing cards and assign them 2 to 14
cards1 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))
cards2 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))
cards3 = list(itertools.product(range(2, 15),['spade', 'heart', 'diamond', 'club']))

Tout d'abord, certaines déclarations sont redondantes:

#combine the 3 decks to make 1
cards = list(cards1+cards2+cards3)

Puisque vous concaténez trois listes, l'objet résultant est également un objet de liste. Donc, cards = cards1 + cards2 + cards3c'est suffisant et donne le même résultat.

les cartes 1/2/3 sont exactement les mêmes, vous répétez donc exactement la même chose 3 fois de suite. Ceci est évidemment faux et peut être simplifié. Vous pouvez simplement écrire:

cards2 = cards1
cards3 = cards1

même si ce n'est pas élégant mais au moins vous évitez les répétitions et votre plage n'est déclarée qu'une seule fois.

Une meilleure façon:

cards = list(itertools.product(range(2, 15), ['spade', 'heart', 'diamond', 'club'])) *3

Ainsi, vous avez répété votre séquence trois fois et créé une nouvelle liste. Puisque vous utilisez itertools, vous pouvez également utiliser itertools.repeat, qui vous donne un générateur, alors que * nvous donne une liste, ce qui est très bien ici.

Enchaînement

    draw = rd.choice(cards)
    cards.remove(draw)
    add_number, full_name = draw
    if add_number == 11:
        full_name = str('Jack'+' of '+full_name)
        add_number = 10

full_name est une chaîne, vous pouvez donc concaténer tous ces éléments en toute sécurité. Ou mieux encore, utilisez une chaîne F (Python> = 3,6):

full_name = f"Jack of {full_name}"