Ejemplo n.º 1
0
def informal_hand_test():
    from shoe import Shoe
    s = Shoe()
    h = Hand(10)
    h.isVerbose = True
    print(h)
    c = s.draw()
    c.flip()
    print(c)
    if h.can_hit():
        h.hit(c)
        print(h)
    c = s.draw()
    c.flip()
    print(c)
    if h.can_hit():
        h.hit(c)
        print(h)
    print('Can double:', h.can_double())
    print('Can hit:', h.can_hit())
    print('Can split:', h.can_split())
    c = s.draw()
    c.flip()
    h.double_down(c, h.bet)
    print(h)
    #should be busted now
    try:
        c = s.draw()
        c.flip()
        h.hit(c)
    except RuleError as error:
        print(error)
        print("tried and failed to hit a busted hand.")
Ejemplo n.º 2
0
class Sim():
    def __init__(self):
        self.DECKS = 8
        self.shoe = Shoe(8)
        self.p1 = Player("Complete_Strategy", 10000)
        self.p2 = Player("Basic_Strategy", 10000)
        self.dealer = Dealer(self.shoe)
        self.sims = 0

    def run(self):
        for x in range(10000):

            print(f"*** Hand {self.sims+1}***")
            self.sims += 1

            print(self.p1)
            print(self.p2)

            self.p1.bet()
            self.p2.bet()

            self.deal_cards()

            while not self.p1.is_done() or not self.p2.is_done():
                card = Card(Rank(random.randint(Rank.Ace, Rank.King)),
                            Suit.Spade)
                action_b = strategy.infinite_basic_action(
                    self.p1.hand, self.dealer)
                action_c = strategy.infinite_complete_action(
                    self.p2.hand, self.dealer)
                self.p1.process_action(action_b, card)
                self.p2.process_action(action_c, card)

            self.dealer.playout_hand()

            self.p1.payout(self.dealer.hand)
            self.p2.payout(self.dealer.hand)

            self.dealer.reset()
            self.shoe.reset_shoe(
            )  # why am I reseting shoe everytime yet still calculating deal probs every hand?

            print("\n")

    def deal_cards(self):
        players_hand = self.random_hand(2)
        self.p1.add_hand(players_hand)
        self.p2.add_hand(deepcopy(players_hand))
        self.dealer.deal_card(
            Card(Rank(random.randint(Rank.Ace, Rank.King)), Suit.Spade))

    def random_hand(self, size):
        cards = []
        for _ in range(size):
            card = Card(Rank(random.randint(Rank.Ace, Rank.King)), Suit.Spade)
            cards.append(card)
            self.shoe.draw(card)

        return Hand(cards)
Ejemplo n.º 3
0
 def test_deal(self):
     shoe = Shoe(1)
     self.assertEqual(len(shoe._cards), 52)
     card1 = shoe.draw()
     card2 = shoe.draw()
     self.assertEqual(len(shoe._cards), 50)
     self.assertNotIn(card1, shoe._cards)
     self.assertNotIn(card2, shoe._cards)
     shoe.receive([card1, card2])
     self.assertEqual(len(shoe._cards), 52)
Ejemplo n.º 4
0
class Dealer(Player):
    def __init__(self):
        super().__init__()
        self._shoe = Shoe(1)

    def hit(self, player, facedown=False):
        card = self._shoe.draw()
        card.facedown = facedown
        player.receive(card)

    def collect(self, cards):
        self._shoe.receive(self.dispose() + cards)

    def deal(self, player):
        self.hit(player)
        self.hit(self)
        self.hit(player)
        self.hit(self)
Ejemplo n.º 5
0
class Dealer(Player):
    def __init__(self,
                 name='Bob the dealer',
                 money=1000000,
                 delay=1,
                 verbose=True):
        super().__init__(name, money)
        self._playingPlayers = []
        self._playersWithInsurance = []
        self._shoe = Shoe()
        self.delay = delay
        self.isVerbose = verbose

    def sit(self, table):
        """Override the player sit method. so that the dealer sits on the right side of the table."""
        self._table = table
        table.add_dealer(self)

    def bet_or_leave(self):
        """
        At the start of each round the player can either bet by entering an amount
        to bet, sit this hand out by entering 0 for a bet, or leave the table by
        entering -1.
        """
        #
        # The dealer will not be in the list of players, so this method will not be
        # called on the dealer.
        #
        pass

    def wants_insurance(self):
        """
        Returns True if the player should buy insurance else return False.

        This procedure is called by the dealer after all players have bet and
        receives their cards and after the dealer has received his cards. It is
        only called if the dealer is showing an ace (the dealer might have blackjack).
        """
        #
        # The dealer will not be in the list of players, so this method will not be
        # called on the dealer.
        #
        pass

    def play(self, hand, dealerShowing):
        """
        Returns the player's action for this hand. The dealer calls this method
        repeatedly for each of the player's hands until all hands are completed.
        Valid return values are listed below. Note that two values are returned:

        choice: one of the plays listeded below
        additionalBet: the amount to "double down" by

        additionalBet is discarded by the caller in all other cases.

        allPlays = {'s': '[S]tand',
                    'h': '[H]it',
                    'd': '[D]ouble down',
                    'p': 's[P]lit',
                    'u': 's[U]rrender'}
        return choice, additionalBet
        """
        if hand.value() < 17:
            choice = 'h'  #hit
        else:
            choice = 's'  #stand
        additionalBet = None
        return choice, additionalBet

    def switch_shoe(self, shoe):
        """
        When we run a simulation instead of a game, we want to make sure that all of
        dealers are using the same shoe and switching shoes at the same time.

        :param shoe: a preshuffled shoe, ready to be delt from.
        :type shoe: Shoe
        :return:
        """
        self._shoe = shoe
        for player in self._table.players:
            player.reset_count()

    def take_bets(self):
        sleep(self.delay)
        if self.isVerbose: print('\n---betting---')
        self._playingPlayers = []
        leavingPlayers = []
        for player in self._table.players:
            #
            # = -1: player is leaving the table
            # =  0: players is sitting this hand out
            # >  0: player is betting this amount
            #
            if self.isVerbose: print(f'\n{player}')
            betAmount = player.bet_or_leave()
            name = player.name.capitalize()
            minimumBet = 1
            if betAmount == -1 or player.money < 1:  # leaving table
                leavingPlayers.append(player)
                if self.isVerbose:
                    print(
                        f"{name} is leaving the table with ${player.money:0.2f}."
                    )
            elif betAmount == 0:
                if self.isVerbose: print(f"{name} is sitting this hand out.")
            elif betAmount > 0 and player.money >= betAmount:
                if betAmount < minimumBet:
                    betAmount = minimumBet
                if betAmount > player.money:
                    betAmount = player.money
                self._playingPlayers.append(player)
                player.rake_out(betAmount)
                self.rake_in(betAmount)
                player.add_hand(Hand(betAmount))
                player.handsPlayed += 1
                player.totalWagers += betAmount
                if self.isVerbose:
                    print(f"{name} is betting ${betAmount:0.2f}.")
            else:
                raise ValueError(
                    f"{player} doesn't have enough money to bet ${betAmount:0.2f}."
                )
            for player in leavingPlayers:
                self._table.leave_table(player)

    def show_card_to_players(self, card):
        """
        Make sure that players who might be counting cards get to see
        every card that is delt, not just the ones in their hands.
        """
        for player in self._playingPlayers:
            player.count(card)

    def deal(self):
        """
        Deal an initial 2 cards to each player and to the dealer.
        """
        #
        # Shuffle the cards if we need to.
        #
        if self._shoe.should_shuffle():
            self._shoe.shuffle()
            for player in self._table.players:
                player.reset_count()
            self._table.shuffling_shoe()
        #
        # Deal cards to each player.
        #
        cardsToDeal = 2
        for _ in range(cardsToDeal):
            for player in self._playingPlayers:
                card = self._shoe.draw().flip()
                player.hands[0].hit(card)
                self.show_card_to_players(card)
        #
        # Deal yourself some cards.
        #
        bet = 0
        self.add_hand(Hand(bet))
        card = self._shoe.draw().flip()

        self.hands[0].hit(card)
        self.show_card_to_players(card)
        self.hands[0].hit(self._shoe.draw().flip())

    def offer_insurance(self):
        if self.isVerbose: print('\n')
        self._playersWithInsurance = []
        if self.hands[0][1].name == 'ace':
            for player in self._playingPlayers:
                if player.wants_insurance():
                    self._playersWithInsurance.append(player)
                    player.insurance = player.hands[0].bet / 2
                    player.timesInsurance += 1

    def play_hands(self):
        """
        Loop through the players and let them choose what they want to do. Then
        process that decision.
        """
        playerOptions = {
            's': self.player_stand,
            'h': self.player_hit,
            'p': self.player_split,
            'd': self.player_double_down,
            'u': self.player_surrender
        }
        if self.isVerbose: print('\n---players are playing---')
        dealerShowing = self.hands[0][1]
        for player in self._playingPlayers:
            for hand in player.hands:
                if hand.isBlackJack:
                    if self.isVerbose:
                        print(f"{player.name} has Blackjack! {hand}.")
                while hand.can_hit():
                    sleep(self.delay)
                    playerDecision, additionalBet = player.play(
                        hand, dealerShowing)
                    if playerDecision.lower() in playerOptions:
                        which_option = playerOptions[playerDecision.lower()]
                        which_option(player, hand, additionalBet)
                    else:
                        if self.isVerbose:
                            print(
                                f"I'm sorry, I don't know what '{playerDecision}' means."
                            )

    def player_stand(self, player, hand, *args):
        hand.stand()
        if self.isVerbose: print(f"{player.name} stands with {hand}.")

    def player_hit(self, player, hand, *args):
        card = self._shoe.draw().flip()
        self.show_card_to_players(card)
        hand.hit(card)
        if self.isVerbose:
            print(f"{player.name} hit and received a {card} {hand}.")
        player.timesHit += 1

    def player_split(self, player, hand, *args):
        if hand.can_split() and player.money >= hand.bet:
            self.rake_in(player.rake_out(hand.bet))
            newHand = Hand(hand.bet)
            newHand.hit(hand.split())
            card = self._shoe.draw().flip()
            self.show_card_to_players(card)
            newHand.hit(card)
            player.add_hand(newHand)
            card = self._shoe.draw().flip()
            self.show_card_to_players(card)
            hand.hit(card)
            if self.isVerbose:
                print(
                    f"{player.name} split and now has: \n   {hand}\n   {newHand}"
                )
            player.timesSplit += 1
            player.handsPlayed += 1
            player.totalWagers += hand.bet
        else:
            if self.isVerbose:
                print("Sorry, you can't split this hand (standing instead).")
            self.player_stand(player, hand)

    def player_double_down(self, player, hand, additionalBet):
        if hand.can_double() and is_number(
                additionalBet) and player.money >= additionalBet:
            card = self._shoe.draw().flip()
            self.show_card_to_players(card)
            hand.double_down(card, additionalBet)
            self.rake_in(player.rake_out(additionalBet))
            if self.isVerbose:
                print(
                    f"{player.name} doubled down and received a {card} {hand}."
                )
            player.timesDoubled += 1
            player.totalWagers += additionalBet
        else:
            if self.isVerbose:
                print("Sorry, you can't double this hand (hitting instead).")
            self.player_hit(player, hand)

    def player_surrender(self, player, hand, *args):
        print('Sorry, surrender is not implemented (pick again).')
        player.timesSurrendered += 1

    #TODO Make sure dealer shows hole card to players somewhere for counting.
    def play_own_hand(self):
        if self.isVerbose: print('\n---dealer is playing---')
        playerOptions = {'s': self.player_stand, 'h': self.player_hit}
        hand = self.hands[0]
        dealerShowing = hand[0]
        while hand.can_hit():
            playerDecision, additionalBet = self.play(hand, dealerShowing)
            which_option = playerOptions[playerDecision]
            which_option(self, hand, additionalBet)

    def payout_hands(self):
        if self.isVerbose: print('\n---results---')
        dealerHand = self.hands[0]
        for player in self._playingPlayers:
            sleep(self.delay * 2)
            for hand in player.hands:
                if hand.isBusted:
                    winnings = 0
                    text = 'lost'
                    player.timesBusted += 1
                    player.timesLost += 1
                    player.lastHand = 'lost'
                elif hand.isBlackJack and not dealerHand.isBlackJack:
                    winnings = hand.bet * 2.5
                    text = 'won (Blackjack!)'
                    player.timesWon += 1
                    player.timesBlackjack += 1
                    player.lastHand = 'won'
                elif hand.isBlackJack and dealerHand.isBlackJack:
                    winnings = hand.bet
                    text = 'pushed (blackjack) and lost'
                    player.timesPushed += 1
                    player.timesBlackjack += 1
                    player.lastHand = 'pushed'
                elif not hand.isBlackJack and dealerHand.isBlackJack:
                    winnings = 0
                    text = 'lost'
                    player.timesLost += 1
                    player.lastHand = 'lost'
                elif hand.value() == dealerHand.value():
                    winnings = hand.bet
                    text = 'pushed and lost'
                    player.timesPushed += 1
                    player.lastHand = 'pushed'
                elif dealerHand.isBusted:
                    winnings = hand.bet * 2
                    text = 'won (dealer busted)'
                    player.timesWon += 1
                    player.lastHand = 'won'
                elif hand.value() > dealerHand.value():
                    winnings = hand.bet * 2
                    text = 'won'
                    player.timesWon += 1
                    player.lastHand = 'won'
                elif hand.value() < dealerHand.value():
                    winnings = 0
                    text = 'lost'
                    player.timesLost += 1
                    player.lastHand = 'lost'
                player.rake_in(winnings)
                self.rake_out(winnings)
                winnings = abs(winnings - hand.bet)
                if self.isVerbose:
                    print(f"{player.name} {text} ${winnings:0.2f} on {hand}.")
        #
        # Payout any insurance bets.
        #
        for player in self._playersWithInsurance:
            if dealerHand.isBlackJack:
                winnings = player.insurance * 2
                player.rake_in(winnings)
                self.rake_out(winnings)
                if self.isVerbose:
                    print(
                        f"{player.name} won ${player.insurance:0.2f} on the insurance bet."
                    )
            else:
                if self.isVerbose:
                    print(
                        f"{player.name} lost ${player.insurance:0.2f} on the insurance bet."
                    )
        #
        # Clear the table and get ready for the next round.
        #
        for player in self._playingPlayers:
            player.discard_hands()
            player.insurance = 0
            if player._chips > player.maxMoney:
                player.maxMoney = player._chips
        self.discard_hands()
        if self.isVerbose: print('---results complete---')
Ejemplo n.º 6
0
class Blackjack(object):
  """
  An environment focused on playing blackjack. Can be played by a human or
  agent. Agents should override `Actor` to provide custom functionality based on
  the current state. An advantage of using this environment is that it allows
  for many actors to play at once and accumulate different experiences.
  """
  def __init__(self, agents, print_desc=False):
    # A typical shoe has 6 decks and reshuffles when roughly 5/6 decks are used.
    self.shoe = Shoe(6, 0.8)
    self.dealer = DealerActor()
    self.agents = []
    self.player_count = len(agents) + 1
    self.print_desc = print_desc

    # Add the agents.
    self.agents = agents

  def deal(self):
    """Deals the cards to the various players and agents."""
    if self.shoe.depth() > self.shoe.reshuffle_ratio:
      self.shoe.shuffle()

    for i in range(2 * self.player_count):
      card = self.shoe.draw()
      # Deal the card to the dealer last. This makes indexing easier, as well as
      # follows Vegas rules.
      if i % self.player_count == self.player_count - 1:
        self.dealer.add_card(card)
      else:
        self.agents[i % self.player_count].add_card(card)

    if self.print_desc:
      print('Dealer: ' + str(self.dealer))
      for idx, agent in enumerate(self.agents):
        print('Player ' + str(idx + 1) + ': ' + str(agent))
      print('-------------------------')

  def play(self):
    """
    Iterates through all of the agents and then the dealer to play their hand.
    """
    if self.dealer.first_card().is_ace():
      count = self.shoe.hilo_count() - self.dealer.hidden_card().hilo_count()
      state = {
        DEALER_STATE_KEY: self.dealer.first_card(),
        COUNT_STATE_KEY: count,
        INSURANCE_STATE_KEY: True
      }
      for idx, agent in enumerate(self.agents):
        a = agent.process_state(state)
        if a == Action.INSURANCE:
          agent.took_insurance = True

    if self.dealer.is_blackjack():
      self.dealer.show_hand = True
      if self.print_desc:
        print('Dealer: ' + str(self.dealer))
        print('Dealer has blackjack.')
      return

    for idx, agent in enumerate(self.agents):
      self.play_agent(agent, idx)

    self.dealer.show_hand = True
    if self.print_desc:
      print('Dealer: ' + str(self.dealer))

    # Assess the dealer actions.
    self.play_dealer(self.dealer)

    if self.print_desc:
      print('[FINAL] Dealer: ' + str(self.dealer))
      print('-------------------------')

  def assess_rewards(self):
    """Finalizes the hand and either takes or hands out winnings."""
    for idx, agent in enumerate(self.agents):
      # Other rewards are the number of cards that were drawn.
      additional_cards = agent.drawn_cards()
      running_reward = 0

      # Iterate through all of the hands of the agent and assess how they
      # performed.
      for hand_idx, hand in enumerate(agent.hands):
        # Get the new reward from the hand and update the running reward.
        # Doubling down doubles the player's bet for that hand.
        bet = (2 if hand.did_double else 1) * agent.bet

        new_reward = self.compare_hands(self.dealer.hands[0],
                                        agent.hands[hand_idx], bet)

        if agent.took_insurance:
          if self.dealer.is_blackjack():
            if not agent.is_blackjack(hand_idx):
              new_reward /= 2
            else:
              new_reward = agent.bet
          else:
            new_reward -= agent.bet / 2
        running_reward += new_reward

        if self.print_desc:
          won = new_reward > 0
          draw = new_reward == 0
          self._print_reward(idx, hand_idx, won, draw, new_reward)

      if self.print_desc:
        print('\n\n')

      # Process the rewards at the end of each agent's calculation.
      agent.process_reward(running_reward, agent.drawn_cards())

    self.dealer.process_reward(0, 0)

  def compare_hands(self, dealer, player, bet):
    player_count = player.count()
    dealer_count = dealer.count()
    dealer_bust = dealer_count > BLACKJACK_VALUE
    player_bust = player_count > BLACKJACK_VALUE

    # Both dealer and player blackjack is a push.
    if dealer.is_blackjack() and player.is_blackjack():
      return 0

    loss = player_bust or (player_count < dealer_count and not dealer_bust)
    win = ((dealer_bust or player_count > dealer_count or
          player.is_blackjack()) and not player_bust)

    if win:
      return (1.5 if player.is_blackjack() else 1) * bet
    elif loss:
      return -1 * bet
    else:
      return 0

  def play_agent(self, agent, idx):
    hand_idx = 0
    while hand_idx < len(agent.hands):
      self.play_hand(agent, idx, hand_idx)
      hand_idx += 1
    if self.print_desc:
      if len(agent.hands) == 1:
        print('[FINAL] Player ' + str(idx + 1) + ': ' + str(agent))
      else:
        print('[FINAL] Player ' + str(idx + 1) + ':\n' + str(agent))


  def play_hand(self, agent, idx, hand_idx):
    """
    Plays an individual hand for an agent.
    agent Agent:
      - The agent that is currently playing.
    idx Int:
      - The index of the agent that is playing.
    hand_idx Int:
      - The index of the hand that is currently being played.
    """
    # Splitting aces only allows for one card to be drawn for each hand.
    # Therefore, don't allow any actions on the second split hand.
    hand = agent.hands[hand_idx]
    if hand.is_split:
      if self.print_desc:
        print('Player ' + str(idx + 1) + ' (' + str(hand_idx) + '): ' +
              str(agent.hands[hand_idx]))
      if hand.cards[0].is_ace():
        return

    d = False
    while not d:
      # The count should be adjusted such that the dealer's second card count
      # isn't revealed.
      count = self.shoe.hilo_count() - self.dealer.hidden_card().hilo_count()
      state = {
        DEALER_STATE_KEY: self.dealer.first_card(),
        COUNT_STATE_KEY: count,
        DOUBLE_DOWN_STATE_KEY: hand.can_double_down(),
        SPLIT_STATE_KEY: hand.can_split(),
        HAND_INDEX_STATE_KEY: hand_idx
      }

      # Process the current state.
      a = agent.process_state(state)

      # Add the card if the user hit.
      if a == Action.HIT or a == Action.DOUBLE_DOWN:
        agent.add_card(self.shoe.draw(), hand_idx)
        if a == Action.DOUBLE_DOWN:
          hand.did_double = True

      split_finished = False
      if a == Action.SPLIT:
        # Split the hands into two hands.
        split_finished = hand.cards[0].is_ace()
        agent.split_hand(hand_idx)
        agent.add_card(self.shoe.draw(), hand_idx)
        agent.add_card(self.shoe.draw(), hand_idx + 1)

      # Print the description after the user takes their action.
      if self.print_desc and a != Action.STAND:
        print('Player ' + str(idx + 1) + ' (' + str(hand_idx) + '): ' +
              str(agent.hands[hand_idx]))

      # The agent is finished after they stand, or double down. A controversial,
      # but relatively accepted rule is only receiving one card for splitting
      # aces.
      d = (a == Action.STAND or a == Action.DOUBLE_DOWN or
           hand.count() > BLACKJACK_VALUE or split_finished)

  def play_dealer(self, dealer):
    """Plays the dealer's hand."""
    d = False
    while not d:
      a = dealer.process_state({})
      if a == Action.HIT:
        dealer.add_card(self.shoe.draw())
        if self.print_desc:
          print('Dealer: ' + str(dealer))

      # Dealer is done when they stand or bust.
      d = a == Action.STAND or dealer.count() >= BLACKJACK_VALUE

  def _print_reward(self, agent_idx, hand_idx, won, draw, reward):
    if not self.print_desc:
      return

    player_desc = 'Player ' + str(agent_idx + 1) + ' (' + str(hand_idx) + ')'
    if won:
      print(player_desc + ' won. +' + str(reward))
    elif draw:
      print(player_desc + ' pushed.')
    else:
      print(player_desc + ' lost. ' + str(reward))
Ejemplo n.º 7
0
class Dealer(Player):

    def __init__(self, name, money):
        super().__init__(name, money)
        self.shoe = Shoe()
        self.players = []
        # Holds bets before hands have been dealt
        # We could make this a dictionary with the player's name as the key,
        # but that assumes the player names are unique. Better solution would
        # probably be to set bets on the Player object
        self.playerBets = []

#region Dealer-specific methods

    def deal_in(self, player):
        """Add a player to the dealer's table"""
        self.players.append(player)

    def has_players(self):
        """Returns true if the dealer is dealing to at least 1 player"""
        return len(self.players) > 0

    def take_bets(self):
        """Ask all players how much they want to bet. Removes them from the table if they bet -1"""
        for player in self.players:
            bet = player.bet_or_leave()
            # Add each player's bet to a list of bets
            self.playerBets.append(bet)
        # Filter out players if they bet -1
        self.players = [p for i, p in enumerate(self.players) if self.playerBets[i] != -1]
        # Remove the -1 bets from the list of bets
        self.playerBets = [b for b in self.playerBets if b != -1]

    def deal(self):
        """Deal out hands to the players. Assumes we've taken bets"""
        # Shuffle the shoe if it needs it
        if (self.shoe.should_shuffle()):
            self.shoe.shuffle()
        
        # Deal out cards to the players
        for index, player in enumerate(self.players):
            bet = self.playerBets[index]
            # Don't deal a hand if the player bet 0
            if bet > 0:
                player.rake_out(bet)
                hand = Hand(bet)
                for _ in range(2):
                    hand.hit(self.shoe.draw().flip())
                player.add_hand(hand)
        
        # Clear out playerBets, all bets are on hands now
        self.playerBets = []

        # Deal out cards to the dealer
        dealerHand = Hand(0)
        for _ in range(2):
            dealerHand.hit(self.shoe.draw().flip())
        # Give the dealer the hand
        self.add_hand(dealerHand)
        
    def resolve_hands(self):
        dealerHand = self.hands[0]
        dealerShowing = dealerHand[-1]
        for player in self.players:
            for hand in player.hands:
                while hand.can_hit():
                    choice, bet = player.play(hand, dealerShowing)
                    if (choice == 's'):
                        hand.stand()
                    if (choice == 'h'):
                        hand.hit(self.shoe.draw().flip())
                    if (choice == 'd'):
                        player.rake_out(bet)
                        hand.double_down(self.shoe.draw().flip(), bet)
                    if (choice == 'p'):
                        player.rake_out(hand.bet)
                        splitHand = Hand(hand.bet)
                        card = hand.split()
                        splitHand.hit(card)
                        splitHand.hit(self.shoe.draw().flip())
                        player.add_hand(splitHand)
                        hand.hit(self.shoe.draw().flip())

                print(f"{player.name} finishes with {hand}")
                        

        # Now that the players have played, the dealer needs to play
        print("Let's see what the dealer has...")
        print(f"{self.name} flips their card, showing {dealerHand}")
        while dealerHand.can_hit():
            self.play(dealerHand, dealerShowing)
        print(f"{self.name} stands with {dealerHand}")

    def payout(self):
        print("Time to pay out!")
        dealerHand = self.hands[0]
        for player in self.players:
            for hand in player.hands:
                # If the player busts, they lose
                if hand.isBusted:
                    print(f"{player.name} busts, losing ${hand.bet:,.2f}.")
                    self.rake_in(hand.bet)
                # If the player's hand is worth less than dealer, they lose
                elif hand < dealerHand:
                    print(f"{player.name} lost to the dealer, losing ${hand.bet:,.2f}.")
                    self.rake_in(hand.bet)
                # If the hands are equal, the player gets their bet back
                elif hand == dealerHand:
                    print(f"{player.name} pushed, returning their bet of ${hand.bet:,.2f}.")
                    player.rake_in(hand.bet)
                # if player has blackjack and dealer doesn't (checked in == case), they win
                elif hand.isBlackJack:
                    # Blackjack pays 3:2
                    print(f"Blackjack! {player.name} wins ${hand.bet * 1.5:,.2f}.")
                    player.rake_in(hand.bet + hand.bet * 1.5)
                # If the player's hand is worth more than dealer, they win
                elif hand > dealerHand:
                    # Winning pays 1:1
                    print(f"{player.name} beat the dealer, winning ${hand.bet:,.2f}.")
                    player.rake_in(hand.bet + hand.bet)
                else:
                    raise NotImplementedError(f'Unchecked condition in payout. Player hand: {hand} Dealer: {dealerHand}.')
            player._hands = []
            self._hands = []

        # Remove players that run out of money
        for player in self.players:
            if player.money <= 0:
                print(f"{player.name} is out of cash. Thanks for playing!")
        self.players = [p for p in self.players if p.money > 0]

#endregion

    def play(self, hand, dealerShowing):
        # Dealer hits on soft 17
        if (hand.hard_value() >= 17):
            hand.stand()
        else:
            hand.hit(self.shoe.draw().flip())
Ejemplo n.º 8
0
class Simulation:

    MIN_BET = 15

    #go through the tables in this sequence

    #[a,b] where a is the dealer's face up card and b is the player's paired card
    PAIRED_TABLE = [
        ['U' for i in range(11)],  #dealer card == 0
        ['U', 'P', 'H', 'H', 'H', 'H', 'H', 'H', 'P', 'S', 'S'],  #1
        ['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'],  #2
        ['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'],  #3
        ['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'],  #4
        ['U', 'P', 'P', 'P', 'P', 'D', 'P', 'P', 'P', 'P', 'S'],  #5
        ['U', 'P', 'P', 'P', 'P', 'D', 'P', 'P', 'P', 'P', 'S'],  #6
        ['U', 'P', 'P', 'P', 'H', 'D', 'H', 'P', 'P', 'S', 'S'],  #7
        ['U', 'P', 'H', 'H', 'H', 'D', 'H', 'H', 'P', 'P', 'S'],  #8
        ['U', 'P', 'H', 'H', 'H', 'D', 'H', 'H', 'P', 'P', 'S'],  #9
        ['U', 'P', 'H', 'H', 'H', 'H', 'H', 'H', 'P', 'S', 'S'],  #10
    ]

    #[a,b] where a is the dealer's face up card and b is the player's non-ace card
    #note: if hitting a card creates an equivalent situation, we need to consider that
    ACE_TABLE = [
        ['U' for i in range(11)],  #dealer card == 0
        ['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'],  #1
        ['U', 'U', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S'],  #2
        ['U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'S', 'S', 'S'],  #3
        ['U', 'U', 'H', 'H', 'D', 'D', 'D', 'D', 'S', 'S', 'S'],  #4
        ['U', 'U', 'D', 'D', 'D', 'D', 'D', 'D', 'S', 'S', 'S'],  #5
        ['U', 'U', 'D', 'D', 'D', 'D', 'D', 'D', 'S', 'S', 'S'],  #6
        ['U', 'U', 'H', 'H', 'H', 'D', 'H', 'S', 'S', 'S', 'S'],  #7
        ['U', 'U', 'H', 'H', 'H', 'D', 'H', 'S', 'S', 'S', 'S'],  #8
        ['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'],  #9
        ['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'],  #10
    ]

    #[a,b] where a is the dealer's face up card and b is the player's hand value
    VALUE_TABLE = [
        ['U' for i in range(21)],  #dealer card == 0
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H',
            'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'
        ],  #1
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H',
            'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'
        ],  #2
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'H',
            'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'
        ],  #3
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S',
            'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'
        ],  #4
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S',
            'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'
        ],  #5
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S',
            'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'
        ],  #6
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H',
            'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'
        ],  #7
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H',
            'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'
        ],  #8
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H',
            'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'
        ],  #9
        [
            'U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'D', 'H',
            'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'
        ],  #10
    ]

    def __init__(self, num_decks):
        self.shoe = Shoe(num_decks)
        self.balance = 0
        self.lower_balance = 0
        self.playing = True

    def run(self):
        while not self.shoe.is_finished():
            if self.shoe.true_count() < -1:
                self.playing = False
            elif self.shoe.true_count() > 1:
                self.playing = True

            if self.playing: self.balance += self.play()

            #draw a number of cards for info
            for i in range(10):
                self.shoe.draw()
        return self.balance

    def play(self):  #play returns amount of money won/lost
        tc = round(self.shoe.true_count())
        bet = max(self.MIN_BET * round((tc + 1) / 2), self.MIN_BET)

        dealer_hand = [self.shoe.draw(), self.shoe.draw()]
        player_hand = [self.shoe.draw(), self.shoe.draw()]
        player_games = []  # for splits

        #check for blackjacks
        dbj = self.blackjack(dealer_hand)
        pbj = self.blackjack(player_hand)
        if dbj and pbj: return 0
        if dbj: return -1 * bet
        if pbj: return bet * 3 / 2

        #run the player hand and stop if there's a loss
        player_result = self.run_player_hand(dealer_hand, player_hand)
        if player_result == 'P':
            #store each hand and metadata as a tuples
            player_games = map(lambda hand: (hand, 'U', 0),
                               self.expand_hands(player_hand))
            #run each hand
            new_player_games = []
            for game in player_games:
                new_player_games.append(
                    (game[0], self.run_player_hand(dealer_hand,
                                                   game[0]), game[2]))
            player_games = new_player_games
        else:
            player_games = [(player_hand, player_result, 0)]

        #split the busted games and nonbusted games
        bust_games = []
        nonbust_games = []
        for game in player_games:
            if game[1] == 'DB':
                game = (game[0], game[1], -2 * bet)
                bust_games.append(game)
            elif game[1] == 'B':
                game = (game[0], game[1], -1 * bet)
                bust_games.append(game)
            else:
                nonbust_games.append(game)

        #run the dealer hand
        dealer_result = self.run_dealer_hand(dealer_hand)
        #print("Dealer hand: ", dealer_hand)

        #compare against each player hand
        new_nonbust_games = []
        for game in nonbust_games:
            gain = self.hand_result(dealer_result, dealer_hand, game[1],
                                    game[0]) * bet
            if game[1] == 'DS': gain *= 2
            new_nonbust_games.append((game[0], game[1], gain))
        nonbust_games = new_nonbust_games

        #cumulate results
        sum = 0
        for game in nonbust_games + bust_games:
            #print("Player hand: ", game[0], "Gain: ", game[2])
            sum += game[2]

        return sum

    def blackjack(self, hand):
        if 1 in hand and 10 in hand: return True
        return False

    #returns a list of non-splittable hands, assuming hand is already splittable and its 2 cards
    def expand_hands(self, player_hand):
        if player_hand[0] != player_hand[1]:
            return [player_hand]
        return self.expand_hands(
            [player_hand[0], self.shoe.draw()]) + self.expand_hands(
                [player_hand[0], self.shoe.draw()])

    def hand_result(self, dealer_result, dealer_hand, player_result,
                    player_hand):
        #we'll need these more than once
        soft_value = self.soft_value(player_hand)
        dealer_soft_value = self.soft_value(dealer_hand)
        #finalize winner and return net gain/loss
        if dealer_result == 'B' or dealer_soft_value < soft_value:
            return 1
        elif dealer_soft_value > soft_value:
            return -1
        else:
            return 0

    def run_player_hand(self, dealer_hand, player_hand):
        next_move = 'U'
        while True:
            #check if last move was double down
            if next_move == 'D':
                if self.hard_value(player_hand) > 21: return 'DB'
                return 'DS'

            #calc next move, including busts from a previous hit
            next_move = self.player_next_move(dealer_hand, player_hand)
            if next_move == 'H' or next_move == 'D':
                player_hand.append(self.shoe.draw())

            if next_move == 'S' or next_move == 'B' or next_move == 'P':
                return next_move

    def run_dealer_hand(self, dealer_hand):
        next_move = 'U'
        while True:
            if self.hard_value(dealer_hand) > 21: return 'B'
            if self.soft_value(dealer_hand) > 17: return 'S'
            dealer_hand.append(self.shoe.draw())

    def player_next_move(self, dealer_hand, player_hand):
        move = 'U'
        hard_value = self.hard_value(player_hand)
        if hard_value > 21: return 'B'
        if hard_value >= 17: return 'S'  #unnecessary but improves performance

        if len(player_hand) == 2 and player_hand[0] == player_hand[1]:
            return Simulation.PAIRED_TABLE[dealer_hand[0]][player_hand[0]]

        if 1 in player_hand and hard_value <= 11:
            return Simulation.ACE_TABLE[dealer_hand[0]][hard_value - 1]

        return Simulation.VALUE_TABLE[dealer_hand[0]][hard_value]

    def hard_value(self, hand):
        sum = 0
        for card in hand:
            sum += card
        return sum

    def soft_value(self, hand):
        ace = False
        sum = 0
        for card in hand:
            sum += card
            if card == 1:
                ace = True
        if ace and sum <= 11:
            return sum + 10
        return sum
Ejemplo n.º 9
0
class Simulation:

	MIN_BET = 15

	#go through the tables in this sequence

	#[a,b] where a is the dealer's face up card and b is the player's paired card
	PAIRED_TABLE = [
		['U' for i in range(11)], #dealer card == 0
		['U', 'P', 'H', 'H', 'H', 'H', 'H', 'H', 'P', 'S', 'S'], #1
		['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'], #2
		['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'], #3
		['U', 'P', 'P', 'P', 'H', 'D', 'P', 'P', 'P', 'P', 'S'], #4
		['U', 'P', 'P', 'P', 'P', 'D', 'P', 'P', 'P', 'P', 'S'], #5
		['U', 'P', 'P', 'P', 'P', 'D', 'P', 'P', 'P', 'P', 'S'], #6
		['U', 'P', 'P', 'P', 'H', 'D', 'H', 'P', 'P', 'S', 'S'], #7
		['U', 'P', 'H', 'H', 'H', 'D', 'H', 'H', 'P', 'P', 'S'], #8
		['U', 'P', 'H', 'H', 'H', 'D', 'H', 'H', 'P', 'P', 'S'], #9
		['U', 'P', 'H', 'H', 'H', 'H', 'H', 'H', 'P', 'S', 'S'], #10
	]

	#[a,b] where a is the dealer's face up card and b is the player's non-ace card
	#note: if hitting a card creates an equivalent situation, we need to consider that
	ACE_TABLE = [
		['U' for i in range(11)], #dealer card == 0
		['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'], #1
		['U', 'U', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S'], #2
		['U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'S', 'S', 'S'], #3
		['U', 'U', 'H', 'H', 'D', 'D', 'D', 'D', 'S', 'S', 'S'], #4
		['U', 'U', 'D', 'D', 'D', 'D', 'D', 'D', 'S', 'S', 'S'], #5
		['U', 'U', 'D', 'D', 'D', 'D', 'D', 'D', 'S', 'S', 'S'], #6
		['U', 'U', 'H', 'H', 'H', 'D', 'H', 'S', 'S', 'S', 'S'], #7
		['U', 'U', 'H', 'H', 'H', 'D', 'H', 'S', 'S', 'S', 'S'], #8
		['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'], #9
		['U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S'], #10
	]

	#[a,b] where a is the dealer's face up card and b is the player's hand value
	VALUE_TABLE = [
		['U' for i in range(21)], #dealer card == 0
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'], #1
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'], #2
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'H', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'], #3
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'], #4
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'], #5
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'D', 'D', 'D', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'S'], #6
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'], #7
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'], #8
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'D', 'D', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'], #9
		['U', 'U', 'U', 'U', 'U', 'H', 'H', 'H', 'H', 'H', 'H', 'D', 'H', 'H', 'H', 'H', 'H', 'S', 'S', 'S', 'S', 'S'], #10
	]



	def __init__(self, num_decks):
		self.shoe = Shoe(num_decks)
		self.balance = 0
		self.lower_balance = 0
		self.playing = True

	def run(self):
		while not self.shoe.is_finished():
			if self.shoe.true_count() < -1:
				self.playing = False
			elif self.shoe.true_count() > 1:
				self.playing = True

			if self.playing: self.balance += self.play()

			#draw a number of cards for info
			for i in range(10):
				self.shoe.draw()
		return self.balance

	def play(self): #play returns amount of money won/lost
		tc = round(self.shoe.true_count())
		bet = max(self.MIN_BET * round((tc + 1)/2), self.MIN_BET)


		dealer_hand = [self.shoe.draw(), self.shoe.draw()]
		player_hand = [self.shoe.draw(), self.shoe.draw()]
		player_games = [] # for splits

		#check for blackjacks
		dbj = self.blackjack(dealer_hand)
		pbj = self.blackjack(player_hand)
		if dbj and pbj: return 0
		if dbj: return -1 * bet
		if pbj: return bet * 3/2

		#run the player hand and stop if there's a loss
		player_result = self.run_player_hand(dealer_hand, player_hand)
		if player_result == 'P':
			#store each hand and metadata as a tuples
			player_games = map(lambda hand: (hand, 'U', 0), self.expand_hands(player_hand))
			#run each hand
			new_player_games = []
			for game in player_games:
				new_player_games.append((game[0], self.run_player_hand(dealer_hand, game[0]), game[2]))
			player_games = new_player_games
		else:
			player_games = [(player_hand, player_result, 0)]

		#split the busted games and nonbusted games
		bust_games = []
		nonbust_games = []
		for game in player_games:
			if game[1] == 'DB':
				game = (game[0], game[1], -2 * bet)
				bust_games.append(game)
			elif game[1] == 'B':
				game = (game[0], game[1], -1 * bet)
				bust_games.append(game)
			else:
				nonbust_games.append(game)

		#run the dealer hand
		dealer_result = self.run_dealer_hand(dealer_hand)
		#print("Dealer hand: ", dealer_hand)

		#compare against each player hand
		new_nonbust_games = []
		for game in nonbust_games:
			gain = self.hand_result(dealer_result, dealer_hand, game[1], game[0]) * bet
			if game[1] == 'DS': gain *= 2
			new_nonbust_games.append((game[0], game[1], gain))
		nonbust_games = new_nonbust_games

		#cumulate results
		sum = 0
		for game in nonbust_games + bust_games:
			#print("Player hand: ", game[0], "Gain: ", game[2])
			sum += game[2]

		return sum
		

	def blackjack(self, hand):
		if 1 in hand and 10 in hand: return True
		return False


	#returns a list of non-splittable hands, assuming hand is already splittable and its 2 cards
	def expand_hands(self, player_hand):
		if player_hand[0] != player_hand[1]:
			return [player_hand]
		return self.expand_hands([player_hand[0], self.shoe.draw()]) + self.expand_hands([player_hand[0], self.shoe.draw()])

	def hand_result(self, dealer_result, dealer_hand, player_result, player_hand):
		#we'll need these more than once
		soft_value = self.soft_value(player_hand)
		dealer_soft_value = self.soft_value(dealer_hand)
		#finalize winner and return net gain/loss
		if dealer_result == 'B' or dealer_soft_value < soft_value: 
			return 1
		elif dealer_soft_value > soft_value:
			return -1
		else:
			return 0


	def run_player_hand(self, dealer_hand, player_hand):
		next_move = 'U'
		while True:
			#check if last move was double down
			if next_move == 'D':
				if self.hard_value(player_hand) > 21: return 'DB'
				return 'DS'

			#calc next move, including busts from a previous hit
			next_move = self.player_next_move(dealer_hand, player_hand)
			if next_move == 'H' or next_move == 'D':
				player_hand.append(self.shoe.draw())

			if next_move == 'S' or next_move == 'B' or next_move == 'P':
				return next_move

	def run_dealer_hand(self, dealer_hand):
		next_move = 'U'
		while True:
			if self.hard_value(dealer_hand) > 21: return 'B'
			if self.soft_value(dealer_hand) > 17: return 'S'
			dealer_hand.append(self.shoe.draw())

	def player_next_move(self, dealer_hand, player_hand):
		move = 'U'
		hard_value = self.hard_value(player_hand)
		if hard_value > 21: return 'B'
		if hard_value >= 17: return 'S' #unnecessary but improves performance

		if len(player_hand) == 2 and player_hand[0] == player_hand[1]:
			return Simulation.PAIRED_TABLE[dealer_hand[0]][player_hand[0]]

		if 1 in player_hand and hard_value <= 11:
			return Simulation.ACE_TABLE[dealer_hand[0]][hard_value - 1]

		return Simulation.VALUE_TABLE[dealer_hand[0]][hard_value]

	def hard_value(self, hand):
		sum = 0
		for card in hand:
			sum += card
		return sum

	def soft_value(self, hand):
		ace = False
		sum = 0
		for card in hand:
			sum += card
			if card == 1:
				ace = True
		if ace and sum <= 11:
			return sum + 10
		return sum