// Card filter: GUARD, when played against anyone but me. public void GuardFilter(int TargetSittingOrder, int GuessedValue) { Debug.Assert(TargetSittingOrder != MySittingOrder); Debug.Assert(GuessedValue >= CardController.VALUE_PRIEST && GuessedValue <= CardController.VALUE_PRINCESS); int CardIndex = GuessedValue - 1; int HiddenHandIndex = GetHiddenHandIndex(TargetSittingOrder); // Update and renormalize the appropriate matrix if (CurrentOpponentCount == 3) { // In case of THREE remaining opponents, update the ThreeOpponentsHandsDistribution ThreeOpponentsHandsDistribution.ClearSlice(HiddenHandIndex, CardIndex); ThreeOpponentsHandsDistribution.Renormalize(); } else if (CurrentOpponentCount == 2) { // In case of TWO remaining opponents, update the TwoOpponentsHandsDistribution TwoOpponentsHandsDistribution.ClearSlice(HiddenHandIndex, CardIndex); TwoOpponentsHandsDistribution.Renormalize(); } else if (CurrentOpponentCount == 1) { // In case of ONE remaining opponents, update the SingleOpponentHandDistribution SingleOpponentHandDistribution[CardIndex] = 0; SingleOpponentHandDistribution.Renormalize(); } }
// On a knock-out, reduce the dimension of the joint distribution array and renormalize it public void KnockOutFilter(int SittingOrder, int DiscardedValue) { Debug.Assert(SittingOrder != MySittingOrder); Debug.Assert(DiscardedValue >= CardController.VALUE_GUARD && DiscardedValue <= CardController.VALUE_PRINCESS); int CardIndex = DiscardedValue - 1; int HiddenHandIndex = GetHiddenHandIndex(SittingOrder); // Account for the discared card AccountForCard(DiscardedValue); // Merge down the current matrix to one dimension lower, taking the slice that corresponds to the knocked-out player's actual hand if (CurrentOpponentCount == 3) { TwoOpponentsHandsDistribution = ThreeOpponentsHandsDistribution.GetSlice(HiddenHandIndex, CardIndex); TwoOpponentsHandsDistribution.Renormalize(); // Also, we move out the knocked-out player's sitting order to the last place of the HiddenHands array AIUtil.ShiftToLast(ref HiddenHands, HiddenHandIndex); } else if (CurrentOpponentCount == 2) { SingleOpponentHandDistribution = TwoOpponentsHandsDistribution.GetSlice(HiddenHandIndex, CardIndex); SingleOpponentHandDistribution.Renormalize(); AIUtil.ShiftToLast(ref HiddenHands, HiddenHandIndex); } // Update player situation CurrentOpponentCount -= 1; PlayerIsKnockedOut[SittingOrder] = true; }
// During the basic opponent turn update, we need to update the joint probability distribution // using just the information about which card he has just played protected void FilterHiddenHand(MoveData TurnData) { Debug.Assert(TurnData.Player.SittingOrder != MySittingOrder); Debug.Assert(!PlayerIsKnockedOut[TurnData.Player.SittingOrder]); Debug.Assert(TurnData.Card.Value >= CardController.VALUE_GUARD && TurnData.Card.Value <= CardController.VALUE_PRINCESS); // Update the hand distribution matrices if (CurrentOpponentCount == 3) { // Prepare a temporary array tempArray3D.Clear(); // Loop through all possible world states and update the tempoprary array accordingly for (int h0 = 0; h0 < CARD_VECTOR_LENGTH; h0++) { for (int h1 = 0; h1 < CARD_VECTOR_LENGTH; h1++) { for (int h2 = 0; h2 < CARD_VECTOR_LENGTH; h2++) { UpdatePartialHandDistribution(TurnData, h0, h1, h2, ref tempArray3D); } } } // Renormalize the temporary array and write it back to the three-player distribution tempArray3D.Renormalize(); ThreeOpponentsHandsDistribution.CopyFrom(tempArray3D); } else if (CurrentOpponentCount == 2) { // Prepare a temporary array tempArray2D.Clear(); // Loop through all possible world states and update the tempoprary array accordingly for (int h0 = 0; h0 < CARD_VECTOR_LENGTH; h0++) { for (int h1 = 0; h1 < CARD_VECTOR_LENGTH; h1++) { UpdatePartialHandDistribution(TurnData, h0, h1, ref tempArray2D); } } // Renormalize the temporary array and write it back to the two-player distribution tempArray2D.Renormalize(); TwoOpponentsHandsDistribution.CopyFrom(tempArray2D); } else if (CurrentOpponentCount == 1) { // Prepare a temporary array tempArray1D.Clear(); // Loop through all possible world states and update the tempoprary array accordingly for (int h0 = 0; h0 < CARD_VECTOR_LENGTH; h0++) { UpdatePartialHandDistribution(TurnData.Card.Value - 1, h0, ref tempArray1D); } // Renormalize the temporary array and write it back to the one-payer distribution tempArray1D.Renormalize(); SingleOpponentHandDistribution.CopyFrom(tempArray1D); } // Finally, recalculate utility arrays RecalculateHandAndDeckdistributions(); }
// The joint probabilities array is good for analysis, but slow for quick access, // so we instead populate smaller, 1D arrays for each hand, as well as for the deck. public void RecalculateHandAndDeckdistributions() { // The computation only works if there ARE any opponents left if (CurrentOpponentCount < 1) { return; } // Precomputation for the aggregated hand distributions update for (int h = 0; h < HiddenHands.Length; h++) { HandDistribution[HiddenHands[h]].Clear(); } DeckDistribution.Clear(); // Precomputations for the deck distribution update Array.Copy(CountUnaccountedForCards, 1, virtualRemainingCards, 0, CARD_VECTOR_LENGTH); // Loop through the first hidden hand for (int h1 = 0; h1 < CARD_VECTOR_LENGTH; h1++) { virtualRemainingCards[h1] -= 1; // If there is only one opponent left, update SingleOpponentHandDistribution if (CurrentOpponentCount < 2) { HandDistribution[HiddenHands[0]][h1] = SingleOpponentHandDistribution[h1]; CumulativeUpdateDeckDistribution(SingleOpponentHandDistribution[h1], virtualRemainingCards, ref DeckDistribution); } else { // Otherwise, enter the second loop for (int h2 = 0; h2 < CARD_VECTOR_LENGTH; h2++) { virtualRemainingCards[h2] -= 1; // If there are two opponents left, update TwoOpponentsHandsDistribution if (CurrentOpponentCount < 3) { HandDistribution[HiddenHands[0]][h1] += TwoOpponentsHandsDistribution[h1, h2]; HandDistribution[HiddenHands[1]][h2] += TwoOpponentsHandsDistribution[h1, h2]; CumulativeUpdateDeckDistribution(TwoOpponentsHandsDistribution[h1, h2], virtualRemainingCards, ref DeckDistribution); } else { // Otherwise, enter the final loop for (int h3 = 0; h3 < CARD_VECTOR_LENGTH; h3++) { HandDistribution[HiddenHands[0]][h1] += ThreeOpponentsHandsDistribution[h1, h2, h3]; HandDistribution[HiddenHands[1]][h2] += ThreeOpponentsHandsDistribution[h1, h2, h3]; HandDistribution[HiddenHands[2]][h3] += ThreeOpponentsHandsDistribution[h1, h2, h3]; // And update ThreeOpponentsHandsDistribution virtualRemainingCards[h3] -= 1; CumulativeUpdateDeckDistribution(ThreeOpponentsHandsDistribution[h1, h2, h3], virtualRemainingCards, ref DeckDistribution); virtualRemainingCards[h3] += 1; } } virtualRemainingCards[h2] += 1; } } virtualRemainingCards[h1] += 1; } // Normalize distributions if (TheDeck.CountCardsLeft > 0) { DeckDistribution.Renormalize(); } for (int h = 0; h < CurrentOpponentCount; h++) { HandDistribution[HiddenHands[h]].Renormalize(); } }