Скрипт блэкджека на Python

Oct 23 2020

Недавно я прошел тест на питоне и, к сожалению, не смог. Я собираюсь повторить свой тест, и учитель посоветовал мне работать более эффективно и чисто. Чтобы попрактиковаться в этом, около 2 недель назад я сделал игру в блэкджек на python и отправил ему на проверку. он еще не ответил, и мой тест на следующей неделе. Может ли кто-нибудь взглянуть и, возможно, указать на вещи, которые нуждаются в улучшении? пожалуйста, я очень хочу пройти этот тест.

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)        

Любые комментарии приветствуются, но имейте в виду, что это мой первый язык программирования, и мне еще предстоит многому научиться. Спасибо!

Ответы

5 AryanParekh Oct 25 2020 at 03:39

Извините, но мои познания в карточных играх заржавели. Пожалуйста, поправьте меня, если что-то не так!

Всегда ловить неверный ввод

Предположим, что пользователь собирается ввести что-то, что ему предлагается в этой строке кода.

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

выберите сумму ставки:

Теперь, если пользователь случайно ввел E . В этом случае ваша программа потерпит неудачу, поскольку она ожидает ввода в виде целого числа. Вот почему вы всегда должны ловить недопустимый ввод, используя Try and Except в Python.

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

Таким образом, если пользователь ввел

выберите сумму: мне нравится питон

Это даст пользователю

Неверный Ввод! Пожалуйста, введите число

Не выходить после неверного / неправильного ввода

В вашей программе, если пользователь делает ставку, превышающую его деньги, программа просто останавливается. Он не будет воспроизводиться снова, почему это должно произойти?

Вы должны попросить пользователя снова ввести правильный ввод, чтобы любая его ошибка не привела к немедленному завершению программы.

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

Лучшее, что можно сделать сейчас, - это переместить это в отдельную вызываемую функцию take_input(), чтобы ваша blackjack()функция могла оставаться чистой, и теперь ввод данных становится легким.

bet = take_input()

Да, вы написали еще несколько строк кода. Но теперь вы знаете, что ваша программа будет поступать правильно, когда возникают исключения.

Упростить код - 1

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

Такой же как

first_name = "Jack of " + first_name

Вам не нужно конвертировать , strкак first_nameэто уже строка.

То же самое относится к следующим строкам, которые я извлек из вашего кода.

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

Избегайте волшебных чисел

Возьмите этот пример

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..., называются магическими числами . Мне нужно было подумать, пока я не понял, что они здесь делают, пока наконец не понял, что это карты.

Хороший способ справиться с этим - использовать перечисления Python .

from enum import Enum

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

Исправьте значения, если они неверны.

Теперь ваш поток if-else выглядит для читателя намного понятнее

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

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

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

Еще один плюс в том, что если вы хотите изменить значение короля с xна y. Поехали бы вы в сотни мест, чтобы найти, где вы могли бы использовать числовую константу в контексте короля?

здесь вы можете просто установить king.valueвсе, что хотите.

import sleep as s

s(0.5)Это тоже сбило меня с толку вначале, я должен был понять, что это sзначит. sвообще не имеет смысла, это просто сбивает с толку любого, кто читает ваш код. Однако sleepявно подразумевает, что вы хотите ... спать! Всегда используйте значащие имена

Разделить работу на функции

В настоящее время ваша blackjack()функция загромождена множеством задач, которые следует перенести в их собственные функции. Так же, как мы переместили процедуру ввода в отдельную take_input()функцию, вы можете создать множество значимых функций, например, draw_new_card()которые могут возвращать новую карту из колоды.

Ваш код СУХОЙ или ВЛАЖНЫЙ?

Извините за знание карточных игр

У вас есть процедура

  1. Нарисовать карточку
  2. проверьте, есть ли на карте >=11 баллов
  3. распечатать точку и имя

Тогда зачем повторять то же самое для второй карты? Вы написали одно и то же дважды. Один раз для первой карты, а следующий - для второй. вы повторили себя. Лучший способ - включить повторение в функцию. Так что все, что вам нужно сделать, это

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)

......

Как видите, использование функций очень помогло.

4 Anonymous Oct 25 2020 at 05:43

Соглашения об именах

Просто чтобы подтвердить мысль, высказанную @Aryan Parekh: не используйте бессмысленные сокращения, например:

import random as rd
from time import sleep as s

Нет никакой пользы, вы усложнили чтение и понимание кода. Итак: использовать random.choice(cards)вместо: rd.choice(cards). random.choice не требует пояснений.

Хороший код должен быть интуитивно понятным, что начинается с правильных соглашений об именах. Даже если вы ленивы, вам следует использовать более длинные и описательные имена, в вашей среде IDE все равно должно быть автозаполнение.

У вас есть такие переменные, как a, cd2, x, y, которые напоминают мне спагетти Basic 80-х годов. Я совершенно не разбираюсь в карточных играх, поэтому я не могу много комментировать алгоритм, но могу комментировать код.

К счастью, вы добавили несколько комментариев.

Последовательность

Вы используете функцию выбора пару раз, но с очень разными именами переменных:

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

и позже:

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

Я думаю, здесь требуется больше последовательности . Если вы повторно используете некоторые операторы, вы можете использовать те же имена переменных в другом месте или, по крайней мере, придерживаться некоторых шаблонов именования, которые имеют смысл. draw - это имя, которое имеет смысл. Но add_number действительно выглядит как имя функции, поэтому я бы назвал его card_number или что-то в этом роде (даже если вы эффективно используете эту переменную для увеличения другого значения).

Репетиция

В вашем коде есть повторение, например:

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

Поскольку вы объединяете три списка, результирующий объект также является объектом списка. Итак, cards = cards1 + cards2 + cards3достаточно и дает тот же результат.

card1 / 2/3 абсолютно одинаковы, поэтому вы повторяете одно и то же 3 раза подряд. Это явно неверно, и его можно упростить. Вы можете просто написать:

cards2 = cards1
cards3 = cards1

хотя это не элегантно, но, по крайней мере, вы избегаете повторения, и ваш диапазон объявляется только один раз.

Лучший способ:

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

Таким образом, вы повторили последовательность трижды и создали новый список. Поскольку вы используете itertools, вы также можете использовать itertools.repeat, который дает вам генератор, тогда как * nдает вам список, что здесь очень хорошо.

Конкатенация

    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 - это строка, поэтому вы можете безопасно объединить все эти элементы. Или еще лучше используйте F-строку (Python> = 3.6):

full_name = f"Jack of {full_name}"