Пример #1
0
        public int GetStrategyScore(int numHandsToPlay)
        {
            int playerChips = 0;
            var deck        = new Deck(testConditions.NumDecks);
            var randomizer  = new Randomizer();

            Hand        dealerHand       = new Hand();
            Hand        playerHand       = new Hand();
            List <Hand> playerHands      = new List <Hand>();
            List <int>  betAmountPerHand = new List <int>();

            for (int handNum = 0; handNum < numHandsToPlay; handNum++)
            {
                dealerHand.Cards.Clear();
                playerHand.Cards.Clear();

                dealerHand.AddCard(deck.DealCard());
                dealerHand.AddCard(deck.DealCard());
                playerHand.AddCard(deck.DealCard());

                if (StackTheDeck)
                {
                    // even out the hands dealt to the player so it's proportionate to the
                    // number of cells in the three grids
                    var rand = randomizer.GetFloatFromZeroToOne();
                    if (rand < 0.33F)
                    {
                        // deal a pair
                        deck.ForceNextCardToBe(playerHand.Cards[0].Rank);
                    }
                    if (rand >= 0.33F && rand < 0.66F)
                    {
                        // deal a soft hand
                        if (playerHand.Cards[0].Rank != Card.Ranks.Ace)
                        {
                            deck.ForceNextCardToBe(Card.Ranks.Ace);
                        }
                        else
                        {
                            deck.EnsureNextCardIsnt(Card.Ranks.Ace);    // avoid a pair of Aces
                        }
                    }
                    // yes, our normal deal for hard hands may result in a pair or a hard hand, but
                    // we don't care since we're just trying to even out the proportion of those type of hands
                }
                playerHand.AddCard(deck.DealCard());

                playerHands.Clear();
                playerHands.Add(playerHand);

                // we need to track how much bet per hand, since you can double down after a split.
                betAmountPerHand.Clear();
                betAmountPerHand.Add(testConditions.BetSize);
                playerChips -= testConditions.BetSize;

                //  1. check for player Blackjack
                if (playerHand.HandValue() == 21)
                {
                    // if the dealer also has 21, then it's a tie
                    if (dealerHand.HandValue() == 21)       // ##!! THIS WAS EDITED FROM !=  TO == ##!!
                    {
                        playerChips += betAmountPerHand[0]; // return the bet
                    }
                    else
                    {
                        // Blackjack typically pays 3:2, although some casinos do 5:4
                        playerChips += testConditions.BlackjackPayoffSize + betAmountPerHand[0];   //+betSize was EDITED IN
                    }
                    betAmountPerHand[0] = 0;
                    continue;   // go to next hand
                }

                //  2. if the dealer has blackjack, then simply move to the next hand, since playerChips was already decremented
                if (dealerHand.HandValue() == 21)
                {
                    continue;
                }

                //  3.  If the player has a playable hand, get a decision and play until standing or busting
                //      If split is selected, a new hand is added to playerHands, which is why we loop like this:
                for (var handIndex = 0; handIndex < playerHands.Count; handIndex++)
                {
                    playerHand = playerHands[handIndex];

                    var gameState = GameState.PlayerDrawing;
                    while (gameState == GameState.PlayerDrawing)
                    {
                        // if the hand was split and resulted in Blackjack, pay off and more to the next hand
                        if (playerHand.HandValue() == 21)
                        {
                            if (playerHand.Cards.Count == 2)                                                                                       // Blackjack
                            {
                                int blackjackPayoff = (testConditions.BlackjackPayoffSize + betAmountPerHand[handIndex]) / testConditions.BetSize; //testConditions.BlackjackPayoffSize * betAmountPerHand[handIndex]  / testConditions.BetSize;
                                playerChips += blackjackPayoff;
                                betAmountPerHand[handIndex] = 0;
                            }
                            gameState = GameState.DealerDrawing;
                            break;
                        }

                        var action = strategy.GetActionForHand(playerHand, dealerHand.Cards[0]);

                        // if there's an attempt to double-down with more than 2 cards, turn into a hit
                        if (action == ActionToTake.Double && playerHand.Cards.Count > 2)
                        {
                            action = ActionToTake.Hit;
                        }

                        switch (action)
                        {
                        case ActionToTake.Hit:
                            playerHand.AddCard(deck.DealCard());

                            // if we're at 21, we automatically stand
                            if (playerHand.HandValue() == 21)
                            {
                                gameState = GameState.DealerDrawing;
                            }

                            // did we bust?
                            if (playerHand.HandValue() > 21)
                            {
                                betAmountPerHand[handIndex] = 0;
                                gameState = GameState.PlayerBusted;
                            }
                            break;

                        case ActionToTake.Stand:
                            gameState = GameState.DealerDrawing;
                            break;

                        case ActionToTake.Double:
                            // double down means bet another chip, and get one and only card card
                            playerChips -= testConditions.BetSize;
                            betAmountPerHand[handIndex] += testConditions.BetSize;

                            playerHand.AddCard(deck.DealCard());
                            if (playerHand.HandValue() > 21)
                            {
                                betAmountPerHand[handIndex] = 0;
                                gameState = GameState.PlayerBusted;
                            }
                            else
                            {
                                gameState = GameState.DealerDrawing;
                            }
                            break;

                        case ActionToTake.Split:
                            // add the new hand to our collection
                            var newHand = new Hand();
                            newHand.AddCard(playerHand.Cards[1]);
                            playerHand.Cards[1] = deck.DealCard();
                            newHand.AddCard(deck.DealCard());
                            playerHands.Add(newHand);

                            // our extra bet
                            playerChips -= testConditions.BetSize;
                            betAmountPerHand.Add(testConditions.BetSize);

                            break;
                        }
                    }
                }

                // 4.  if there are playable hands for the player, get the dealer decisions
                bool playerHandsAvailable = betAmountPerHand.Sum() > 0;
                if (playerHandsAvailable)
                {
                    var gameState = GameState.DealerDrawing;

                    // draw until holding 17 or busting
                    while (dealerHand.HandValue() < 17)
                    {
                        dealerHand.AddCard(deck.DealCard());
                        if (dealerHand.HandValue() > 21)
                        {
                            // payoff each hand that is still valid - busts and blackjacks have 0 for betAmountPerHand
                            for (int handIndex = 0; handIndex < playerHands.Count; handIndex++)
                            {
                                playerChips += betAmountPerHand[handIndex] * 2;  // the original bet and a matching amount
                            }
                            gameState = GameState.DealerBusted;
                            break;
                        }
                    }

                    // 5. and then compare the dealer hand to each player hand
                    if (gameState != GameState.DealerBusted)
                    {
                        int dealerHandValue = dealerHand.HandValue();
                        for (int handIndex = 0; handIndex < playerHands.Count; handIndex++)
                        {
                            var playerHandValue = playerHands[handIndex].HandValue();

                            // if it's a tie, give the player his bet back
                            if (playerHandValue == dealerHandValue)
                            {
                                playerChips += betAmountPerHand[handIndex];
                            }
                            else
                            {
                                if (playerHandValue > dealerHandValue)
                                {
                                    // player won
                                    playerChips += betAmountPerHand[handIndex] * 2;  // the original bet and a matching amount
                                }
                                else
                                {
                                    // player lost, nothing to do since the chips have already been decremented
                                }
                            }
                        }
                    }
                }
            }

            return(playerChips);
        }
Пример #2
0
        private static void AddStrategyForUpcard(Card.Ranks upcardRank, Strategy result, CandidateSolution <bool, ProblemState> candidate)
        {
            Card dealerCard = new Card(upcardRank, Card.Suits.Diamonds);

            // do pairs
            for (var pairedRank = Card.Ranks.Ace; pairedRank >= Card.Ranks.Two; pairedRank--)
            {
                // build player hand
                Hand playerHand = new Hand();
                playerHand.AddCard(new Card(pairedRank, Card.Suits.Hearts));
                playerHand.AddCard(new Card(pairedRank, Card.Suits.Spades));

                // find strategy
                SetupStateData(candidate.StateData, playerHand, dealerCard);
                candidate.Evaluate();

                // get the decision and store in the strategy object
                var action = GetActionFromCandidate(candidate.StateData);

                result.SetActionForPair(upcardRank, pairedRank, action);
            }

            // then soft hands
            // we don't start with Ace, because that would be AA, which is handled in the pair zone
            // we also don't start with 10, since that's blackjack.  So 9 is our starting point
            for (int otherCard = 9; otherCard > 1; otherCard--)
            {
                // build player hand
                Hand playerHand = new Hand();

                // first card is an ace, second card is looped over
                playerHand.AddCard(new Card(Card.Ranks.Ace, Card.Suits.Hearts));
                playerHand.AddCard(new Card((Card.Ranks)otherCard, Card.Suits.Spades));

                // find strategy
                SetupStateData(candidate.StateData, playerHand, dealerCard);
                candidate.Evaluate();

                // get the decision and store in the strategy object
                var action = GetActionFromCandidate(candidate.StateData);
                result.SetActionForSoftHand(upcardRank, otherCard, action);
            }

            // hard hands.
            for (int hardTotal = 20; hardTotal > 4; hardTotal--)
            {
                // build player hand
                Hand playerHand = new Hand();

                // divide by 2 if it's even, else add one and divide by two
                int firstCardRank  = ((hardTotal % 2) != 0) ? (hardTotal + 1) / 2 : hardTotal / 2;
                int secondCardRank = hardTotal - firstCardRank;

                // 20 is always TT, which is a pair, so we handle that by building a 3 card hand
                if (hardTotal == 20)
                {
                    playerHand.AddCard(new Card(Card.Ranks.Ten, Card.Suits.Diamonds));
                    firstCardRank  = 6;
                    secondCardRank = 4;
                }

                // we don't want pairs, so check for that
                if (firstCardRank == secondCardRank)
                {
                    firstCardRank++;
                    secondCardRank--;
                }

                playerHand.AddCard(new Card((Card.Ranks)firstCardRank, Card.Suits.Diamonds));
                playerHand.AddCard(new Card((Card.Ranks)secondCardRank, Card.Suits.Spades));

                // find strategy
                SetupStateData(candidate.StateData, playerHand, dealerCard);
                candidate.Evaluate();

                // get the decision and store in the strategy object
                var action = GetActionFromCandidate(candidate.StateData);
                result.SetActionForHardHand(upcardRank, hardTotal, action);
            }
        }
Пример #3
0
        //-------------------------------------------------------------------------
        // each candidate gets evaluated here
        //-------------------------------------------------------------------------
        private float EvaluateCandidate(CandidateSolution <bool, ProblemState> candidate)
        {
            int playerChips = 0;

            for (int handNum = 0; handNum < TestConditions.NumHandsToPlay; handNum++)
            {
                // for each hand, we generate a random deck.  Blackjack is often played with multiple decks to improve the house edge
                MultiDeck deck = new MultiDeck(TestConditions.NumDecks);
                // always use the designated dealer upcard (of hearts), so we need to remove from the deck so it doesn't get used twice
                deck.RemoveCard(dealerUpcardRank, "H");

                Hand dealerHand = new Hand();
                Hand playerHand = new Hand();
                playerHand.AddCard(deck.DealCard());
                dealerHand.AddCard(new Card(dealerUpcardRank, "H"));
                playerHand.AddCard(deck.DealCard());
                dealerHand.AddCard(deck.DealCard());

                // save the cards in state, and reset the votes for this hand
                candidate.StateData.PlayerHands.Clear();
                candidate.StateData.PlayerHands.Add(playerHand);

                // do the intial wager
                int totalBetAmount = TestConditions.BetSize;
                playerChips -= TestConditions.BetSize;

                // outer loop is for each hand the player holds.  Obviously this only happens when they've split a hand
                for (int handIndex = 0; handIndex < candidate.StateData.PlayerHands.Count; handIndex++)
                {
                    candidate.StateData.HandIndex = handIndex;
                    playerHand = candidate.StateData.PlayerHand; // gets the current hand, based on index

                    // loop until the hand is done
                    var currentHandState = TestConditions.GameState.PlayerDrawing;

                    // check for player having a blackjack, which is an instant win
                    if (playerHand.HandValue() == 21)
                    {
                        // if the dealer also has 21, then it's a tie
                        if (dealerHand.HandValue() != 21)
                        {
                            currentHandState = TestConditions.GameState.PlayerBlackjack;
                            playerChips     += TestConditions.BlackjackPayoffSize;
                        }
                        else
                        {
                            // a tie means we just ignore it and drop through
                            currentHandState = TestConditions.GameState.HandComparison;
                        }
                    }

                    // check for dealer having blackjack, which is either instant loss or tie
                    if (dealerHand.HandValue() == 21)
                    {
                        currentHandState = TestConditions.GameState.HandComparison;
                    }

                    // player draws
                    while (currentHandState == TestConditions.GameState.PlayerDrawing)
                    {
                        // get the decision
                        candidate.StateData.VotesForDoubleDown = 0;
                        candidate.StateData.VotesForHit        = 0;
                        candidate.StateData.VotesForStand      = 0;
                        candidate.StateData.VotesForSplit      = 0;
                        candidate.Evaluate();   // throw away the result, because it's meaningless

                        // look at the votes to see what to do
                        var action = GetAction(candidate.StateData);

                        // if there's an attempt to double-down with more than 2 cards, turn into a hit
                        if (action == ActionToTake.Double && playerHand.Cards.Count > 2)
                        {
                            action = ActionToTake.Hit;
                        }

                        // if we're trying to split, but don't have a pair, turn that into a stand?
                        if (action == ActionToTake.Split && !playerHand.IsPair())
                        {
                            Debug.Assert(false, "Vote for split without a pair");
                        }

                        switch (action)
                        {
                        case ActionToTake.Hit:
                            // hit me
                            playerHand.AddCard(deck.DealCard());
                            // if we're at 21, we're done
                            if (playerHand.HandValue() == 21)
                            {
                                currentHandState = TestConditions.GameState.DealerDrawing;
                            }
                            // did we bust?
                            if (playerHand.HandValue() > 21)
                            {
                                currentHandState = TestConditions.GameState.PlayerBusted;
                            }
                            break;

                        case ActionToTake.Stand:
                            // if player stands, it's the dealer's turn to draw
                            currentHandState = TestConditions.GameState.DealerDrawing;
                            break;

                        case ActionToTake.Double:
                            // double down means bet another chip, and get one and only card card
                            playerChips    -= TestConditions.BetSize;
                            totalBetAmount += TestConditions.BetSize;
                            playerHand.AddCard(deck.DealCard());
                            if (playerHand.HandValue() > 21)
                            {
                                currentHandState = TestConditions.GameState.PlayerBusted;
                            }
                            else
                            {
                                currentHandState = TestConditions.GameState.DealerDrawing;
                            }
                            break;

                        case ActionToTake.Split:
                            // do the split and add the hand to our collection
                            var newHand = new Hand();
                            newHand.AddCard(playerHand.Cards[1]);
                            playerHand.Cards[1] = deck.DealCard();
                            newHand.AddCard(deck.DealCard());
                            candidate.StateData.PlayerHands.Add(newHand);

                            //Debug.WriteLine("TID " + AppDomain.GetCurrentThreadId() + " " +
                            //    "is splitting and has " + candidate.StateData.PlayerHands.Count + " hands");
                            Debug.Assert(candidate.StateData.PlayerHands.Count < 5, "Too many hands for player");

                            // our extra bet
                            playerChips -= TestConditions.BetSize;
                            // we don't adjust totalBetAmount because each bet pays off individually, so the total is right
                            //totalBetAmount += TestConditions.BetSize;
                            break;
                        }
                    }

                    // if the player busted, nothing to do, since chips have already been consumed.  Just go on to the next hand
                    // on the other hand, if the player hasn't busted, then we need to play the hand for the dealer
                    while (currentHandState == TestConditions.GameState.DealerDrawing)
                    {
                        // if player didn't bust or blackjack, dealer hits until they have 17+ (hits on soft 17)
                        if (dealerHand.HandValue() < 17)
                        {
                            dealerHand.AddCard(deck.DealCard());
                            if (dealerHand.HandValue() > 21)
                            {
                                currentHandState = TestConditions.GameState.DealerBusted;
                                playerChips     += totalBetAmount * 2; // the original bet and a matching amount
                            }
                        }
                        else
                        {
                            // dealer hand is 17+, so we're done
                            currentHandState = TestConditions.GameState.HandComparison;
                        }
                    }

                    if (currentHandState == TestConditions.GameState.HandComparison)
                    {
                        int playerHandValue = playerHand.HandValue();
                        int dealerHandValue = dealerHand.HandValue();

                        // if it's a tie, give the player his bet back
                        if (playerHandValue == dealerHandValue)
                        {
                            playerChips += totalBetAmount;
                        }
                        else
                        {
                            if (playerHandValue > dealerHandValue)
                            {
                                // player won
                                playerChips += totalBetAmount * 2;  // the original bet and a matching amount
                            }
                            else
                            {
                                // player lost, nothing to do since the chips have already been decremented
                            }
                        }
                    }
                }
            }

            return(playerChips);
        }
Пример #4
0
        public static void ShowPlayableHands(StrategyBase strategy, Canvas canvas, string savedImageName, string displayText)
        {
            // clear the screen
            canvas.Children.Clear();

            Color hitColor    = Colors.LightGreen,
                  standColor  = Color.FromRgb(252, 44, 44),
                  doubleColor = Colors.Yellow,
                  splitColor  = Colors.MediumPurple;

            AddInformationalText(displayText, canvas);

            // display a grid for non-paired hands without an ace.  One column for each possible dealer upcard
            AddColorBox(Colors.White, "", 0, 0, canvas);
            int x = 1, y = 0;

            foreach (var upcardRank in Card.ListOfRanks)
            {
                // strategy for 10, J, Q, and K are the same, so skip some of those
                if (upcardRank == Card.Ranks.Jack || upcardRank == Card.Ranks.Queen || upcardRank == Card.Ranks.King)
                {
                    continue;
                }

                Card dealerUpcard = new Card(upcardRank, Card.Suits.Diamonds);

                string upcardRankName = Card.RankText(upcardRank);
                AddColorBox(Colors.White, upcardRankName, x, 0, canvas);
                y = 1;

                for (int hardTotal = 20; hardTotal > 4; hardTotal--)
                {
                    // add a white box with the total
                    AddColorBox(Colors.White, hardTotal.ToString(), 0, y, canvas);

                    // build player hand
                    Hand playerHand = new Hand();

                    // divide by 2 if it's even, else add one and divide by two
                    int firstCardRank  = ((hardTotal % 2) != 0) ? (hardTotal + 1) / 2 : hardTotal / 2;
                    int secondCardRank = hardTotal - firstCardRank;
                    if (firstCardRank == secondCardRank)
                    {
                        firstCardRank++;
                        secondCardRank--;

                        if (firstCardRank == 11)
                        {
                            // hard 20 needs to be three cards, so in this case 9, 4, 7
                            firstCardRank = 4;
                            playerHand.AddCard(new Card(Card.Ranks.Seven, Card.Suits.Clubs));
                        }
                    }

                    playerHand.AddCard(new Card((Card.Ranks)firstCardRank, Card.Suits.Diamonds));
                    playerHand.AddCard(new Card((Card.Ranks)secondCardRank, Card.Suits.Hearts));

                    // get strategy and display
                    var action = strategy.GetActionForHand(playerHand, dealerUpcard);
                    switch (action)
                    {
                    case ActionToTake.Hit:
                        AddColorBox(hitColor, "H", x, y, canvas);
                        break;

                    case ActionToTake.Stand:
                        AddColorBox(standColor, "S", x, y, canvas);
                        break;

                    case ActionToTake.Double:
                        AddColorBox(doubleColor, "D", x, y, canvas);
                        break;
                    }
                    y++;
                }
                x++;
            }

            // and another for hands with an ace
            // display a grid for hands without an ace.  One column for each possible dealer upcard
            const int leftColumnForAces = 12;

            AddColorBox(Colors.White, "", leftColumnForAces, 0, canvas);
            x = leftColumnForAces + 1;
            foreach (var upcardRank in Card.ListOfRanks)
            {
                // strategy for 10, J, Q, and K are the same, so skip some of those
                if (upcardRank == Card.Ranks.Jack || upcardRank == Card.Ranks.Queen || upcardRank == Card.Ranks.King)
                {
                    continue;
                }

                string upcardRankName = Card.RankText(upcardRank);
                Card   dealerUpcard   = new Card(upcardRank, Card.Suits.Diamonds);

                AddColorBox(Colors.White, upcardRankName, x, 0, canvas);
                y = 1;

                // we don't start with Ace, because that would be AA, which is handled in the pair zone
                // we also don't start with 10, since that's blackjack.  So 9 is our starting point
                // only need 2 through 9, since a-a is in the pairs table, and a-10 is blackjack
                for (var otherCard = Card.Ranks.Nine; otherCard >= Card.Ranks.Two; otherCard--)
                {
                    // add a white box with the player hand: "A-x"
                    string otherCardRank = Card.RankText(otherCard);
                    AddColorBox(Colors.White, "A-" + otherCardRank, leftColumnForAces, y, canvas);

                    // build player hand
                    Hand playerHand = new Hand();
                    // first card is an ace, second card is looped over
                    playerHand.AddCard(new Card(Card.Ranks.Ace, Card.Suits.Hearts)); // ace of hearts
                    playerHand.AddCard(new Card(otherCard, Card.Suits.Spades));

                    // get strategy and display
                    var action = strategy.GetActionForHand(playerHand, dealerUpcard);
                    switch (action)
                    {
                    case ActionToTake.Hit:
                        AddColorBox(hitColor, "H", x, y, canvas);
                        break;

                    case ActionToTake.Stand:
                        AddColorBox(standColor, "S", x, y, canvas);
                        break;

                    case ActionToTake.Double:
                        AddColorBox(doubleColor, "D", x, y, canvas);
                        break;
                    }
                    y++;
                }
                x++;
            }

            // finally, a grid for pairs
            int startY = y + 1;

            AddColorBox(Colors.White, "", leftColumnForAces, startY, canvas);
            x = leftColumnForAces + 1;
            foreach (var upcardRank in Card.ListOfRanks)
            {
                // strategy for 10, J, Q, and K are the same, so skip some of those
                if (upcardRank == Card.Ranks.Jack || upcardRank == Card.Ranks.Queen || upcardRank == Card.Ranks.King)
                {
                    continue;
                }

                string upcardRankName = Card.RankText(upcardRank);
                Card   dealerUpcard   = new Card(upcardRank, Card.Suits.Diamonds);

                AddColorBox(Colors.White, upcardRankName, x, startY, canvas);
                y = startY + 1;

                for (var pairedRank = Card.Ranks.Ace; pairedRank >= Card.Ranks.Two; pairedRank--)
                {
                    // strategy for 10, J, Q, and K are the same, so skip some of those
                    if (pairedRank == Card.Ranks.Jack || pairedRank == Card.Ranks.Queen || pairedRank == Card.Ranks.King)
                    {
                        continue;
                    }

                    // add a white box with the player hand: "x-x"
                    string pairedCardRank = Card.RankText(pairedRank);
                    AddColorBox(Colors.White, pairedCardRank + "-" + pairedCardRank, leftColumnForAces, y, canvas);

                    // build player hand
                    Hand playerHand = new Hand();
                    playerHand.AddCard(new Card(pairedRank, Card.Suits.Hearts)); // X of hearts
                    playerHand.AddCard(new Card(pairedRank, Card.Suits.Spades)); // X of spades

                    // get strategy and display
                    var action = strategy.GetActionForHand(playerHand, dealerUpcard);
                    switch (action)
                    {
                    case ActionToTake.Hit:
                        AddColorBox(hitColor, "H", x, y, canvas);
                        break;

                    case ActionToTake.Stand:
                        AddColorBox(standColor, "S", x, y, canvas);
                        break;

                    case ActionToTake.Double:
                        AddColorBox(doubleColor, "D", x, y, canvas);
                        break;

                    case ActionToTake.Split:
                        AddColorBox(splitColor, "P", x, y, canvas);
                        break;
                    }
                    y++;
                }
                x++;
            }

            // now that's it's drawn, save an image?
            if (!string.IsNullOrEmpty(savedImageName))
            {
                SaveCanvasToPng(canvas, savedImageName);
            }
        }