protected override void Start()
 {
     if (MyPerceptor == null || !MyPerceptor.isActiveAndEnabled)
     {
         MyPerceptor = GetFirstActivePerceptor();
     }
     base.Start();
 }
Exemple #2
0
    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);
        }
    }
Exemple #3
0
 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);
    }
Exemple #7
0
 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);
 }
Exemple #8
0
 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);
 }
Exemple #9
0
 public abstract MoveData.DualUtility EstimateMoveUtility(MoveData move, CardController otherCard, AIGenericPerceptor perceptorData);
Exemple #10
0
    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);
    }