// the final result which we use when testing public ActionToTake GetActionForHand(Hand hand, Card dealerUpcard) { if (hand.HandValue() >= 21) { return(ActionToTake.Stand); } var upcardIndex = IndexFromRank(dealerUpcard.Rank); if (hand.IsPair()) { var pairIndex = IndexFromRank(hand.Cards[0].Rank); return(pairsStrategy[upcardIndex, pairIndex]); } if (hand.HasSoftAce()) { // we want the total other than the high ace int howManyAces = hand.Cards.Count(c => c.Rank == Card.Ranks.Ace); int total = hand.Cards .Where(c => c.Rank != Card.Ranks.Ace) .Sum(c => c.RankValueHigh) + (howManyAces - 1); return(softStrategy[upcardIndex, total]); } return(hardStrategy[upcardIndex, hand.HandValue()]); }
//------------------------------------------------------------------------- // 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); }