I tried to implement blackjack of card game in Python

Introduction

Last time, I created a deck function of playing cards as shown in here.

Using the previous deck function, this time I implemented the main game function of blackjack. How would you actually write a program and extend what you said from existing requirements? I think I learned a lot by thinking about it!

Functional requirements

For the basic rules and requirements, refer to the one in Graduation exam from programming beginners should develop "Blackjack". Thank you!

--The initial card is 52. Make sure there are no duplicate cards when drawing --A two-player match between the player and the dealer. Players run, dealers run automatically --At the start of execution, the player and the dealer each draw two cards. The drawn card is displayed on the screen. However, ** Don't know the dealer's second card ** --After that, the player draws the card first. Burst if the number of players exceeds 21, the game ends at that point --Players can choose to draw the next card each time they draw a card --Once the player has finished drawing, the dealer will continue to draw until his or her hand is 17 or higher. --A game when the player and the dealer finish drawing. The one closer to 21 wins --J, Q and K are treated as 10 -** A is treated only as "1" for the time being. ** Do not set to "11" --No double down, no split, no surrender, no other special rules

This time, in addition to these requirements, I added the following functions.

Implementation

The following classes are prepared and each file is separated. The Card class and Deck class do not include blackjack-specific specifications so that they can be used in other card games.

Card class / Deck class

I used the one created in this article as it is.

Player class / Dealer class

from bj import BlackJack


class Player:
    """
Child (player that can be operated manually)
    """

    def __init__(self):
        self.win_count = 0
        self.hands = []
        self.card_current_score = 0
        self.card_current_score_sub = 0
        self.has_A_card = False

    def keep_drawing_card(self, deck):
        """
Let the player decide hit or stand
(The player's turn ends at stand)

        Parameters
        ----------
        deck : deck
A set of cards
        """
        want_to_draw = True
        while want_to_draw:
            hit_or_stand_msg = "\nHit(1) or Stand(2) : "
            hit_or_stand_res = input(hit_or_stand_msg)
            if hit_or_stand_res == "1":
                #1 draw for hit
                self.draw_card(deck)
                print(f"player draw card is : {self.hands[-1]}")
                BlackJack.calc_current_score(self)
                sub_score = ""
                if self.has_A_card is True:
                    sub_score = \
                        f", {self.card_current_score_sub}"
                print(
                    f"players's total_score : \
{self.card_current_score}{sub_score}")

                #Forced termination of player turn with burst
                if BlackJack.is_score_bust(int(self.card_current_score)) and \
                    BlackJack.is_score_bust(
                        int(self.card_current_score_sub)):
                    print("player bust!!!")
                    want_to_draw = False

                if self.card_current_score == 21 or \
                        self.card_current_score_sub == 21:
                    #Forced termination when it reaches 21
                    want_to_draw = False

            elif hit_or_stand_res == "2":
                #In the case of stand, the turn ends
                want_to_draw = False
            else:
                # 1,Re-enter commands other than 2
                print("Is useless")

    def draw_card(self, deck, num=1):
        """
Draw a card from the deck and add it to your hand
* Even if different numbers are drawn, it is ok

        Parameters
        ----------
        num : int, default 1
Number of times to draw a card

        Examples
        --------
        >>> player.draw_card(2) #2 draws[♠︎-J, ♠︎-10]
        >>> player.draw_card(3) # [♦︎-9, ♣️-10, ♠︎-2]
        >>> print(player.hands)
        [♠︎-J, ♠︎-10, ♦︎-9, ♣️-10, ♠︎-2]
        """
        self.hands_store = deck.pick_card(num)
        self.hands.extend(self.hands_store)


class Dealer(Player):
    """
Parent (automatic operation)
    """

    def keep_drawing_card(self, deck):
        """
dealer keeps drawing cards automatically until it exceeds 17
End when 17 is exceeded

        Parameters
        ----------
        deck : object
Current hand
        """
        self.has_A_card = False
        while self.card_current_score < 17 or \
                self.card_current_score_sub < 17:
            self.draw_card(deck)
            print(f"dealer draw card is : {self.hands[-1]}")
            BlackJack.calc_current_score(self)
            sub_score = ""
            if self.has_A_card:
                sub_score = \
                    f", {self.card_current_score_sub}"
            print(
                f"dealer's total_score : {self.card_current_score}{sub_score}")
            if BlackJack.is_score_bust(self.card_current_score) and \
                    BlackJack.is_score_bust(
                    int(self.card_current_score_sub)):
                print("dealer bust!!!")

Game class

from deck import stock
import role
from bj import BlackJack


class Game:
    """
Main game (create player and dealer instance when creating instance)

    Examples
    --------
    >>> game = Game()
    >>> game.main() #Game start (displays the initial phase below)
    dealer's hands : [❤︎-7, *-*]

    player's hands : [♠︎-9, ♦︎-J]
    players's total_score : 19

    Hit(1) or Stand(2) :
    """

    def __init__(self):
        #Create player and dealer
        self.player = role.Player()
        self.dealer = role.Dealer()

    def get_nearest_score(self, score_list):
        """
Returns the number closest to 21 below 21 from the main and sub scores
Returns 0 if both exceed 21

        Parameters
        ----------
        score_list : list
List of main and sub scores

        Returns
        --------
        main_score : int
A number close to 21 of the two scores (0 if both are greater than 21)
        """
        main_score = 0
        for score in score_list:
            if score > 21:
                #Numbers greater than 21 burst
                continue
            elif main_score < score:
                main_score = score
        return main_score

    def judge_winner(self, player, dealer):
        """
Win / loss judgment

        Parameters
        ----------
        dealer : object
parent
        player : object
Child
        """

        # player,Get and compare the closer to 21 of 21 or less in each dealer's score
        player_score_list = [
            player.card_current_score,
            player.card_current_score_sub]
        player_score = self.get_nearest_score(player_score_list)

        dealer_score_list = [
            dealer.card_current_score,
            dealer.card_current_score_sub]
        dealer_score = self.get_nearest_score(dealer_score_list)

        judge_win = ""
        #Both bursts are draw
        if player_score == 0 and dealer_score == 0:
            judge_win = "---draw---"

        if dealer_score < \
                player_score <= 21:
            # dealer < player <=At 21, player wins
            judge_win = "player win!"
            player.win_count += 1
        elif player_score <= 21 \
                < dealer_score:
            #Player wins when player is 21 or less, dealer bursts
            judge_win = "player win!"
            player.win_count += 1
        elif player_score == dealer_score \
                and player_score <= 21:
            #Neither bursts, and if the numbers are the same, a draw
            judge_win = "---draw---"
        else:
            #Otherwise all players lose
            judge_win = "dealer win!"
            dealer.win_count += 1
        #Console display
        print(f"\n/***********/\n/{judge_win}/\n/***********/")

    def display_final_result(
            self,
            player_win_count,
            dealer_win_count,
            total_count):
        """
Win / loss judgment

        Parameters
        ----------
        player_win_count : int
Number of player wins
        dealer_win_count : int
Dealer wins
        total_count : int
Total number of games
        """
        #Calculate the number of draws by subtracting the number of player and dealer wins from the total number of games
        draw_count = total_count - player_win_count - dealer_win_count
        return f"""\
*-*-*-*-*-*-*-*
total:{total_count}
win:{player_win_count}
lose:{dealer_win_count}
draw:{draw_count}
*-*-*-*-*-*-*-*\
"""

    def main(self):
        """
Blackjack main game function
        """

        #Deck set (determine the number of sets)
        deck = stock.Deck()

        total_count = 0
        can_play_game = True
        #When there are 5 or more cards left
        while can_play_game and len(deck.cards) > 5:

            self.player.hands = []
            self.dealer.hands = []

            #Number of games+1
            total_count += 1

            #Draw two at first
            self.player.draw_card(deck, 2)
            self.dealer.draw_card(deck, 2)

            #player initial score calculation
            BlackJack.calc_current_score(self.player)
            #If A is subtracted, the sub score is also displayed.
            player_sub_score = BlackJack.check_draw_A(self.player)

            #Score display at the time of initial draw (one on the dealer side is turned down)
            print("\n--Game Start--\n")
            first_msg = f"""\
dealer's hands : [{self.dealer.hands[0]}, *-*]
player's hands : {self.player.hands}

players's total_score : {self.player.card_current_score}{player_sub_score}\
            """
            print(f"{first_msg}")

            #Let the player decide hit or stand (stand ends the player's turn)
            self.player.keep_drawing_card(deck)

            print("\n--Result--\n")

            #dealer score calculation
            BlackJack.calc_current_score(self.dealer)
            #If A is subtracted, the sub score is also displayed.
            dealer_sub_score = BlackJack.check_draw_A(self.dealer)
            dealer_msg = f"""\
dealer's hands : {self.dealer.hands}
dealer's total_score : {self.dealer.card_current_score}{dealer_sub_score}\
            """
            print(f"{dealer_msg}")

            #Draw until the dealer's hand totals 17
            self.dealer.keep_drawing_card(deck)

            #Win / loss judgment
            self.judge_winner(self.player, self.dealer)

            print("\n--Game End--\n")

            #Game restart
            restart_msg = "End the game with Q, start the game otherwise:"
            start_res = input(restart_msg)
            if start_res == 'Q':
                can_play_game = False

        #Calculate and display the number of games and the number of wins
        final_score_str = self.display_final_result(
            self.player.win_count, self.dealer.win_count, total_count)
        print(final_score_str)


if __name__ == '__main__':
    game = Game()
    game.main()

BlackJack class

class BlackJack:
    """
Blackjack rules
    """

    RANKS = (*"A23456789", "10", *"JQK")
    values = list(range(1, 11))  # 1〜10
    values.extend([10, 10, 10])  # JQK
    VALUES = (values)
    #Associate the display mark with the score
    # {'A': 1, '2': 2, '3': 3, '4': 4, '5': 5,
    #  '6': 6, '7': 7, '8': 8, '9': 9, '10': 10,
    #  'J': 10, 'Q': 10, 'K': 10}
    RANK_TO_VALUES = dict(zip(RANKS, VALUES))

    @classmethod
    def calc_current_score(cls, person):
        """
Calculate current score

        Parameters
        ----------
        person : object
Current hand

        Returns
        --------
        card_current_score : int
Current score
        """
        person.card_current_score = 0
        person.card_current_score_sub = 0
        person.has_A_card = False
        for card in person.hands:
            card_rank = str(card).split("-")[1]
            card_value = cls.RANK_TO_VALUES[card_rank]

            #Consider the time of A
            person.card_current_score += card_value
            person.card_current_score_sub += card_value
            if card_value == 1:
                if person.has_A_card:
                    #When A is continuous, the subscore is doubled and the subscore is added.+10(+11-1)
                    person.card_current_score_sub += 10
                    print(person.card_current_score_sub)
                    continue
                person.has_A_card = True
                person.card_current_score_sub += 11

    @classmethod
    def is_score_bust(cls, total_score):
        """
Calculate current score

        Parameters
        ----------
        current_hands : list
Current hand

        Returns
        --------
        True or False
True in burst
        """
        return total_score > 21

    @classmethod
    def check_draw_A(cls, person):
        """
If there is an A in your hand, the subscore is also displayed

        Parameters
        ----------
        person : object
            player or dealer

        Returns
        --------
        person_sub_score : str
Character string for subscore (empty string if there is no A in your hand)
        """
        person_sub_score = ""
        if person.has_A_card is True:
            person_sub_score = f", {person.card_current_score_sub}"
        return person_sub_score

Worried points

About score calculation of A (calc_current_score of Player class)

I was worried about this, but I prepared a subscore from the beginning, and when I subtracted A, the console display, When calculating the score, I implemented it as +1 for the main and +11 for the sub. I think there are other better ways. I was able to implement it fairly smoothly up to the point of calculating the score with A as 1, By making it a way to prepare subscores I think it was a bit of a failure that the processing of score calculation increased.

Multiple score victory judgment (Game class get_nearest_score, judge_winner)

This time we have 2 scores each for player and dealer In each other's list, the score closer to 21 without exceeding 21 was used as the judgment score.

If both scores are burst, the score will be 0, and if one is burst, the other will be the judgment score. → There is no player more than 17 (because there is a draw end at 16) → dealer automatically draws until both scores exceed 17

motion

When executed, it looks like this. Finally, the number of games and the number of player wins are displayed.

$ python main.py 

--Game Start--

dealer's hands : [❤︎-J, *-*]
player's hands : [♦︎-3, ♠︎-3]

players's total_score : 6            

Hit(1) or Stand(2) : 1
player draw card is : ♠︎-Q
players's total_score : 16

Hit(1) or Stand(2) : 1
player draw card is : ♠︎-5
players's total_score : 21

--Result--

dealer's hands : [❤︎-J, ♦︎-5]
dealer's total_score : 15            
dealer draw card is : ❤︎-Q
dealer's total_score : 25
dealer burst!!!

/***********/
/player win!/
/***********/

--Game End--

End the game with Q, start the game otherwise:

--Game Start--

dealer's hands : [♠︎-10, *-*]
player's hands : [♠︎-8, ♦︎-8]

players's total_score : 16            

Hit(1) or Stand(2) : 2

--Result--

dealer's hands : [♠︎-10, ♣️-A]
dealer's total_score : 11, 22            
dealer draw card is : ♣️-5
dealer's total_score : 16, 27
dealer draw card is : ♣️-Q
dealer's total_score : 26, 37
dealer burst!!!

/***********/
/player win!/
/***********/

--Game End--

End the game with Q, start the game otherwise:

--Game Start--

dealer's hands : [❤︎-K, *-*]
player's hands : [♦︎-A, ♠︎-7]

players's total_score : 8, 19            

Hit(1) or Stand(2) : 2

--Result--

dealer's hands : [❤︎-K, ♠︎-J]
dealer's total_score : 20            

/***********/
/dealer win!/
/***********/

--Game End--

End the game with Q, start the game otherwise: Q
*-*-*-*-*-*-*-*
total:3
win:2
lose:1
draw:0
*-*-*-*-*-*-*-*

end

Double down and split are future issues.

Recommended Posts

I tried to implement blackjack of card game in Python
I tried to implement a card game of playing cards in Python
I tried to implement PLSA in Python
I tried to implement permutation in Python
I tried to implement ADALINE in Python
I tried to implement PPO in Python
I tried to implement a misunderstood prisoner's dilemma game in Python
I tried to implement TOPIC MODEL in Python
I tried to implement selection sort in python
I tried to implement a pseudo pachislot in Python
I tried to implement Dragon Quest poker in Python
I tried to implement GA (genetic algorithm) in Python
I tried to implement a one-dimensional cellular automaton in Python
I tried to implement the mail sending function in Python
I tried to fix "I tried stochastic simulation of bingo game with Python"
I wrote a doctest in "I tried to simulate the probability of a bingo game with Python"
I tried playing a typing game in Python
I tried to implement Bayesian linear regression by Gibbs sampling in python
I tried to implement PCANet
I tried to implement StarGAN (1)
I tried to graph the packages installed in Python
I want to easily implement a timeout in python
I tried to implement Minesweeper on terminal with python
I tried to implement an artificial perceptron with python
I tried to summarize how to use pandas in python
[Python] I tried to get Json of squid ring 2
I tried to implement automatic proof of sequence calculation
I tried to summarize the string operations of Python
I tried to implement merge sort in Python with as few lines as possible
I tried to implement what seems to be a Windows snipping tool in Python
I tried to find the entropy of the image with python
I tried to implement Deep VQE
I tried the accuracy of three Stirling's approximations in python
I tried to create API list.csv in Python from swagger.yaml
I tried to touch Python (installation)
Implementation of life game in Python
I tried to implement adversarial validation
I tried "How to get a method decorated in Python"
[Python] I tried to visualize the follow relationship of Twitter
I tried to implement ListNet of rank learning with Chainer
I tried to implement hierarchical clustering
I tried to make a stopwatch using tkinter in python
I tried to implement Realness GAN
I tried Line notification in Python
I tried to create a Python script to get the value of a cell in Microsoft Excel
I tried to make a regular expression of "amount" using Python
[Python] I tried to implement stable sorting, so make a note
I tried to make a regular expression of "time" using Python
I tried to create a list of prime numbers with python
I wrote the code to write the code of Brainf * ck in python
Implement a deterministic finite automaton in Python to determine multiples of 3
I tried to make an analysis base of 5 patterns in 3 years
I tried to improve the efficiency of daily work with Python
I tried to automatically collect images of Kanna Hashimoto with Python! !!
I tried to summarize Python exception handling
I tried to implement Autoencoder with TensorFlow
Python3 standard input I tried to summarize
I tried using Bayesian Optimization in Python
I wanted to solve ABC159 in Python
I tried to implement CVAE with PyTorch
[Python] I tried to calculate TF-IDF steadily