2023-01-10 08:29:28 +01:00
|
|
|
# import itertools
|
|
|
|
import random
|
|
|
|
|
|
|
|
|
|
|
|
def shoe(n_of_decks: int = 1) -> list:
|
|
|
|
"""Create shuffled shoe of n decks of cards
|
|
|
|
|
|
|
|
Args:
|
|
|
|
n_of_decks (int, optional): number of decks. Defaults to 1.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list: shoe- shuffled decks of cards
|
|
|
|
"""
|
|
|
|
vals = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'jack', 'queen', 'king', 'ace']
|
|
|
|
suits = ['spades', 'clubs', 'hearts', 'diamonds']
|
|
|
|
|
|
|
|
deck = []
|
|
|
|
for _ in range(n_of_decks):
|
|
|
|
for _ in suits:
|
|
|
|
deck.extend(vals)
|
|
|
|
# deck.extend(itertools.product(vals, suits))
|
|
|
|
|
|
|
|
random.shuffle(deck)
|
|
|
|
return deck
|
|
|
|
|
2023-01-18 14:13:01 +01:00
|
|
|
def cards_eval(hand: list, agent: str) -> list:
|
2023-01-10 08:29:28 +01:00
|
|
|
"""Evaluate hand value. Will return two values if there is an ace
|
|
|
|
in the hand and both values are below 21.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
hand (list): list of cards
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list: returns a list of values of the hand
|
|
|
|
"""
|
|
|
|
evaluation = [0, 0]
|
|
|
|
for value in hand:
|
2023-01-18 14:13:01 +01:00
|
|
|
print(f"{agent} : {value}")
|
2023-01-10 08:29:28 +01:00
|
|
|
if value in ['jack', 'queen', 'king']:
|
|
|
|
evaluation[0] += 10
|
|
|
|
evaluation[1] += 10
|
|
|
|
elif value == 'ace':
|
|
|
|
evaluation[0] += 1
|
|
|
|
evaluation[1] += 11
|
|
|
|
else:
|
|
|
|
evaluation[0] += int(value)
|
|
|
|
evaluation[1] += int(value)
|
|
|
|
if evaluation[0] == evaluation[1]:
|
|
|
|
return [evaluation[0]]
|
|
|
|
elif evaluation[1] > 21:
|
|
|
|
return [evaluation[0]]
|
|
|
|
else:
|
|
|
|
return evaluation # both values returned
|
|
|
|
|
|
|
|
def dealer(hand: list, shoe: iter) -> int:
|
|
|
|
"""Dealer hand resolution
|
|
|
|
|
|
|
|
Args:
|
|
|
|
hand (list): dealer hand
|
|
|
|
shoe (iter): iterator of shoe
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int: dealer hand value
|
|
|
|
"""
|
2023-01-18 14:13:01 +01:00
|
|
|
evaluation = cards_eval(hand, "dealer")
|
2023-01-10 12:15:53 +01:00
|
|
|
while max(evaluation) <= 17: #solve soft 17
|
2023-01-10 08:29:28 +01:00
|
|
|
hand.append(next(shoe))
|
2023-01-18 14:13:01 +01:00
|
|
|
evaluation = cards_eval(hand, "dealer")
|
2023-01-10 08:29:28 +01:00
|
|
|
return max(evaluation)
|
|
|
|
|
|
|
|
def AI(hand: list, face_up: str) -> str:
|
|
|
|
#TODO: add fuzzy logic
|
|
|
|
"""Fuzzy AI
|
|
|
|
possible player decision:
|
|
|
|
'hit', 'double down', 'split', 'surrender', 'stand'
|
|
|
|
Args:
|
|
|
|
hand (list): player hand
|
|
|
|
face_up (str): dealer face up card
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
list: player decision
|
|
|
|
"""
|
|
|
|
### temp
|
|
|
|
# if face_up == 'ace':
|
|
|
|
# return 'surrender'
|
|
|
|
# else:
|
2023-01-18 14:13:01 +01:00
|
|
|
evaluation = cards_eval(hand, "player")
|
2023-01-10 12:15:53 +01:00
|
|
|
if max(evaluation) <= 17:
|
2023-01-10 08:29:28 +01:00
|
|
|
return 'hit'
|
|
|
|
else:
|
|
|
|
return 'stand'
|
|
|
|
### temp
|
|
|
|
|
|
|
|
def blackjack(shoe: iter, dealer_hand: list=[], player_hand: list =[]) -> str:
|
|
|
|
"""Single blackjack round
|
|
|
|
|
|
|
|
Args:
|
|
|
|
shoe (iter): shoe iterator
|
|
|
|
dealer_hand (list, optional): dealer hand. Should be non empty only in SPLIT.
|
|
|
|
player_hand (list, optional): player hand. Should be non empty only in SPLIT.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: game result
|
|
|
|
"""
|
|
|
|
if dealer_hand == [] and player_hand == []:
|
|
|
|
dealer_hand = [next(shoe), next(shoe)]
|
|
|
|
player_hand = [next(shoe), next(shoe)]
|
|
|
|
face_up = dealer_hand[0]
|
|
|
|
|
|
|
|
decision = ''
|
|
|
|
while decision != 'stand' or decision != 'surrender':
|
|
|
|
decision = AI(player_hand, face_up)
|
2023-01-18 14:13:01 +01:00
|
|
|
print(f"Decision: {decision}")
|
2023-01-10 08:29:28 +01:00
|
|
|
if decision == 'hit':
|
|
|
|
player_hand.append(next(shoe))
|
|
|
|
elif decision == 'double down':
|
|
|
|
player_hand.append(next(shoe))
|
|
|
|
elif decision == 'split':
|
2023-01-10 12:15:53 +01:00
|
|
|
#this wont work!
|
|
|
|
#it will need to
|
|
|
|
# dealer_value = dealer(dealer_hand, shoe)
|
|
|
|
#be calculated before and passed to blackjack()
|
|
|
|
#so both wager have the same dealer_value!
|
|
|
|
##this will work reccursevly
|
2023-01-10 08:29:28 +01:00
|
|
|
player_hand = player_hand[0]
|
|
|
|
blackjack(shoe, dealer_hand, player_hand)#new wager start
|
|
|
|
#old wager continue
|
|
|
|
elif decision == 'surrender':
|
|
|
|
break
|
|
|
|
elif decision == 'stand':
|
|
|
|
break
|
|
|
|
|
|
|
|
#dealer turn
|
|
|
|
dealer_value = dealer(dealer_hand, shoe)
|
2023-01-18 14:13:01 +01:00
|
|
|
player_value = max(cards_eval(player_hand, "player"))
|
2023-01-10 08:29:28 +01:00
|
|
|
# print(dealer_value, player_value) #debug
|
|
|
|
|
|
|
|
#round end
|
2023-01-18 14:13:01 +01:00
|
|
|
print(f"Dealer's deck value: {dealer_value}")
|
|
|
|
print(f"Player's deck value: {player_value}")
|
2023-01-10 12:15:53 +01:00
|
|
|
if player_value > 21:
|
|
|
|
return 'dealer win'
|
|
|
|
elif dealer_value > 21:
|
2023-01-10 08:29:28 +01:00
|
|
|
return 'player win'
|
|
|
|
elif player_value > dealer_value:
|
|
|
|
return 'player win'
|
|
|
|
elif player_value == dealer_value:
|
|
|
|
return 'push' #keep money, no win no lose 0$
|
|
|
|
else:
|
|
|
|
return 'dealer win'
|
|
|
|
#TODO: add adidtional return with wager value like +10$ or -20$
|
|
|
|
|
|
|
|
def game_loop() -> None:
|
2023-01-10 12:15:53 +01:00
|
|
|
stats = [0, 0] #wins, loses
|
2023-01-10 08:29:28 +01:00
|
|
|
shoe_iter = iter(shoe(10))
|
|
|
|
while True:
|
|
|
|
#round start
|
|
|
|
try:
|
|
|
|
result = blackjack(shoe_iter)
|
|
|
|
except StopIteration:
|
|
|
|
break
|
|
|
|
if result == 'player win':
|
2023-01-10 12:15:53 +01:00
|
|
|
stats[0] += 1
|
2023-01-10 08:29:28 +01:00
|
|
|
elif result == 'dealer win':
|
2023-01-10 12:15:53 +01:00
|
|
|
stats[1] += 1
|
|
|
|
# elif result == 'push':
|
|
|
|
# stats[0] += 0
|
|
|
|
# stats[1] += 1
|
2023-01-18 14:13:01 +01:00
|
|
|
if result in ['player win', 'dealer win']:
|
|
|
|
print(result)
|
|
|
|
print("===="*10)
|
2023-01-10 08:29:28 +01:00
|
|
|
return stats
|
|
|
|
|
|
|
|
# if __name__ == '__main__':
|
2023-01-10 12:15:53 +01:00
|
|
|
# print(game_loop())
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
statistics = [0, 0]
|
|
|
|
import time
|
|
|
|
#don't use time.time() for counting code execution time!
|
|
|
|
start = time.perf_counter()
|
2023-01-18 14:13:01 +01:00
|
|
|
for i in range(100):
|
2023-01-10 12:15:53 +01:00
|
|
|
wins, loses = game_loop()
|
|
|
|
statistics[0] += wins
|
|
|
|
statistics[1] += loses
|
|
|
|
|
|
|
|
end = time.perf_counter()
|
|
|
|
result = end - start
|
|
|
|
print(result)
|
|
|
|
print(statistics)
|
2023-01-18 14:13:01 +01:00
|
|
|
print(statistics[0]/sum(statistics))
|