首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >面向对象Blackjack

面向对象Blackjack
EN

Code Review用户
提问于 2022-10-03 20:56:27
回答 3查看 213关注 0票数 6

编辑:添加了V2链接

这里是github存储库,如果这里允许的话,因为接收建议似乎很容易。

BlackJack

我正在寻找批评,因为我所学到的一切都是自学成才,主要是以这种方式。自学,实践,建立,张贴,完善,继续前进。

第一段代码是继承的代码。

代码语言:javascript
复制
import time, itertools, random



def countyDots():
    for i in range(5):
            line = "." * i
            print(line, end="")
            print("\r", end="")
            time.sleep(0.5)



class Card(object):

    def __init__(self, rank, suit):

        self.rank = rank
        self.suit = suit

        self.valueChart = {
            "2": 2,
            "3": 3,
            "4": 4,
            "5": 5,
            "6": 6,
            "7": 7,
            "8": 8,
            "9": 9,
            "10": 10,
            "J": 10,
            "Q": 10,
            "K": 10,
            "A": 11,
        }


    def getValues(self):

        cardValues = self.valueChart
        returnedValue = 0

        for rank, value in cardValues.items():

            if self.rank == rank:
                returnedValue = value

        return returnedValue  
        



class Player(object):

    def __init__(self):
        self.name = self.defineName()

        #initialize player bank to $200
        self.balance = 200
        print("Beginning Balance: $200.00 USD\n")

        ##initialize player hand
        self.hand = []
        

        #initialize score and currentBet to 0
        self.score = 0


        self.bet = 0


    def placeBet(self):
        
        currentBet = input(f"BALANCE: ${self.balance}\nHow much would you like to bet? (1, 2, 5, 10, 25):  ")
        
        def is_number(n):
            try:
                int(n)# Type-casting the string to `float`.
                        # If string is not a valid `float`, 
                        # it'll raise `ValueError` exception
            except ValueError:
                return False
            return True

        if is_number(currentBet):
            currentBet = int(currentBet)
            if currentBet in (1, 2, 5, 10, 25):
                self.balance -= currentBet
                self.bet = currentBet
                print(f"\nCurrent Bet: {currentBet}\nBALANCE: {self.balance}\n")
            else:
                self.placeBet()
        else:
            self.placeBet()


        
    def defineName(self):

        #Ask for name from user
        newName = input("What is your Name?:  ")

        #confirm new name with user
        confirmation = input(f"Your name is {newName}, correct? (Y/N):   ")
        
        #convert confirmation to all lowercase and eliminate whitespace
        confirmation = confirmation.lower().strip()

        while confirmation != "y":

            newName = input("Sorry. What is your Name?:  ")
            countyDots()
            confirmation = input(f"So you prefer to be called {newName}?  (Y/N):   ")
            confirmation = confirmation.lower().strip()

        if confirmation == "y":
            return newName
            

    def showHand(self):

        print(f"{self.name}'s HAND")

        for card in self.hand:
            rank = card[0]
            suit = card[1]
            print(f"{rank} of {suit}")



    def getAction(self):

        action = input("Would you like to HIT or STAND?:  ")

        action = action.lower().strip()

        return action


    def calculateScore(self):

        self.score = 0

        for card in self.hand:
            rank = card[0]
            suit = card[1]
            card = Card(rank, suit)
            

            value = card.getValues()
            self.score += value
        
        print(f"{self.name}'s SCORE: {self.score}\n")
        return self.score

    def showBalance(self):

        print(f"BALANCE: {self.balance}")






class Dealer(object):

    def __init__(self):
        self.name = "Dealer"

        #initialize a dealer score to 0
        self.score = 0

        #initialize a dealer hand
        self.hand = []

        #initialize a dealer bank of 1,000,000
        self.bank = 1000000


    def showHand(self):

        print(f"{self.name}'s HAND")

        for card in self.hand:
            rank = card[0]
            suit = card[1]
            print(f"{rank} of {suit}")

        


    def calculateScore(self):

        self.score = 0

        for card in self.hand:
            rank = card[0]
            suit = card[1]
            card = Card(rank, suit)
            

            value = card.getValues()
            self.score += value
        
        print(f"{self.name}'s SCORE: {self.score}\n")
        return self.score

    






class Deck(object):


    def __init__(self):

        self.deck = []
        self.ranks = (
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "10",
            "J",
            "Q",
            "K",
            "A",
        )



    def buildDeck(self):

        suits = ("Spades ♠", "Clubs ♣", "Hearts ♥", "Diamonds ♦")
        cards = list(itertools.product(self.ranks, suits))
        random.shuffle(cards)

        for card in cards:
            self.deck.append(card)

        return self.deck


class Shoe(Deck):

    def __init__(self):

        self.shoe = []

    
    def buildShoe(self):

        for i in range(5):


            newDeck = Deck()
            newDeck.buildDeck()
            for card in newDeck.deck:
                self.shoe.append(card)

    
    def dealCard(self):

        gameDeck = self.shoe
        dealtCard = self.shoe[0]
        self.shoe.pop(0)

        return dealtCard

第二位代码是在底部执行的gameLogic文件。

代码语言:javascript
复制
class GameLogic(object):

    def __init__(self, player, dealer, shoe):

        self.player = player
        self.dealer = dealer
        self.gameShoe = shoe

    
    def beginGame(self):

        self.dealer.hand = []
        self.player.hand = []

        #initalize the players first bet
        self.player.placeBet()

        #initialize the shoe of cards (5 decks of cards)
        self.gameShoe.buildShoe()
        

        self.player.hand.append(self.gameShoe.dealCard())
        self.dealer.hand.append(self.gameShoe.dealCard())
        self.player.hand.append(self.gameShoe.dealCard())
        self.dealer.hand.append(self.gameShoe.dealCard())

        self.player.showHand()
        self.player.calculateScore()
        countyDots()
        self.dealer.showHand()              #These need to be changed so that they reflect one face up card
        self.dealer.calculateScore()        #and one face down card
        countyDots()

        self.playerAction(self.player.getAction())

    def keepPlaying(self):

        userResponse = input("\nPRESS ENTER FOR NEXT HAND\nType 'EXIT' to quit game\n")
        userResponse = userResponse.lower().strip()

        if userResponse == "":
            self.beginGame()

        elif userResponse == "exit":
            exit()

        else:
            self.keepPlaying()
    
    def playerAction(self, action):

        

        if action == "hit":
            print("You chose to Hit")

            #player takes on an additional card
            self.player.hand.append(self.gameShoe.dealCard())
            self.player.showHand()
            self.player.calculateScore()


            if self.player.score <= 21:
                newAction = self.player.getAction()
                self.playerAction(newAction)

            elif self.player.score > 21:
                print("BUSTED!!! YOU LOSE")
                #Proceed to 
                self.dealer.showHand()

                #set currentbet to 0
                self.player.bet = 0

                self.keepPlaying()
                    #show dealers hand for good faith
                    #set player bet to 0
                    #set forth with "would you like to play another round"

            #if player total score isnt higher than 21, we ask the same question
                #if the player total score is higher than 21, we end the game immediately and proceed to ask endgame()

        elif action == "stand":
            print("You chose to STAND\n")

            #if action is to stand then we need to proceed forward with the game logic for the dealer.
            self.dealer.showHand()
            self.dealer.calculateScore()

            while self.dealer.score < 16:
                countyDots()
                self.dealer.hand.append(self.gameShoe.dealCard())
                self.dealer.showHand()
                self.dealer.calculateScore()
                


            if self.dealer.score > 21:
                print("The dealer has busted!")
                
                #pay the player the player 2times their bet
                payout = self.player.bet * 2
                self.player.balance += payout
                print(f"{self.player.name} WON {self.player.bet}")
                self.player.showBalance()

                #reset bet to 0 for the next round
                self.player.bet = 0
                self.keepPlaying()

            
            elif self.dealer.score <= 21 and self.dealer.score >= 16:
                
                if self.player.score > self.dealer.score:
                    print("The player WINS!")
                    #pay the player the player 2times their bet
                    payout = self.player.bet * 2
                    self.player.balance += payout
                    print(f"{self.player.name} WON {self.player.bet}")
                    self.player.showBalance()

                    #reset bet to 0 for the next round
                    self.player.bet = 0
                    self.keepPlaying()


                elif self.player.score <= self.dealer.score:
                    print("The PLAYER LOSES!")
                    print(f"{self.player.name} LOST {self.player.bet}")
                    self.player.showBalance()

                    #reset bet to 0 for the next round
                    self.player.bet = 0
                    self.keepPlaying()    

        else:
            print("Some Real Bad shit happened herrrrrr")


    















#initialize a dealer
dealer1 = Dealer()

#initialize a player
player1 = Player()

#initialize a game shoe
gameShoe = Shoe()

#Initialize GamePlay
game = GameLogic(player1, dealer1, gameShoe)

#Begin Game Play
game.beginGame()
EN

回答 3

Code Review用户

发布于 2022-10-04 18:28:24

Python 3类

class Card(object):被废弃为Python2语法。使用Python3,(object)基类是隐含的,只是不必要的混乱。写class Card:就行了。

-真理之源--

每个Card都有一个字典,将卡级转换为一个值。一副牌里有52张牌,你就会有52个相同的字典。鞋里有5层甲板,你会有260本相同的字典!

此外,每个Deck都有一个ranks元组。5层产生5个相同的元组的牌号。

(另外,一个Shoe继承了Deck (后来更多),所以更多的副本??)

您应该在您的程序中,在一个全局结构中指定一次卡级。在这方面,Enum是一个很好的选择:

代码语言:javascript
复制
from enum import Enum

class Rank(Enum):
    TWO = ('2', 2)
    THREE = ('3', 3)
    FOUR = ('4', 4)
    FIVE = ('5', 5)
    SIX = ('6', 6)
    SEVEN = ('7', 7)
    EIGHT = ('8', 8)
    NINE = ('9', 9)
    TEN = ('10', 10)
    JACK = ('J', 10)
    QUEEN = ('Q', 10)
    KING = ('K', 10)
    ACE = ('A', 11)

    @property
    def symbol(self):
        return self.value[0]

    @property
    def points(self):
        return self.value[1]

同样,您也可以使用Enum来制作西装:

代码语言:javascript
复制
class Suit(Enum):
    CLUBS = ('Clubs', '♣')
    DIAMONDS = ('Diamonds', '♦')
    HEART = ('Hearts', '♥')
    SPADES = ('Spades', '♠')

    @property
    def suit_name(self):
        return self.value[0]

    @property
    def symbol(self):
        return self.value[1]

一张卡片是一对价值:等级和套装。但更重要的是,它有一个字符串来描述它,比如"Queen of Clubs ♣"。您可以在两个不同的位置使用f"{rank} of {suit}"将一个卡片(实际上是一个元组)转换为一个合适的字符串。因为您有一个Card类,所以它应该能够将自己转换成一个合适的字符串。

一张牌有一个点值,这个值(在21点的情况下)完全由牌的等级决定。但是我们不应该要求卡片的等级,然后把它转换成一个点值,相反,我们应该只要求卡片的点值,让它从它的排名和适应来确定它的价值。

牌是不可变的。如果你有一个Card,你不应该能够把它换成另一套衣服,或者用一个像card.rank = 'A'这样的语句来排名。我们可以使用dataclass使我们的Card对象抵抗恶意更改:

代码语言:javascript
复制
from dataclasses import dataclass

...

@dataclass(frozen=True)
class Card:
    rank: Rank
    suit: Suit

    def point_value(self):
        return self.rank.points

    def __str__(self):
        return f"{self.rank.symbol} of {self.suit.suit_name} {self.suit.symbol}"
代码语言:javascript
复制
>>> card = Card(Rank.ACE, Suit.SPADES)
>>> print(card)
A of Spades ♠
>>> print(card.point_value())
11

甲板

代码语言:javascript
复制
class Deck(object):
    def __init__(self):
        self.deck = []

真奇怪。如果我创建了一副牌,deck = Deck(),它没有任何卡在里面!

一副牌的构造函数实际上应该创建一副牌:

代码语言:javascript
复制
class Deck(object):
    def __init__(self):
        self.deck = [Card(rank, suit) for rank, suit in itertools.product(Rank, Suit)]

请注意,上面实际上在这里创建了一个Card对象列表,而不仅仅是秩和适合值的元组。

您的Deck类的功能似乎相当有限。你不能把牌退到牌上,也不能把剩下的牌洗牌,以此类推。

Shoe

代码语言:javascript
复制
class Shoe(Deck):
    def __init__(self):
        self.shoe = []

正如所写的,Shoe是一个Deck,但是没有Deck的任何功能。虽然它是从Deck继承的,但由于super().__init__()从未被调用,这种继承就中断了。Show().buildDeck()将给一个AttributeError

Shoe不是Deck;不要继承它。

该鞋是由5个甲板,顺序!想象一下,你有5个甲板,有红色、黄色、绿色、蓝色和橙色的背。你把每一个甲板都单独地洗洗,然后把它们一个堆放在鞋的另一个上面,而不把它们放在一起。这样就不可能(而不是不太可能)从鞋中依次画出三个"Ace of Spaces♠“。

你应该把5层的卡片加到鞋里,然后再把鞋子洗牌。

票数 2
EN

Code Review用户

发布于 2022-10-04 18:57:30

不需要递归

有几个方法包含不必要的递归,即这些方法包含对自己的调用。我注意到了(在Player.placeBet()GameLogic.keepPlaying()GameLogic.playerAction()中),但可能还有更多。这是非常危险的-它基本上意味着,如果你玩足够长的时间,你的游戏将耗尽内存,并将崩溃与一个RecursionError

这意味着您必须修改这些部分,可能需要使用适当的while循环。下面是placeBet()的一个修订版--注意while True循环的使用,即一个非常流行的Python成语。对于其他有问题的方法,您也可以采用。

代码语言:javascript
复制
    def place_bet(self):
        while True:
            response = input(f"BALANCE: ${self.balance}\n"
                             "How much would you like to bet? (1, 2, 5, 10, 25):  ")
            try:
                bet = int(response)
            except ValueError:
                print("Please enter a number!")
            else:
                if bet not in (1, 2, 5, 10, 25):
                    print("Only bets of 1, 2, 5, 10, or 25 are possible.")
                else:
                    self.balance -= bet
                    self.bet = bet
                    print(f"\nCurrent Bet: {currentBet}\nBALANCE: {self.balance}\n")                        
                    break

太多局部变量

许多方法包含不必要的局部变量。例如,Card.getValues()创建cardValues (valueChart的副本)以及变量returnedValues。您可以通过创建另外两个循环变量rankvalue来遍历D18,这两个变量也是不需要的:基本上,这里所需要的是:

代码语言:javascript
复制
def getValues(self):
    return self.valueChart[self.rank]

考虑到这一点,您可能首先考虑去掉getValues(),因为它可以被简单的字典查找所取代。

过载法

GameLogic.playerAction()被压倒的方法- it做的太多了,比你想象的要多得多。它不仅处理选定的行动,它似乎也负责经销商的反应,例如,当玩家输,它负责现金,并为下一轮重置。你真的需要把这里的逻辑分解成更小的部分。

错误、遗漏或危险的游戏行为

您的代码当前的行为有时有些出乎意料。我注意到的事情:

  • 有可能比你现在的赌注更多
  • 如果你的第一只手是一个21点,你不应该赢吗?
  • 当涉及到输入错误(例如hir而不是hit)时,您的游戏是非常不可原谅的。像这样的错误应该被另一个while循环捕获。
票数 2
EN

Code Review用户

发布于 2022-10-12 04:00:28

其他人说了很多话,我觉得可能没有必要重复。关于你提出的问题,我还有另一个看法:

代码语言:javascript
复制
import random
import time

class Card:

    cards_values = {
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "10": 10,
        "J": 10,
        "Q": 10,
        "K": 10,
        "A": 11,
    }

    allowed_suits = {
        "Spades ♠", 
        "Clubs ♣", 
        "Hearts ♥", 
        "Diamonds ♦"
    }

    def __init__(self, rank, suit):
        self.rank = rank
        self.suit = suit

    @property
    def value(self):
        return self.cards_values[self.rank]

class Participant:

    def __init__(self, name, balance):
        self.name = name
        self.balance = balance
        self.clear_hand()
    
    def receive_card(self, card):
        self.hand.append(card)
        self.score += card.value

    def clear_hand(self):
        self.hand = []
        self.score = 0

    def wins(self, amount):
        self.balance += amount

    def loses(self, amount):
        self.balance -= min(amount, self.balance)
    
    @staticmethod
    def ask_user_for_choice(question, choices, please_give_good_answer):
        not_chosen = True
        while not_chosen:
            answer = input(question).upper().strip()
            if not_chosen := (answer not in {x.upper().strip() for x in choices}):
                print(please_give_good_answer)
        return answer   

    def show_hand_and_score(self):
        # Show dots
        self.show_dots()

        hand = "\n".join(
            (
                f"{self.name}'s hand:",
                '\n'.join(
                    f"{current_card.rank} of {current_card.suit}" 
                    for current_card in self.hand
                )
            )
        )
        print(hand)
        print(f"{self.name}'s score: {self.score}")

    @staticmethod
    def show_dots():
        for nb_dots in range(5):
            print("." * nb_dots, "\r", end='')
            time.sleep(0.5)
        print()

    def show_balance(self):
        print(f"{self.name}'s BALANCE: {self.balance}$")

    def has_busted(self):
        if has_busted := self.score > 21:
            print(f"{self.name} has busted!")
 
        return has_busted

class Player(Participant):

    def ask_bet(self):
        self.show_balance()
        current_bet = int(
            self.ask_user_for_choice(
                f"How much would you like to bet? (1, 2, 5, 10, 25): ",
                ("1", "2", "5", "10", "25"),
                "Please only choose your bet as one of these choices: 1, 2, 5, 10 or 25"
            )
        )

        if current_bet > self.balance:
            raise ValueError(f'Balance (${self.balance})is not big enough for bet (${current_bet})')
        
        return current_bet

    def ask_if_hit_is_next_move(self):
        player_action = self.ask_user_for_choice(
            "Would you like to HIT or STAND? ",
            ('HIT', 'STAND'),
            'Please only answer "HIT" or "STAND"'
        )
        return player_action == 'HIT'
    
    def has_busted_after_hitting(self, card_received):
        self.receive_card(card_received)
        self.show_hand_and_score()
         
        return self.has_busted ()

    def has_won(self, dealers_score):
        return self.score > dealers_score

    def wins(self, current_bet):
        super().wins(current_bet)
        print(f"{self.name} WON {current_bet}$")

    def loses(self, current_bet):
        super().loses(current_bet)
        print(f"{self.name} LOST {current_bet}$")

    def ask_if_we_continue(self):
        player_answer = self.ask_user_for_choice(
            "\nPRESS ENTER FOR NEXT HAND\nType 'EXIT' to quit game\n",
            ('', 'EXIT'),
           'Please only press enter for next hand or type "EXIT" to quit the game'
        )
        return player_answer == 'EXIT'

class Dealer(Participant):
    
    def __init__(self, name, balance, nb_decks):
        super().__init__(name, balance)
        self.nb_decks = nb_decks
        self.shoe = None

    def deal_card(self):
        if not self.shoe:
            self.create_shoe()

        return next(self.shoe)

    def create_shoe(self):

        def create_deck():

            cards = [
                Card(current_rank, current_suit) 
                for current_rank in Card.cards_values 
                for current_suit in Card.allowed_suits
            ]
            random.shuffle(cards)

            return cards

        self.shoe = (
            current_card 
            for _ in range(self.nb_decks)
            for current_card in create_deck()
        )

    def has_busted_after_getting_cards(self):
        self.show_hand_and_score()

        while self.score < 16:
            self.receive_card(self.deal_card())
            self.show_hand_and_score()

        return self.has_busted()

    def has_enough_funds_for_bet(self, current_bet):
        return self.balance >= current_bet

    def already_won(self):
        return self.score == 21

    def show_hand_and_score(self, hidden=False):
        if hidden:
            self.show_dots()
            hand  = "\n".join(
                (
                    f"{self.name}'s hand:",
                    f"{self.hand[0].rank} of {self.hand[0].suit}", 
                    "(hidden card)"
                )
            )
            print(hand)
        else:
            super().show_hand_and_score()

class Blackjack_table:

    def __init__(self, bank=1000000, nb_decks=5, default_player_starting_bablance=200):

        name_not_confirmed = True
        while name_not_confirmed:
            player_name = input("What name do you want to use? ")
            name_not_confirmed = Player.ask_user_for_choice(
                f"Is your name {player_name} (Y/N)? ",
                'YN',
                'Please answer only "Y" or "N"'
            ) == 'N'

        self.player = Player(player_name, default_player_starting_bablance)
        self.dealer = Dealer("Dealer", bank, nb_decks)

    def play_rounds(self):

        finished_playing = False
        while not finished_playing:

            if self.player.balance <= 0:
                print("You don't have any funds left")
                break

            if self.dealer.balance <= 0:
                print("The bank has lost all their funds.")
                break

            self.player.clear_hand()
            self.dealer.clear_hand()

            try:
                self.current_bet = self.player.ask_bet()
            except ValueError as bet_error:
                print(bet_error.args[0])
                continue

            if not self.dealer.has_enough_funds_for_bet(self.current_bet):
                print("Sorry, the dealer doesn't have enough funds for this bet")
                self.dealer.show_balance()
                finished_playing = self.player.ask_if_we_continue()
                continue
            
            for _ in range(2):
                self.player.receive_card(self.dealer.deal_card())
                self.dealer.receive_card(self.dealer.deal_card())
                
            self.player.show_hand_and_score()
            self.dealer.show_hand_and_score(hidden=True)
            
            if self.player.has_busted():
                self.dealer_wins()
                finished_playing = self.player.ask_if_we_continue()
                continue
            if self.dealer.has_busted():
                self.player_wins()
                finished_playing = self.player.ask_if_we_continue()
                continue

            round_is_not_finished = True
            while round_is_not_finished:
                if self.player.ask_if_hit_is_next_move():
                    if self.player.has_busted_after_hitting(self.dealer.deal_card()):
                        self.dealer.show_hand_and_score()
                        self.dealer_wins()
                        round_is_not_finished = False
                    # round_is_not_finished stays True otherwise
                else: # player is standing
                    if self.dealer.has_busted_after_getting_cards():
                        self.player_wins()
                    else:
                        if self.player.has_won(self.dealer.score):
                            self.player_wins()
                        else:
                            self.dealer_wins()
                    round_is_not_finished = False
                    
            finished_playing = self.player.ask_if_we_continue()

        print("Gave over. Hope to see you sson!")

    def player_wins(self):
        print("The player WINS!")
        self.player.wins(self.current_bet)
        self.dealer.loses(self.current_bet)
        self.player.show_balance()
        self.dealer.show_balance()

    def dealer_wins(self):
        print("The player LOSES!")
        self.dealer.wins(self.current_bet)
        self.player.loses(self.current_bet)
        self.player.show_balance()
        self.dealer.show_balance()

if __name__ == '__main__':
    table = Blackjack_table(100)
    table.play_rounds()

除了其他人已经说过的话之外,我还要补充说,你们应该研究:

在大多数情况下,我试图:

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/280178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档