Script di blackjack in Python

Oct 23 2020

Recentemente ho avuto un test di Python e, sfortunatamente, ho fallito. Ripeterò il test e l'insegnante mi ha dato il consiglio di lavorare in modo più efficiente e pulito. Per fare pratica, circa 2 settimane fa ho realizzato un gioco di blackjack con Python e gliel'ho mandato per controllare. ha ancora risposto e il mio test è la prossima settimana. Qualcuno può dare un'occhiata e magari sottolineare le cose che devono essere migliorate? per favore, voglio davvero superare questo 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)        

Qualsiasi commento è ben accetto, ma tieni presente che questo è il mio primo linguaggio di programmazione e ho ancora molto da imparare. Grazie!

Risposte

5 AryanParekh Oct 25 2020 at 03:39

Mi dispiace ma la mia conoscenza dei giochi di carte è arrugginita. Per favore correggimi se qualcosa non va!

Individua sempre input non validi

Supponiamo che l'utente stia per inserire qualcosa, che gli viene richiesto da questa riga di codice

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

seleziona l'importo da scommettere:

Ora, cosa succede se l'utente ha immesso accidentalmente E . In questo caso, il programma fallirà poiché si aspetta un input sotto forma di numero intero. Questo è il motivo per cui dovresti sempre rilevare input non validi utilizzando Try and Except in Python

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

In questo modo, se l'utente è entrato

seleziona l'importo da essere: mi piace python

Darebbe all'utente

Inserimento non valido! Per favore inserisci un numero

Non uscire dopo un input non valido / errato

Nel tuo programma, se l'utente inserisce una scommessa che è più del denaro che ha, il programma si ferma. Non verrà riprodotto di nuovo, perché dovrebbe accadere?

Dovresti chiedere all'utente di inserire nuovamente un input valido, in modo che qualsiasi errore da parte sua non comporti l'immediata chiusura del programma

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 cosa migliore da fare ora è spostarlo in una funzione separata chiamata take_input(), in modo che la tua blackjack()funzione possa rimanere pulita e ora prendere l'input è facile

bet = take_input()

Sì, hai scritto qualche altra riga di codice. Ma ora sai che il tuo programma farà la cosa giusta quando si verificano eccezioni.

Semplifica il codice - 1

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

Equivale a

first_name = "Jack of " + first_name

Non è necessario convertire in strcome first_nameè già una stringa.

Lo stesso vale per le seguenti righe che ho estratto dal tuo codice.

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

Evita i numeri magici

Prendi questo esempio

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... sono conosciuti come numeri magici . Ho dovuto riflettere per capire cosa stavano facendo qui fino a quando finalmente ho capito che erano carte.

Il modo migliore per affrontare questo problema è usare le enumerazioni di Python .

from enum import Enum

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

Correggere i valori se sono sbagliati.

Ora il tuo thread if-else sembra molto più chiaro al lettore

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

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

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

Un altro vantaggio è che cosa succede se si desidera modificare il valore del re da xa y. Andresti in centinaia di posti per scoprire dove potresti aver usato una costante numerica nel contesto del re?

qui puoi semplicemente impostare king.valuequello che vuoi.

import sleep as s

s(0.5)Anche questo mi aveva confuso all'inizio, dovevo scoprire cosa ssignificava. snon ha alcun significato, confonde solo chiunque legga il tuo codice. Tuttavia, sleepimplica chiaramente che tu voglia ... Dormire! Usa sempre nomi significativi

Suddividi il lavoro in funzioni

Attualmente, la tua blackjack()funzione è ingombra di molte attività che dovrebbero essere spostate nelle loro funzioni. Proprio come abbiamo spostato la procedura di input in una take_input()funzione separata , puoi creare molte funzioni significative come quelle draw_new_card()che possono restituire una nuova carta dal mazzo.

Il tuo codice è DRY o WET

Scusa la mia conoscenza dei giochi di carte

Hai la procedura

  1. pesca una carta
  2. controlla se la carta è di >=11 punti
  3. stampare il punto e il nome

Allora perché ripetere di nuovo la stessa cosa per la seconda carta? Hai scritto la stessa cosa due volte. Una volta per la prima carta e la successiva per la seconda. ti sei ripetuto. Il modo migliore è scomporre la ripetizione in una funzione. Quindi tutto ciò che devi fare è

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)

......

Puoi vedere che l'uso delle funzioni ha aiutato molto.

4 Anonymous Oct 25 2020 at 05:43

Convenzioni di denominazione

Solo per rafforzare il punto sollevato da @Aryan Parekh: non usare abbreviazioni prive di significato, ad esempio:

import random as rd
from time import sleep as s

Non ci sono vantaggi, hai reso il codice più difficile da leggere e comprendere. Quindi: utilizzare random.choice(cards)al posto di: rd.choice(cards). random.choice si spiega da sé.

Un buon codice dovrebbe essere intuitivo, che inizia con le convenzioni di denominazione appropriate. Anche se sei pigro dovresti usare nomi più lunghi e descrittivi, il tuo IDE dovrebbe comunque avere il completamento automatico.

Hai variabili come a, cd2, x, y che mi ricordano gli spaghetti Basic degli anni '80. Faccio davvero schifo ai giochi di carte, quindi non posso commentare molto sull'algoritmo ma posso commentare il codice.

Per fortuna hai messo dei commenti.

Consistenza

Usi la funzione di scelta un paio di volte ma con nomi di variabili molto diversi:

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

e più tardi:

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

Penso che qui sia necessaria una maggiore coerenza . Se riutilizzi alcune affermazioni potresti anche usare gli stessi nomi di variabile altrove o almeno attenersi ad alcuni modelli di denominazione che hanno senso. disegnare è un nome che ha senso. Ma add_number sembra davvero il nome di una funzione, quindi lo chiamerei card_number o qualcosa del genere (anche se stai effettivamente utilizzando quella variabile per incrementare un altro valore).

Ripetizione

C'è ripetizione nel tuo codice, ad esempio:

#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']))

Prima di tutto, alcune affermazioni sono ridondanti:

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

Poiché stai concatenando tre elenchi, anche l'oggetto risultante è un oggetto elenco. Quindi, cards = cards1 + cards2 + cards3è sufficiente e produce lo stesso risultato.

carte1 / 2/3 sono esattamente le stesse, quindi stai ripetendo la stessa identica cosa 3 volte di seguito. Questo è ovviamente sbagliato e può essere semplificato. Potresti semplicemente scrivere:

cards2 = cards1
cards3 = cards1

anche se non è elegante ma almeno eviti la ripetizione e il tuo intervallo viene dichiarato una sola volta.

Un modo migliore:

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

Pertanto, hai ripetuto la sequenza tre volte e hai creato un nuovo elenco. Dato che stai usando itertools, potresti anche usare itertools.repeat, che ti dà un generatore, mentre * nti dà un elenco, che va bene qui.

Concatenazione

    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 è una stringa, quindi puoi concatenare in sicurezza tutti questi elementi. O meglio ancora, usa una stringa F (Python> = 3.6):

full_name = f"Jack of {full_name}"