protected override IEnumerator PickAMove()
    {
        // Wait until the perceptor has finished working
        yield return(new WaitUntil(() => MyPerceptor.READY));

        // Get available moves
        List <MoveData> availableMoves = myHand.GetLegalMoves(Game, this);

        availableMoves.AddRange(justDrawn.GetLegalMoves(Game, this));
        // Find the move wit the highest utility
        MoveData.DualUtility highestUtility = MoveData.DualUtility.MinValue;
        MoveData             bestMove       = availableMoves[0];

        foreach (MoveData move in availableMoves)
        {
            MoveData.DualUtility utility = GetMoveUtility(move);
            // Compare and discard
            if (utility.CompareTo(highestUtility) > 0)
            {
                bestMove       = move;
                highestUtility = utility;
            }
        }
        // Return the highest-utility move
        myNextMove = bestMove;
    }
示例#2
0
    protected override IEnumerator PickAMove()
    {
        // Wait until the perceptor has finished working
        yield return(new WaitUntil(() => MyPerceptor.READY));

        // Get available moves
        List <MoveData> availableMoves = myHand.GetLegalMoves(Game, this);

        availableMoves.AddRange(justDrawn.GetLegalMoves(Game, this));
        // Calculate utilities of every move
        MoveData.DualUtility[] moveUtility    = new MoveData.DualUtility[availableMoves.Count];
        MoveData.DualUtility   highestUtility = MoveData.DualUtility.MinValue;
        for (int i = 0; i < availableMoves.Count; i++)
        {
            moveUtility[i] = GetMoveUtility(availableMoves[i]);
            if (moveUtility[i].CompareTo(highestUtility) > 0)
            {
                highestUtility = moveUtility[i];
            }
        }
        // Calculate cutoff for random selection
        float cutoff = Mathf.Max(0, RELATIVE_CUTOFF_POINT * highestUtility.Utility);

        // Return a weighted random of the resulting moves
        myNextMove = availableMoves[AIUtil.GetWeightedRandom(moveUtility, highestUtility.Rank, cutoff)];
    }
示例#3
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);
 }
示例#4
0
    protected MoveData.DualUtility GetMoveUtility(MoveData move)
    {
        CardController remainingHand = (move.Card == justDrawn) ? myHand : justDrawn;

        // Get basic move utility
        MoveData.DualUtility moveUtility = move.Card.EstimateMoveUtility(move, remainingHand, MyPerceptor);
        // Get remaining hand value utility
        float marginalHandValueUtility = GetMarginalHandValueUtility(Game, remainingHand.Value);
        // Get the utility of playing the current hand given that the opponent knows it
        float opponentKnowledgeUtility = GetOpponentKnowledgeUtility();

        // Weighted sum of utilities
        MoveData.DualUtility result = new MoveData.DualUtility(moveUtility.Rank, CONSIDERATION_WEIGHT_MOVE_UTILITY * moveUtility.Utility +
                                                               CONSIDERATION_WEIGHT_MARGINAL_HAND_VALUE * marginalHandValueUtility +
                                                               CONSIDERATION_WEIGHT_OPPONENT_KNOWLEDGE * opponentKnowledgeUtility +
                                                               CONSIDERATION_WEIGHT_TACTICAL_DANGER * GetTacticalDanger(move.Target, remainingHand) +
                                                               CONSIDERATION_WEIGHT_STRATEGIC_DANGER * GetStrategicDanger(move.Target) +
                                                               CONSIDERATION_WEIGHT_GRUDGE * GetGrudgeAgainst(move.Target));
        // Drop the rank on new game to avoid knocking out the player immediately
        if (move.Target == Game.HumanPlayer && !MyPerceptor.HumanPlayerHasActed)
        {
            result.Rank = Mathf.Min(result.Rank, MoveData.RANK_ONLY_IF_NECESSARY);
        }
        return(result);
    }
示例#5
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);
    }
示例#6
0
    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);
    }
 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);
 }
示例#8
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 (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);
    }
示例#9
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);
 }