I am trying to meet these requirements:
Cards should be drawn in a pseudo-random order, Hearts and the Ace of Spades will be considered special cards with increased chances of being drawn. Each Heart Suit card should be 2x more likely to be drawn than a non-special card. The Ace of Spades should be 3x more likely to be drawn than a non-special card.
The drawing odds above refer to the likelihood of a card being the next card drawn. Exactly one card of each suit and numeric value should be seen exactly one time during a run through the deck.
I at first thought to add more cards of the special type to the deck to increase odds. After looking at the requirements, the deck would be more than 52 cards and show more than exactly one card each run. Am I understanding the requirements correctly?
Interpreting it another way, I would shift the special cards towards the beginning of the List so that the chance of drawing the NEXT card was high. But what makes one card 3x more likely and another 2x more likely?
Here is what I have that is relevant so far:
public void ShuffleDeck()
{
for (int i = deck.Count - 1; i > 0; i--)
{
int j = Random.Range(0, i + 1);
SwapCards(i, j);
}
}
private void SwapCards(int i, int j)
{
Card temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
public Card DrawCard()
{
if (deck.Count > 0)
{
Card drawnCard = deck[0];
deck.RemoveAt(0);
drawnCards.Add(drawnCard);
return drawnCard;
}
return null;
}
public void ApplyDrawingOdds(int oddsMultiplier, string specialSuit, string specialRank = "")
{
// search for special cards in the deck
List<Card> specialCards = deck.FindAll(card => card.Suit == specialSuit && (specialRank == "" || card.Rank == specialRank));
// loop through special cards
foreach (Card specialCard in specialCards)
{
// retrieve index in deck
int index = deck.IndexOf(specialCard);
// calculate the target index for swapping based on oddsMultiplier
int targetIndex = System.Math.Max(0, index - oddsMultiplier + 1);
// swap current special card with other cards in deck
for (int k = 0; k < oddsMultiplier; k++)
{
// move towards beginning of deck
SwapCards(index, targetIndex + k);
}
}
}
public void ResetDeck()
{
deck.AddRange(drawnCards);
drawnCards.Clear();
ShuffleDeck();
}
There might be more elegant single time processed solutions ... but one simple yet maybe a bit more expensive approach would be to ad-hoc while drawing a card generate a weighted list and pick a random card from that.
Assuming you don't draw a card every frame the performance impact for an only maximum 52 element deck should be quite neglectable.
You wouldn't need to shuffle your deck up front at all ;)
Alternatively you could as said also generate the full deck only once, draw a random and then simply remove all matches from the weighted deck
Note that of course that logically either way this also means that the heart and ace of spades will be drawn earlier ... and then no longer be available for the rest of drawing ;)