static void RunFinalTests(CandidateSolution <double, StateData> candidate, out double finalScore, out string testDetails) { var sampleData = new Dataset(); StringBuilder sb = new StringBuilder(); sb.AppendLine("Test results at " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()); // run through our test data and see how close the answer the genetic program // comes up with is to the training data. Lower fitness scores are better than bigger scores double totalDifference = 0; while (true) { var row = sampleData.GetRowOfTestingData(); if (row == null) { break; } // populate vars with values from row of the training data for (int i = 0; i < Dataset.NumColumns; i++) { if (i != Dataset.LabelColumnIndex) { candidate.SetVariableValue("v" + i, row[i]); } } var result = candidate.Evaluate(); // now figure the difference between the calculated value and the training data var actualAnswer = row[Dataset.LabelColumnIndex]; var diff = result - actualAnswer; totalDifference += diff * diff; Console.WriteLine("Ans: " + actualAnswer.ToString(" 0") + " AI: " + result.ToString("0.00")); sb.AppendLine(actualAnswer.ToString("0.0") + ", " + result.ToString("0.000")); } totalDifference = Math.Sqrt(totalDifference); finalScore = (float)totalDifference; sb.AppendLine("\"Final score\", " + finalScore.ToString("0.000")); testDetails = sb.ToString(); }
static float EvaluateCandidate(CandidateSolution <double, StateData> candidate) { var sampleData = new Dataset(); // run through our test data and see how close the answer the genetic program // comes up with is to the training data. Lower fitness scores are better than bigger scores double totalDifference = 0; while (true) { var row = sampleData.GetRowOfTrainingData(); if (row == null) { break; } // populate vars with values from row of the training data, and then get the calculated value for (int i = 0; i < Dataset.NumColumns; i++) { if (i != Dataset.LabelColumnIndex) { candidate.SetVariableValue("v" + i, row[i]); } } var result = candidate.Evaluate(); // now figure the difference between the calculated value and the training data var actualAnswer = row[Dataset.LabelColumnIndex]; var diff = result - actualAnswer; totalDifference += diff * diff; } // sqrt of the summed squared diffences totalDifference = Math.Sqrt(totalDifference); // fitness function returns a float return((float)totalDifference); }
//------------------------------------------------------------------------- // 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); }
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); } }