如何避免栈溢出异常同时维持抽三张唯一角斗士卡的功能?
Hey there! Let's break down what's causing your stack overflow and inefficiency issues, then fix them with a cleaner, more robust approach.
First, let's call out the core problems in your current code:
- Multiple
Randominstances: Creating newRandomobjects in quick succession uses the same system clock seed, leading to nearly identical random numbers—this is why you're getting duplicates constantly, triggering endless recursive calls. - Recursive redraws: Every time you hit a duplicate or used card, you recursively call the entire draw method. When available cards are scarce, this can spiral into infinite recursion and blow up your stack memory.
- Redundant condition checks: 33 nearly identical
ifblocks are not only a nightmare to maintain, they're also inefficient to run every time you draw.
Here's the optimized solution, step by step:
Step 1: Use a single Random instance
Initialize one Random at the class level instead of creating new ones for each card. This ensures truly random number generation and cuts down on duplicate draws.
Step 2: Track used cards with a collection
Replace all those gladXBoard/gladXDiscard checks with a HashSet<int> to track which cards are already in play or discarded. This makes checking card availability an O(1) operation and eliminates all those redundant ifs.
Step 3: Draw directly from available cards (no recursion!)
Instead of drawing random cards and then checking for duplicates, pull directly from a pool of available cards. This eliminates the need for redraw checks entirely and removes the risk of stack overflow.
Optimized Code Example
// Class-level variables to avoid repeated initialization private Random _gladCardRandom = new Random(); private HashSet<int> _usedGladiatorCards = new HashSet<int>(); // Helper method to mark a card as used (call this when a card goes to the board or discard pile) public void MarkGladiatorCardAsUsed(int cardId) { _usedGladiatorCards.Add(cardId); } // Shuffle logic: Reset the available card pool (adjust based on your game's discard rules) public void ShuffleGladCards() { _usedGladiatorCards.Clear(); // If you have a discard pile, add those cards back to the available pool here } public void DrawThreeUniqueGladiatorCards() { // Check if we have enough available cards; shuffle if we don't int availableCardCount = 33 - _usedGladiatorCards.Count; if (availableCardCount < 3) { ShuffleGladCards(); availableCardCount = 33; } // Generate a list of all available (unused) cards List<int> availableCards = Enumerable.Range(1, 33) .Where(id => !_usedGladiatorCards.Contains(id)) .ToList(); // Pick 3 unique random cards from the available pool List<int> drawnCards = new List<int>(); for (int i = 0; i < 3; i++) { int randomIndex = _gladCardRandom.Next(availableCards.Count); int selectedCard = availableCards[randomIndex]; drawnCards.Add(selectedCard); availableCards.RemoveAt(randomIndex); // Remove the card to avoid duplicates } // Assign to your existing variables drawnGladCard1 = drawnCards[0]; drawnGladCard2 = drawnCards[1]; drawnGladCard3 = drawnCards[2]; // Mark the drawn cards as used (adjust if your game doesn't mark drawn cards immediately) foreach (int cardId in drawnCards) { _usedGladiatorCards.Add(cardId); } }
Even More Efficient Version (Using Shuffling)
If your game allows shuffling the entire deck and drawing in order, use the Fisher-Yates shuffle algorithm for even better performance:
private Random _gladCardRandom = new Random(); private List<int> _availableGladCards = Enumerable.Range(1, 33).ToList(); public void ShuffleGladCards() { // Fisher-Yates shuffle: Efficiently randomizes the list for true randomness int n = _availableGladCards.Count; while (n > 1) { n--; int k = _gladCardRandom.Next(n + 1); // Swap elements (C# 7.0+ tuple swap syntax) (_availableGladCards[k], _availableGladCards[n]) = (_availableGladCards[n], _availableGladCards[k]); } } public void DrawThreeUniqueGladiatorCards() { // Reset and shuffle if we don't have enough cards left if (_availableGladCards.Count < 3) { _availableGladCards = Enumerable.Range(1, 33).ToList(); ShuffleGladCards(); } // Draw the first 3 cards (already randomized from shuffling) drawnGladCard1 = _availableGladCards[0]; drawnGladCard2 = _availableGladCards[1]; drawnGladCard3 = _availableGladCards[2]; // Remove the drawn cards from the available pool _availableGladCards.RemoveRange(0, 3); }
Key Improvements
- No more stack overflow: We've eliminated recursion entirely, so there's no risk of stack depth issues.
- Faster performance: Drawing directly from an available pool avoids redundant checks, and collections make availability checks instant.
- Cleaner, maintainable code: No more 33 identical
ifblocks—managing used cards is done with a single collection. - Truly random draws: Using one
Randominstance ensures your draws are actually random, reducing duplicate chances from the start.
内容的提问来源于stack exchange,提问作者Jae Bolton

