Pythonのブラックジャックスクリプト

Oct 23 2020

最近、Pythonテストを行いましたが、残念ながら失敗しました。私はテストをやり直すつもりです、そして先生は私にもっと効率的できれいに働くためのヒントをくれました。これを練習するために、私は約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を入力した場合はどうなりますか。この場合、プログラムは整数形式の入力を想定しているため、失敗します。これが、PythonでTry andExceptを使用して常に無効な入力をキャッチする必要がある理由です。

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

このように、ユーザーが入力した場合

量を選択してください:私はPythonが好きです

それはユーザーに与えるでしょう

無効入力!番号を入力してください

無効/不正な入力後に終了しないでください

あなたのプログラムでは、ユーザーが彼が持っているお金よりも多い賭けを入力した場合、プログラムはただ停止します。それは再び再生されません、なぜこれが起こるべきですか?

ユーザーに有効な入力をもう一度入力するように依頼する必要があります。そうすれば、ユーザーが間違いを犯しても、プログラムがすぐに終了することはありません。

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()、デッキから新しいカードを返すことができるような意味のある関数をたくさん作成できます。

あなたのコードはDRYまたはWETですか

カードゲームの知識を失礼します

手順があります

  1. カードを取る
  2. カードが>=11ポイントかどうかを確認します
  3. ポイントと名前を印刷する

では、なぜ2枚目のカードでも同じことを繰り返すのでしょうか。あなたは同じことを二度書いた。最初のカードに1回、2番目のカードに次のカード。あなたは自分自身を繰り返しました。最良の方法は、繰り返しを関数に因数分解することです。あなたがする必要があるのは

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にはとにかくオートコンプリートが必要です。

80年代のスパゲッティベーシックを思い出させるa、cd2、x、yのような変数があります。私はカードゲームが大好きなので、アルゴについてはあまりコメントできませんが、コードについてはコメントできます。

幸いなことに、あなたはいくつかのコメントを入れました。

一貫性

選択関数を数回使用しますが、変数名は大きく異なります。

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

以降:

draw = rd.choice(cards)
cards.remove(draw)
add_number, full_name = 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)

3つのリストを連結しているので、結果のオブジェクトもリストオブジェクトになります。したがって、cards = cards1 + cards2 + cards3で十分であり、同じ結果が得られます。

カード1/2/3はまったく同じなので、まったく同じことを3回続けて繰り返します。これは明らかに間違っており、単純化することができます。あなたは単に書くことができます:

cards2 = cards1
cards3 = cards1

それはエレガントではありませんが、少なくとも繰り返しを避け、範囲が1回だけ宣言されます。

より良い方法:

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

したがって、シーケンスを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}"