# Blackjack Python: มาเล่นแบล็คแจ็คกันเถอะ (กับ Python)

2021-9-24     แหล่งกำเนิด：

You can find the code in its entirety on my GitHub here.

It probably would have been a good idea to use object oriented programming. But at this point, I am not yet used to writing code in that way. I will probably revise my code at some point in the future to be object oriented; but that is a project for another day.

To start, let’s get our input statements out of the way:

import numpy as np

import pandas as pd

import random

import matplotlib.pyplot as plt

import seaborn as sns

Now let’s make a few functions to help us out. First, we need a function that creates a new deck of cards for us to play with. The make_decks function does just that — it adds four of each card type (ace, 2, 3, 4, etc.) to the list new_deck, shuffles new_deck, and returns the newly created list (deck) for us to play with. Also note that we can specify via num_decks how many decks we want the function to create.

# Make a deck

def make_decks(num_decks, card_types):

new_deck=[]

for i in range(num_decks):

for j in range(4):

new_deck.extend(card_types)

random.shuffle(new_deck)

return new_deck

We also need a function that can add up the value of the cards in our hand. It is slightly more complicated than simple summation because aces can be worth either 1 or 11, depending on whichever is most advantageous to its holder. So our function first tallies up the value of each non-ace card in the hand (I represent all face cards with the number 10 as they are all functionally the same in blackjack). Then it counts up the number of aces. Finally, it determines how much each ace should be worth depending on the value of the rest of your cards.

Corrections: In my previous version of the code below, there was an error. To fix it, I added the helper function ace_values which takes as input the number of aces in your hand as an integer and outputs a list of unique values that your aces could be worth. Figuring out the permutations (and their sums) for a given number of aces was more work than I thought and I needed to write the following 2 helper functions to get it done (fore more details, please refer to my comments in the following code block):

# This function lists out all permutations of ace values in the

# array sum_array.

# For example, if you have 2 aces, there are 4 permutations:

# [[1,1], [1,11], [11,1], [11,11]]

# These permutations lead to 3 unique sums: [2, 12, 22]

# Of these 3, only 2 are <=21 so they are returned: [2, 12]

def get_ace_values(temp_list):

sum_array=np.zeros((2**len(temp_list), len(temp_list)))

# This loop gets the permutations

for i in range(len(temp_list)):

n=len(temp_list) – i

half_len=int(2**n * 0.5)

for rep in range(int(sum_array.shape/half_len/2)):

sum_array[rep*2**n : rep*2**n+half_len, i]=1

sum_array[rep*2**n+half_len : rep*2**n+half_len*2, i]=11

# Only return values that are valid (<=21)

return list(set([int(s) for s in np.sum(sum_array, axis=1)\

if s<=21]))# Convert num_aces, an int to a list of lists

# For example if num_aces=2, the output should be [[1,11],[1,11]]

# I require this format for the get_ace_values function

def ace_values(num_aces):

temp_list=[]

for i in range(num_aces):

temp_list.append([1,11])

return get_ace_values(temp_list)

The above two functions can now be used by the function total_up, which like I mentioned above, calculates the value of the cards in our hand (including the correct handling of any aces):

# Total up value of hand

def total_up(hand):

aces=0

total=0

for card in hand:

if card !=’A’:

total +=card

else:

aces +=1

# Call function ace_values to produce list of possible values

# for aces in hand

ace_value_list=ace_values(aces)

final_totals=[i+total for i in ace_value_list if i+total<=21]

if final_totals==[]:

return min(ace_value_list) + total

else:

return max(final_totals)

Now that our helper functions are out of the way, let’s move on to the main loop. First, I define my key variables:

stacks is the number of card stacks (where each card stack can be one or more decks) we will simulate.players is the number of players in each simulated game.num_decks is the number of decks in each stack.card_types is a list of all 13 card types.

stacks=50000

players=1

num_decks=1card_types=[‘A’,2,3,4,5,6,7,8,9,10,10,10,10]

Now begins the main loops of our simulator. There are two:

A for loop that iterates through the 50,000 stacks of cards that we want to simulate.A while loop, that for each stack of cards, plays blackjack until there are 20 or fewer cards in the stack. At that point it moves onto the next stack.

The numpy array, curr_player_results, is an important variable where I store the game result for each player — 1 for a win, 0 for a tie, and -1 for a loss. Each element in this array corresponds to one player at the blackjack table.

Within the while loop, we deal a card to each player and then the dealer (where the Python comment says “Deal FIRST card”), and then we do it again so that everyone has 2 cards. In order to deal the cards, I take advantage of the pop function with an input of 0 — this returns the first element of a list while simultaneously removing it from that list (perfect for dealing cards from a stack). When the number of cards left in the dealer’s blackjack python stack drops to 20 or below, a new stack of cards is used to replace the old one (moves to the next iteration of our for loop).

for stack in range(stacks):

blackjack=set([‘A’,10])

dealer_cards=make_decks(num_decks, card_types)

while len(dealer_cards) > 20:

curr_player_results=np.zeros((1,players))

dealer_hand=[]

player_hands=[[] for player in range(players)] # Deal FIRST card

for player, hand in enumerate(player_hands):

player_hands[player].append(dealer_cards.pop(0))

dealer_hand.append(dealer_cards.pop(0))

# Deal SECOND card

for player, hand in enumerate(player_hands):

player_hands[player].append(dealer_cards.pop(0))

dealer_hand.append(dealer_cards.pop(0))

Next the dealer blackjack python checks if he or she has a blackjack (an ace and a 10). Notice that in the previous code block, I defined blackjack as a set that includes an ace and a 10.

If the dealer has a blackjack, then the players lose (and get assigned a -1 in curr_player_results) unless they too have a blackjack (in which case it is a tie).

# Dealer checks for 21

if set(dealer_hand)==blackjack:

for player in range(players):

if set(player_hands[player]) !=blackjack:

curr_player_results[0,player]=-1

else:

curr_player_results[0,player]=0

If the dealer does not have a blackjack, then play continues. The players do their own blackjack checks — if they have one, they win (in some casinos blackjack pays 1.5 to 1, in other words if your bet was \$100 then you win \$150). I record a win by setting the element corresponding to that player in the array curr_player_results to 1.

For the players without blackjack, they now have the option to hit, stay, etc. For this simulation, my objective was to capture all kinds of player decisions — smart ones, lucky ones, and stupid ones. So I based the decision for the player on a coin flip (if random.random() generates a value higher than 0.5 the player hits, otherwise the player stays).

Deciding via coin flip might sound silly, but by making the hit/stay decision uncorrelated to what is actually happening in the game, we can observe all types of situations and ultimately generate a rich dataset for analysis.

I am not trying to figure out the optimal strategy right this instance. Rather, I want to use this simulator to generate training data, with which I can eventually train a neural network to play blackjack optimally (in a future post).

For Python, the \ character denotes line continuation and can be used to format extra long lines of code for better readability — you will see me use it in the following code block.

else:

for player in range(players):

# Players check for 21

if set(player_hands[player])==blackjack:

curr_player_results[0,player]=1

else:

# Hit randomly, check for busts

while (random.random() >=0.5) and \

(total_up(player_hands[player]) <=11):

player_hands[player].append]

(dealer_cards.pop(0))

if total_up(player_hands[player]) > 21:

curr_player_results[0,player]=-1

break

In the final section of our loop (almost there!), it’s the dealer’s turn. The dealer must hit until either he or she busts or has a hand that sums to at least 17. So the while loop deals cards to the dealer until 17 is reached, then we check if our dealer busted. If a dealer bust occurs, then every player that has not already lost (via busting) wins and we record a 1 for them in curr_player_results.

# Dealer hits based on the rules

while total_up(dealer_hand) < 17:

dealer_hand.append(dealer_cards.pop(0))

# Compare dealer hand to players hand

# but first check if dealer busted

if total_up(dealer_hand) > 21:

for player in range(players):

if curr_player_results[0,player] !=-1:

curr_player_results[0,player]=1

If the dealer does not bust, then each player compares his or her hand to the dealer’s — the higher hand wins.

else:

for player in range(players):

if total_up(player_hands[player]) > \

total_up(dealer_hand):

if total_up(player_hands[player]) <=21:

curr_player_results[0,player]=1

elif total_up(player_hands[player])==\

total_up(dealer_hand):

curr_player_results[0,player]=0

else:

curr_player_results[0,player]=-1

Finally, at the conclusion of each blackjack game, we append the game results along with other variables that we care about to the lists that we will use to track our overall simulation results:

# Track features

dealer_card_feature.append(dealer_hand)

player_card_feature.append(player_hands)

player_results.append(list(curr_player_results))

## ปฏิเสธความรับผิดชอบ

โป๊กเกอร์ ส ตา ร์ สเผยแพร่บทความนี้เพื่อวัตถุประสงค์ในการส่งข้อมูลเพิ่มเติมไม่ได้หมายความว่าเห็นด้วยหรือยืนยันรายละเอียดของ เนื้อหาของบทความนี้เป็นเพียงข้อมูลอ้างอิงไม่ถือเป็นข้อเสนอการลงทุน นักลงทุนดำเนินการตามความเสี่ยงของตัวเอง

ป้ายชื่อ： แนะนำอ่าน
บทความร้อน