Card Shuffling in C#

2019-01-01 09:49发布

问题:

I am trying to write a code for a project that lists the contents of a deck of cards, asks how much times the person wants to shuffle the deck, and then shuffles them. It has to use a method to create two random integers using the System.Random class.

These are my classes:

Program.cs:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Deck mydeck = new Deck();
            foreach (Card c in mydeck.Cards)
            {
                Console.WriteLine(c);
            }
            Console.WriteLine(\"How Many Times Do You Want To Shuffle?\");

        }
    }
}

Deck.cs:

namespace ConsoleApplication1
{
    class Deck
    {    
        Card[] cards = new Card[52];
        string[] numbers = new string[] { \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"J\", \"Q\", \"K\" };
        public Deck()
        {
            int i = 0;
            foreach(string s in numbers)
            {
                cards[i] = new Card(Suits.Clubs, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Spades, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Hearts, s);
                i++;

            }
            foreach (string s in numbers)
            {
                cards[i] = new Card(Suits.Diamonds, s);
                i++;

            }
        }

        public Card[] Cards
        {
            get
            {
                return cards;


            }
        }
    }  
}

Enums.cs:

namespace ConsoleApplication1
{        
    enum Suits 
    {
        Hearts,
        Diamonds,
        Spades,
        Clubs
    }
}

Card.cs:

namespace ConsoleApplication1
{
    class Card
    {
        protected Suits suit;
        protected string cardvalue;
        public Card()
        {
        }
        public Card(Suits suit2, string cardvalue2)
        {
            suit = suit2;
            cardvalue = cardvalue2;
        }
        public override string ToString()
        {
            return string.Format(\"{0} of {1}\", cardvalue, suit);
        }
    }
 }

Please tell me how to make the cards shuffle as much as the person wants and then list the shuffled cards.

回答1:

Use Fisher-Yates shuffle.

Your C# code should look something like this:

static public class FisherYates
{
    static Random r = new Random();
    //  Based on Java code from wikipedia:
    //  http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
    static public void Shuffle(int[] deck)
    {
        for (int n = deck.Length - 1; n > 0; --n)
        {
            int k = r.Next(n+1);
            int temp = deck[n];
            deck[n] = deck[k];
            deck[k] = temp;
        }
    }
}


回答2:

Shuffling a deck of cards is something that seems trivial at first, but usually the algorithm that most people come up with is incorrect.

Jeff Atwood (Coding Horror) wrote a few very good articles on the subject:

http://www.codinghorror.com/blog/archives/001008.html

http://www.codinghorror.com/blog/archives/001015.html

(especially the second one is a must-read)



回答3:

I think this is a case where you may just be getting too caught up in the abstraction.

Shuffling a deck of cards in software is a matter of providing the deck to the user in a random order. This doesn\'t actually require you to shuffle them ahead of time.

Init your deck. (I typically use a number from 1 to 52 to represent the card and mathmatically compute which card is.)

  1. Deal a card by using a random number generator to pick a card out of the Deck of availible cards.
  2. Swap that card with the one at the end of the deck.
  3. Decrement a counter pointing to the end of the deck, to remove that card from the deck.
  4. Goto step 1 until you are done drawing cards.

Edit: And generally speaking, if you have a good random number generator nothing is gained by \"Shuffling\" it multiple times.

This should be possible using the data structures you\'ve shown. You just need to add a \"Draw\" method, and member variable to keep track of the end of the deck. If you are hell bent on actually performing the \"shuffle\" ahead of time, then A your professor\'s a jerk, B anytime you draw 52 cards the deck will be shuffled. Once you\'ve draw all cards, you need to provide a \"DeckEmpty\" method, and method to reset the End of Deck to include all cards again.



回答4:

to correcly shuffle a deck you should NOT ONLY use the Random class, the seed is only 2^32 which means your Random object can give you only 2^32 (supposed) different order where there is 52! (factorial 52) way of agencing a real life deck.

i\'m using 2 guid to create 32bytes of random data -> 8 seed of 4bytes and i shuffle the cards with thoses 8 different seeds

then by seed i get a certain number of cards [5,5,6,6,6,7,8,9]

here is the code i use

    public void Shuffle(Guid guid1, Guid guid2)
    {
        int[] cardsToGet = new int[] { 5, 5, 6, 6, 6, 7, 8, 9 };
        byte[] b1 = guid1.ToByteArray();
        byte[] b2 = guid2.ToByteArray();

        byte[] all = new byte[b1.Length + b2.Length];
        Array.Copy(b1, all, b1.Length);
        Array.Copy(b2, 0, all, b1.Length, b2.Length);

        List<Card> cards = new List<Card>(this);
        Clear();

        for (int c = 0; c < cardsToGet.Length; c++)
        {
            int seed = BitConverter.ToInt32(all, c * 4);
            Random random = new Random(seed);
            for (int d = 0; d < cardsToGet[c]; d++)
            {
                int index = random.Next(cards.Count);
                Add(cards[index]);
                cards.RemoveAt(index);
            }
        }
    }


回答5:

Your Shuffle might work, but it\'s not really efficient and not lifelike. You should try this way:

//The shuffle goes like this: you take a portion of the deck, then put them in random places
private void Shuffle()
{
 int length = DeckofCards.Count;
 int level = 20; //number of shuffle iterations

 List<Card> Shuffleing; //the part of the deck were putting back
 Random rnd = new Random();
 int PickedCount, BackPortion; //the last used random number

 for (int _i = 0; _i < level; _i++)
 {
  PickedCount = rnd.Next(10, 30); //number of cards we pick out
  Shuffleing = DeckofCards.GetRange(0, PickedCount);
  DeckofCards.RemoveRange(0, PickedCount);

  while (Shuffleing.Count != 0)
  {
   PickedCount = rnd.Next(10, DeckofCards.Count - 1); //where we place a range of cards
   BackPortion = rnd.Next(1, Shuffleing.Count / 3 + 1); //the number of cards we but back in one step
   DeckofCards.InsertRange(PickedCount, Shuffleing.GetRange(0, BackPortion)); //instering a range of cards
   Shuffleing.RemoveRange(0, BackPortion); //we remove what we just placed back
  }
 }
}

This way you might get a more lifelike shuffle with less iterations



回答6:

The shuffling should work in this manner:

You take two random cards in the deck (the index of the card in the deck is the random numbers) And swap positions of the two cards. For instance take card at index 2 and card at index 9 and have them change place.

And that can be repeated a certain number of times.

The algorithm should look something like this:

int firstNum = rnd.Next(52);
int secondNum = rnd.Next(52);

Card tempCard = MyCards[firstNum];
MyCards[firstNum] = MyCards[secondNum];
MyCards[secondNum] = tempCard;


回答7:

Overall I\'d say look at each deck as an object which contains an array of Card objects, which each Card object each contains a value and suite int property, which can be applied to an Enum of values and suites to gather the named version as per the Type of Deck you are using. (This would allow this bit of code to be more versatile and allow easier value comparisons 3 < 11 (jack) !~) Your style will work for a school project, I am just getting OCD with it!

class Card
{
    public int value
    { get; set; }

    public int suite
    { get; set; }
}


abstract class Deck
{
    public Card[] cards
    { get; set; }

    public void ShuffleCards(int timesToShuffle)
    {
        Card temp;
        Random random = new Random();
         // int timesToShuffle = random.Next(300, 600); #Had it setup for random shuffle
        int cardToShuffle1, cardToShuffle2; 

        for (int x = 0; x < timesToShuffle; x++)
        {
            cardToShuffle1 = random.Next(this.cards.Length);
            cardToShuffle2 = random.Next(this.cards.Length);
            temp = this.cards[cardToShuffle1];

            this.cards[cardToShuffle1] = this.cards[cardToShuffle2];
            this.cards[cardToShuffle2] = temp;
        }
    }
}

That is assuming that you used a base Deck class, then inherit it to the type of deck you want (making it so you can apply this same code to Uno decks or what ever.) Code for normal type of deck class.

class NormalDeck : Deck
{
    // This would go in the NormalGame class to apply the enumerators to the values as a cipher.
    // Need int values for logic reasons (easier to work with numbers than J or K !!!
    // Also allows for most other methods to work with other deck<Type> (ex: Uno, Go Fish, Normal cards)
    public enum Suites
    {
        Hearts,
        Diamonds,
        Spades,
        Clover
    };

    // Same comment as above. 
    public enum Values
    { Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King };

    public void NewNormalDeck()
    {
        // Clear the deck of cards
        if (this.cards != null)
        {
            Array.Clear(this.cards, 0, this.cards.Length);
        }

        //Set Value to length of Normal deck of Cards without Jokers 
        cards = new Card[52];

        // to keep count of which card we are.  
        int curNumofCards = 0;

        // Cycle through all of the suites listed in \"suites\" then all the values of     that suite
        for (int x = 0; x < Enum.GetValues(typeof(Suites)).GetLength(0); x++)
        {
            for (int y = 0; y < Enum.GetValues(typeof(Values)).GetLength(0); y++)
            {
                Card newCard = new Card();
                newCard.suite = x;
                newCard.value = y;
                this.cards[curNumofCards] = newCard;
                curNumofCards++;
            }
        }
    }
}


回答8:

I made a program that contains 7 cards, then shuffle and I hope to take in order to help them.

class Program {

static void Main(string[] args)
{
    Random random = new Random(); 
    var cards = new List<string>();
    //CARDS VECRTOR
    String[] listas = new String[] { \"Card 1\", \"Card 2\", \"Card 3\", \"Card 4\", \"Card 5\", \"Card 6\", \"Card 7\"};


    for (int i = 0; i<= cards.Count; i++)
    {

        int number = random.Next(0, 7); //Random number 0--->7


        for (int j = 0; j <=6; j++)
        {
            if (cards.Contains(listas[number])) // NO REPEAT SHUFFLE
            {

                number = random.Next(0, 7); //AGAIN RANDOM

            }
            else
            {
                cards.Add(listas[number]); //ADD CARD
            }
        }

    }

    Console.WriteLine(\" LIST CARDS\");

    foreach (var card in cards)
    {
        Console.Write(card + \" ,\");


    }

    Console.WriteLine(\"Total Cards: \"+cards.Count);

    //REMOVE

    for (int k = 0; k <=6; k++)
    {
       // salmons.RemoveAt(k);
        Console.WriteLine(\"I take the card: \"+cards.ElementAt(k));
        cards.RemoveAt(k); //REMOVE CARD
        cards.Insert(k,\"Card Taken\"); //REPLACE INDEX
        foreach (var card in cards)
        {
            Console.Write(card + \" \" + \"\\n\");

        }


    }


    Console.Read(); //just pause

}

}



回答9:

static void Shuffle(List<int> cards)
    {
        Console.WriteLine(\"\");
        Console.WriteLine(\"Shuffling\");
        Console.WriteLine(\"---------\");

        cards = cards.OrderBy(x => Guid.NewGuid()).ToList();

        foreach (var card in cards)
        {
            Console.WriteLine(card.ToString());
        }
    }