protected override void Start() { if (MyPerceptor == null || !MyPerceptor.isActiveAndEnabled) { MyPerceptor = GetFirstActivePerceptor(); } base.Start(); }
protected void UpdateStatisticsForPlayer(int SittingOrder, float[] ActualDeckDistribution, int DeckCardsLeft) { AIGenericPerceptor Perceptor = Perceptors[SittingOrder]; // Calculate the deck MSE float[] EstimatedDeckDistribution = Perceptor.GetCardProbabilitiesInDeck(); float nextDeckMSE = AIUtil.GetMeanSquaredError(ActualDeckDistribution, EstimatedDeckDistribution); float nextDeckXEE = AIUtil.GetCrossEntropyError(ActualDeckDistribution, EstimatedDeckDistribution); // Calculate the card adversary distribution for other players float nextAdversaryHandMSE = 0, nextAdversaryHandXEE = 0; Vector3 nextAdversaryHandMLC = Vector3.zero; float[] ActualHandDistribution = new float[CardController.VALUE_PRINCESS + 1], EstimatedHandDistribution; int countHandsChecked = 0; for (int p = 0; p < Players.Length; p++) { if (p != SittingOrder && !Players[p].KnockedOut) { // Simulate the target player's hand distribution (perfect certainty) Array.Clear(ActualHandDistribution, 0, ActualHandDistribution.Length); ActualHandDistribution[Players[p].GetHand().Value] = 1f; // Get MSE for this distribution EstimatedHandDistribution = Perceptor.GetCardProbabilitiesInHand(Players[p]); nextAdversaryHandMSE += AIUtil.GetMeanSquaredError(ActualHandDistribution, EstimatedHandDistribution); nextAdversaryHandXEE += AIUtil.GetCrossEntropyError(ActualHandDistribution, EstimatedHandDistribution); nextAdversaryHandMLC += AIUtil.GetMostLikelyCardError(EstimatedHandDistribution, Players[p].GetHand().Value); countHandsChecked += 1; } } // Update the statistics for this perceptor class if (countHandsChecked > 0) { nextAdversaryHandMSE /= countHandsChecked; nextAdversaryHandMLC /= countHandsChecked; PerceptorStats[PerceptorClassNames[SittingOrder]].AppendStatistics(nextDeckMSE, nextDeckXEE, DeckCardsLeft, nextAdversaryHandMSE, nextAdversaryHandXEE, nextAdversaryHandMLC, countHandsChecked); } }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(move.Target != null); Debug.Assert(otherCard != null); // No point in playing against protected opponents MoveData.DualUtility result = MoveData.DualUtility.Default; if (move.Target.Protected || otherCard.Value == CardController.VALUE_GUARD) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } // The Baron's utility is the certainty that the other player's card is lower than your own for (int i = CardController.VALUE_GUARD; i < otherCard.Value; i++) { result.Utility += perceptorData.GetCardProbabilityInHand(move.Target, i); } // However, if the chance of tying or losing is above the certainty threshold, also drop the rank if ((1f - result.Utility) > AIGenericPerceptor.CERTAINTY_THRESHOLD) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } return(result); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(otherCard != null); // The Handmaid's utility is just the marginal utility of the card remaining in the player's hand return(new MoveData.DualUtility(MoveData.RANK_NORMAL, PlayerController.GetMarginalHandValueUtility(move.Player.Game, otherCard.Value))); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(otherCard != null); // The Countess' utility is very high if the other card is a Prince or a King (i.e. the utility of not losing the round) MoveData.DualUtility result = MoveData.DualUtility.Default; if (IsKnockOutByCountess(otherCard.Value, move.Card.Value)) { result.Rank = MoveData.RANK_MUST_PLAY; } else if (!move.Player.Game.TheKingAndBothPrincesAreDiscarded) { // Otherwise, it's the utility of potential misdirection (unless the King and both Princes are already discarded) result.Utility = Mathf.Max(0, (5f - otherCard.Value) / 4); } return(result); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(move.Target != null); // No point in playing against protected opponents MoveData.DualUtility result = MoveData.DualUtility.Default; if (otherCard.Value == CardController.VALUE_COUNTESS) { result.Rank = MoveData.RANK_NEVER_EVER; return(result); } else if (move.Target.Protected) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } // The Prince's utility is the marginal utility of the (estimated) target's hand, // compared to the estimate marginal utility of a draw from deck float targetHandMarginalUtility = PlayerController.GetMarginalHandValueUtility(move.Player.Game, perceptorData.GetExpectedHandValue(move.Target)); float deckDrawMarginalUtility = PlayerController.GetMarginalHandValueUtility(move.Player.Game, perceptorData.GetExpectedDeckValue()); // The resulting utility against opponents and against oneself is reversed if (move.Target == move.Player) { result.Utility = Mathf.Clamp01(deckDrawMarginalUtility - targetHandMarginalUtility); if (otherCard.Value == CardController.VALUE_PRINCESS) { result.Rank = MoveData.RANK_NEVER_EVER; return(result); } } else { result.Utility = Mathf.Clamp01(targetHandMarginalUtility - deckDrawMarginalUtility); // If you are certain the other player has the Princess, playing the Prince against them is paramount if (perceptorData.GetCertainHandValue(move.Target) == CardController.VALUE_PRINCESS) { result.Rank = MoveData.RANK_PARAMOUNT; } } return(result); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(move.Target != null); // No point in playing against protected opponents MoveData.DualUtility result = MoveData.DualUtility.Default; if (move.Target.Protected) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } // The utility of playing a Priest is the measure of the current player's uncertainty regarding the target player's hand, // but only if this player will have another turn, otherwise this knowledge is worthless (in which case the utility stays 0) if (perceptorData.WillThisPlayerHaveAnotherTurn(move.Player)) { float highestProbability = 0; float[] TargetHandProbabilities = perceptorData.GetCardProbabilitiesInHand(move.Target); for (int i = CardController.VALUE_GUARD; i <= CardController.VALUE_PRINCESS; i++) { if (TargetHandProbabilities[i] > highestProbability) { highestProbability = TargetHandProbabilities[i]; } } // The uncertainty score is the inverse of certainty result.Utility = 1f - highestProbability; } return(result); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); // The utility of playing the Princess is always negative, because it is an instant loss return(MoveData.DualUtility.MinValue); }
public abstract MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData);
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(move.Target != null); Debug.Assert(otherCard != null); // No point in playing against protected opponents MoveData.DualUtility result = MoveData.DualUtility.Default; if (otherCard.Value == CardController.VALUE_COUNTESS) { result.Rank = MoveData.RANK_NEVER_EVER; return(result); } else if (move.Target.Protected || otherCard.Value == CardController.VALUE_GUARD || (perceptorData.GetCardProbabilityInHand(move.Target, CardController.VALUE_GUARD) > 0) && perceptorData.WillThisPlayerHaveAnotherTurn(move.Target)) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } // The Kings's utility is the difference between the marginal utilities of own hand and the (estimated) target's hand float ownMarginalHandValueUtility = PlayerController.GetMarginalHandValueUtility(move.Player.Game, otherCard.Value); // Estimate the target player's hand and marginal utility float targetsMarginalHandValueUtility = PlayerController.GetMarginalHandValueUtility(move.Player.Game, perceptorData.GetExpectedHandValue(move.Target)); // Normalize the difference before returning result.Utility = (targetsMarginalHandValueUtility - ownMarginalHandValueUtility + 1f) / 2; return(result); }
public override MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData) { Debug.Assert(move.Card == this); Debug.Assert(move.Target != null); Debug.Assert(move.TargetHandGuess >= VALUE_PRIEST && move.TargetHandGuess <= VALUE_PRINCESS); // No point in playing against protected opponents, or when the likelihoood of guessing right is too low MoveData.DualUtility result = MoveData.DualUtility.Default; float LikelihoodOfCorrectGuess = perceptorData.GetCardProbabilityInHand(move.Target, move.TargetHandGuess); if (LikelihoodOfCorrectGuess < REASONABLE_DOUBT_CUTOFF || perceptorData.GetRevealedCardCount(move.TargetHandGuess) >= GameController.CARD_COUNT[move.TargetHandGuess]) { result.Rank = MoveData.RANK_NEVER_EVER; return(result); } else if (move.Target.Protected) { result.Rank = MoveData.RANK_BARELY_SENSIBLE; } else if (perceptorData.GetCertainHandValue(move.Target) == move.TargetHandGuess) { result.Rank = MoveData.RANK_PARAMOUNT; } // Utility of playing a Guard is just the certainty that the other player has the card you were going to guess result.Utility = LikelihoodOfCorrectGuess; return(result); }