Script de blackjack em Python

Oct 23 2020

Recentemente, fiz um teste de python e, infelizmente, falhei. Vou refazer meu teste e a professora me deu a dica para trabalhar de forma mais eficiente e limpa. Para praticar isso, fiz um jogo de blackjack há cerca de 2 semanas com python e enviei para ele verificar. ele ainda respondeu e meu teste é na próxima semana. Alguém pode dar uma olhada e talvez apontar coisas que precisam ser melhoradas? por favor, eu realmente quero passar neste teste.

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)        

Quaisquer comentários são bem-vindos, mas tenha em mente que esta é minha primeira linguagem de programação e ainda tenho muito que aprender. Obrigado!

Respostas

5 AryanParekh Oct 25 2020 at 03:39

Sinto muito, mas meu conhecimento em jogos de cartas está enferrujado. Por favor, corrija-me se algo estiver errado!

Sempre capturar entrada inválida

Suponha que o usuário vai inserir algo, que é solicitado a ele a partir desta linha de código

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

selecione o valor da aposta:

Agora, e se o usuário entrou acidentalmente E . Nesse caso, seu programa falhará, pois espera uma entrada na forma de um inteiro. É por isso que você sempre deve capturar entradas inválidas usando Try and Except em Python

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

Dessa forma, se o usuário inseriu

selecione a quantidade para ser: eu gosto de python

Isso daria ao usuário

Entrada inválida! Por favor, coloque um numero

Não saia após uma entrada inválida / incorreta

No seu programa, se o usuário inserir uma aposta maior do que o dinheiro que ele possui, o programa simplesmente para. Não vai tocar novamente, por que isso deveria acontecer?

Você deve pedir ao usuário para inserir uma entrada válida novamente, para que qualquer erro dele não resulte no encerramento imediato do programa

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

A melhor coisa a fazer agora é mover isso para uma função separada chamada take_input(), para que sua blackjack()função possa permanecer limpa, e agora é fácil obter entradas

bet = take_input()

Sim, você escreveu mais algumas linhas de código. Mas agora você sabe que seu programa fará a coisa certa quando ocorrerem exceções.

Simplifique o código - 1

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

É o mesmo que

first_name = "Jack of " + first_name

Você não precisa converter para strporque first_namejá é uma string.

O mesmo se aplica às seguintes linhas que extraí de seu código.

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

Evite números mágicos

Veja este exemplo

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... são conhecidos como números mágicos . Tive que pensar um pouco para entender o que eles estavam fazendo aqui até que finalmente entendi que eram cartas.

A boa maneira de lidar com isso é usar os enums do Python .

from enum import Enum

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

Corrija os valores se eles estiverem errados.

Agora seu tópico if-else parece muito mais claro para o leitor

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

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

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

Outro ponto positivo é que se você quiser alterar o valor do rei de xpara y. Você iria a centenas de lugares para descobrir onde poderia ter usado uma constante numérica no contexto do rei?

aqui você pode apenas definir king.valueo que quiser.

import sleep as s

s(0.5)Isso também me deixou confuso no início, eu tinha que descobrir o que ssignificava. snão é significativo, apenas confunde qualquer pessoa que leia seu código. No entanto, sleepimplica claramente que você deseja ... Dormir! Sempre use nomes significativos

Divida o trabalho em funções

Atualmente, sua blackjack()função está repleta de muitas tarefas que devem ser movidas para suas próprias funções. Assim como movemos o procedimento de entrada para uma take_input()função separada , você pode criar várias funções significativas como draw_new_card()aquela que pode retornar uma nova carta do baralho.

Seu código é DRY ou WET?

Desculpe meu conhecimento de jogos de cartas

Você tem o procedimento

  1. desenhar um cartão
  2. verifique se o cartão é de >=11 pontos
  3. imprima o ponto e o nome

Então, por que repetir a mesma coisa novamente para a segunda carta? Você escreveu a mesma coisa duas vezes. Uma vez para o primeiro cartão e depois para o segundo. você se repetiu. A melhor maneira é fatorar a repetição em uma função. Então tudo que você precisa fazer é

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)

......

Você pode ver que o uso de funções ajudou muito.

4 Anonymous Oct 25 2020 at 05:43

Convenções de nomenclatura

Apenas para reforçar o ponto levantado por @Aryan Parekh: Não use abreviações sem sentido, por exemplo:

import random as rd
from time import sleep as s

Não há nenhum benefício, você tornou o código mais difícil de ler e entender. Assim: usar random.choice(cards)em vez de: rd.choice(cards). random.choice é autoexplicativo.

Um bom código deve ser intuitivo, que começa com as convenções de nomenclatura adequadas. Mesmo se você for preguiçoso, você deve usar nomes mais longos e descritivos; seu IDE deve ter preenchimento automático de qualquer maneira.

Você tem variáveis ​​como a, cd2, x, y que me lembram o spaghetti Basic dos anos 80. Eu sou totalmente péssimo em jogos de cartas, então não posso comentar muito sobre o algo, mas posso comentar sobre o código.

Felizmente, você fez alguns comentários.

Consistência

Você usa a função de escolha algumas vezes, mas com nomes de variáveis ​​muito diferentes:

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

e depois:

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

Eu acho que mais consistência é necessária aqui. Se você reutilizar algumas instruções, também pode usar os mesmos nomes de variáveis ​​em outro lugar ou pelo menos seguir alguns padrões de nomenclatura que façam sentido. desenhar é um nome que faz sentido. Mas add_number realmente se parece com um nome de função, então eu a chamaria de card_number ou algo parecido (mesmo que você esteja usando efetivamente essa variável para incrementar outro valor).

Repetição

Há repetição em seu código, por exemplo:

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

Em primeiro lugar, algumas declarações são redundantes:

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

Como você está concatenando três listas, o objeto resultante também é um objeto de lista. Portanto, cards = cards1 + cards2 + cards3é suficiente e produz o mesmo resultado.

cards1 / 2/3 são exatamente iguais, então você está repetindo exatamente a mesma coisa 3 vezes seguidas. Isso está obviamente errado e pode ser simplificado. Você pode simplesmente escrever:

cards2 = cards1
cards3 = cards1

embora isso não seja elegante, mas pelo menos você evita a repetição e seu intervalo é declarado apenas uma vez.

Uma maneira melhor:

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

Assim, você repetiu sua sequência três vezes e criou uma nova lista. Como você está usando itertools, também pode usar itertools.repeat, que fornece um gerador, enquanto * nfornece uma lista, o que é ótimo aqui.

Concatenação

    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 é uma string, então você pode concatenar com segurança todos esses itens. Ou melhor ainda, use uma string F (Python> = 3.6):

full_name = f"Jack of {full_name}"