import random from FuzzyControlSystem import getStartDecision, getSplitDecision, getHardHandDecision, getSoftHandDecision, getDecision 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 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10] suits = ['spades', 'clubs', 'hearts', 'diamonds'] deck = [] for _ in range(n_of_decks): for _ in suits: deck.extend(vals) random.shuffle(deck) return deck def cards_eval(hand: list) -> list: """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 = [sum(hand), sum(hand)] for value in hand: if value == 1: evaluation[1] += 10 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 """ evaluation = cards_eval(hand) while max(evaluation) <= 17: #solve soft 17 hand.append(next(shoe)) evaluation = cards_eval(hand) return max(evaluation) def count_highlow(hand: list) -> int: highlow = 0 low_cards = [1, 2, 3, 4, 5, 6, 7] for card in hand: if card in low_cards: highlow -= 1 elif card == 10: highlow += 1 return highlow def AI(hand: list, face_up: str, losses_in_row: int) -> 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 losses_in_row: number of losses in row in the current game Returns: list: player decision """ vals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10] cards_count = len(hand) highlow = count_highlow(hand) evaluation = cards_eval(hand) if len(evaluation) != 1: evaluation = evaluation[1] else: evaluation = evaluation[0] is_pair = any([hand.count(val) > 1 for val in vals]) decision = '' decision = getDecision(face_up, hand, is_pair) return decision def show_game_state(player_hand: list, dealer_hand: list, splited: bool, decision: str='') -> None: """Print dealer and player card values and player decision Args: player_hand (list): List of cards in player hand dealer_hand (list): List of cards in dealer hand splited (bool): True if it is subprocess by spliting decision (str, optional): Player decision. Will not be printed if defaults to ''. """ if decision and cards_eval(player_hand)[0] > 21: print("split| " if splited else '', end='') print(f"dealer: {cards_eval(dealer_hand)} player: {cards_eval(player_hand)} fail") elif decision: print("split| " if splited else '', end='') print(f"dealer: {cards_eval(dealer_hand)} player: {cards_eval(player_hand)} decision: {decision}") elif not cards_eval(player_hand)[0] > 21: print("split| " if splited else '', end='') print(f"dealer: {cards_eval(dealer_hand)} player: {cards_eval(player_hand)}") def blackjack(shoe: iter, dealer_hand: list=[], player_hand: list=[], bet: int=10, losses_in_row: int=0) -> 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. bet: amount of money the player betted losses_in_row: number of losses in row in the current game Returns: str: game result """ splited = False if dealer_hand == [] and player_hand == []: dealer_hand = [next(shoe), next(shoe)] player_hand = [next(shoe), next(shoe)] else: splited = True #dealer turn face_up = dealer_hand[0] dealer_value = dealer(dealer_hand, shoe) decision = '' while decision not in ['stand', 'surrender']: decision = AI(player_hand, face_up, losses_in_row) show_game_state(player_hand, [face_up], splited, decision) if decision == 'hit': player_hand.append(next(shoe)) elif decision == 'double down': # breakpoint() bet *= 2 player_hand.append(next(shoe)) elif decision == 'split': #this work reccursevly player_hand = [player_hand[0]] splited_ai = True #temp blackjack(shoe, dealer_hand, player_hand.copy(), bet)#new wager start #old wager continue elif decision == 'surrender': break elif decision == 'stand': #dealer_hand.append(next(shoe)) #dealer_value = dealer(dealer_hand, shoe) break player_value = max(cards_eval(player_hand)) show_game_state(player_hand, dealer_hand, splited) print("split| " if splited else '', end='') if player_value == 21 and 1 in player_hand: print('player blackjack', "="*50, sep='\n') return 'player blackjack', bet elif player_value > 21: print('dealer win', "="*50, sep='\n') return 'dealer win', bet elif dealer_value > 21: print('player win', "="*50, sep='\n') return 'player win', bet elif player_value > dealer_value: print('player win', "="*50, sep='\n') return 'player win', bet elif player_value == dealer_value: print('push', "="*50, sep='\n') return 'push', bet #keep money, no win no lose 0$ else: print('dealer win', "="*50, sep='\n') return 'dealer win', bet def game_loop(balance, bet) -> None: wins, losses, draws = 0, 0, 0 player_blackjack = 0 shoe_iter = iter(shoe(10)) losses_in_row = 0 notable_results = ['player win', 'dealer win', 'player blackjack'] while True: #round start try: balance -= bet result, game_bet = blackjack(shoe_iter, bet=bet, losses_in_row=losses_in_row) except StopIteration: break if result == 'player win': wins += 1 balance += (2*game_bet) losses_in_row = 0 elif result == 'player blackjack': wins += 1 # player_blackjack += 1 balance += (2*game_bet) + (game_bet/2) losses_in_row = 0 elif result == 'dealer win': losses += 1 losses_in_row += 1 elif result == 'push': balance += game_bet draws += 1 return wins, losses, draws, balance #player_blackjack def calculate_bet(wins, losses, hand): pass # if __name__ == '__main__': # print(game_loop()) if __name__ == '__main__': statistics = [0, 0, 0] money_sum = 0 import time #don't use time.time() for counting code execution time! start = time.perf_counter() for i in range(10): wins, loses, draws, money = game_loop(0, 10) statistics[0] += wins statistics[1] += loses statistics[2] += draws money_sum += money end = time.perf_counter() result = end - start print(f'time: {round(result, 3)} seconds') print(f'wins: {statistics[0]} | losses: {statistics[1]} | draws: {statistics[2]}') print(f'win {round((statistics[0]/sum(statistics)*100), 2)}%') print(f'balance: {money_sum}') # print(f'moeny return {round((money_sum)*100, 2)}%') # total_wins, total_losses, total_draws = 0, 0, 0 # total_p_blackjack = 0 # balance = 0 # bet = 10 # import time # #don't use time.time() for counting code execution time! # start = time.perf_counter() # for i in range(100): # wins, loses, draws, balance, p_blackjack = game_loop(balance, bet) # total_wins += wins # total_losses += loses # total_draws += draws # total_p_blackjack += p_blackjack # end = time.perf_counter() # result = end - start # print(result) # print(f"Wins: {total_wins}, Losses: {total_losses}, Draws: {total_draws}") # print(f"Wins/Losses ratio: {total_wins/sum([total_wins, total_losses])}") # print(f"Balance: {balance}") # print(f"Times player hit blackjack: {total_p_blackjack}")