15

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.

Jacob
  • 77,566
  • 24
  • 149
  • 228

9 Answers9

53

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;
        }
    }
}
hughdbrown
  • 47,733
  • 20
  • 85
  • 108
  • 2
    FisherYates? Hmm, I didn't know there was a name for it. The first time I implement that was on an 8-bit Atari in Basic! I think I was in 8th grade. – NoMoreZealots Jul 19 '09 at 20:06
  • i get these errors when i use it in my Deck.cs class Every place it says Deck (yes I did capitalize it to match my code) it says it is a 'type but is used as a 'variable'. and for when it says Deck.Length it says that My application does not have a definition for 'length' –  Jul 20 '09 at 05:28
  • 1
    You shouldn't capitalize the variable name 'deck' to match your class name. If you do, yes, you will get an error because you will be confusing a variable with a class. Others have told you what to do: - create an array of 52 integers [0..51] inclusive - shuffle the integers - dealing the cards is removing the top int from the array - cards are represented as strings by translating integer values to card+suit Good luck. – hughdbrown Jul 20 '09 at 13:33
  • I know this is old, but still current. Wouldnt it be better to use the Crypto library instead for better randomization instead? – Piotr Kula Mar 27 '14 at 20:18
  • @ppumkin Better how? -- faster, more random, easier to understand, Microsoft recommended practice (for another reason), something else? If you think this is a better approach, make the case for that. – hughdbrown Apr 13 '14 at 16:21
  • @hughdbrown I did write in my comment, better for randomization? I know its slower to initialise but I did a test for 1million shuffles took 4 seconds on my dual core AMD. The algorithm is good but some tests show the distribution using the Random class selects very similar patters during shuffling -Low variance, while the Crypto has a much higher variance. – Piotr Kula Apr 13 '14 at 18:11
  • 1
    I am not an expert in these matters, but I think the approach is to estimate the number of possible outcomes, state how many bits of randomness that many outcomes requires, and then figure out if your method has that many bits of randomness or more. There are 52! random decks. I believe that is 225 bits of randomness: >>> math.log(factorial(52), 2) 225.5810031237028 So how many bits of entropy does the MS Crypto library have? A quick google does not give me the answer. – hughdbrown Aug 25 '14 at 16:31
  • @hughdbrown As long as the seed is big enough to be unguessable and the PRNG algorithm is secure it doesn't matter if some outputs are unreachable since you can't know which ones are reachable and which not. – CodesInChaos Feb 27 '15 at 11:46
  • do a better job naming your variables so that novices, like me, can more easily follow – MikeT Jun 05 '15 at 06:27
17

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)

Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
  • This is not what im looking for. I am looking for someone to show me how to shuffle cards through the code i am using –  Jul 19 '09 at 19:43
  • 8
    Are you looking for someone to write the code for you or tell you how to do it? (Assuming this is an assignment, you'll get more out of the class if you write it yourself. If you don't do the assignment you usually fail the test.) – NoMoreZealots Jul 19 '09 at 23:59
5

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.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
NoMoreZealots
  • 5,274
  • 7
  • 39
  • 56
3

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);
            }
        }
    }
  • Would the cryptographic class solve the pseudo random number generator issuses? – Piotr Kula Mar 27 '14 at 20:24
  • _"your Random object can give you only 2^32 (supposed) different order"_ -- actually, it's "worse" than that, because a negative seed is never used. If one passes a negative seed to the constructor, the absolute value is used. So you "only" have 2^31 possible sequences. Still, I put "worse" and "only" in quotes because, for most purposes, 2 billion possible sequences is plenty. – Peter Duniho Feb 13 '17 at 04:37
2

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

Lyozsi
  • 21
  • 1
0

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++;
            }
        }
    }
}
0

Use an Enum within your Card Class.

public class Card
{
    public Suit Suit { get; set; }
    public CardValue CardValue { get; set; }
}

public enum CardValue 
{
    Ace = 1,
    Two = 2,
    Three = 3,
    Four = 4,
    Five = 5,
    Six = 6,
    Seven = 7,
    Eight = 8,
    Nine = 9,
    Ten = 10,
    Jack = 11,
    Queen = 12,
    King = 13
}

public enum Suit
{ 
    Clubs,
    Hearts,
    Spades,
    Dimonds
}

Once you have that you can create a PlayersHand Class

public class PlayersHand
{
    public List<Card> Cards { get; set; }
}

Now that you have both the classes created and supported with the Enum, just give each player a hand from the deck, Which is just a List of Cards.

List<Card> deck = new List<Card>();

        // 52 cards in a deck, 13 cards per suit.
        foreach (Suit suit in Enum.GetValues(typeof(Suit))) 
        {
            foreach (CardValue cardValue in Enum.GetValues(typeof(CardValue))) 
            {
                Card card = new Card
                {
                    Suit = suit,
                    CardValue = cardValue
                };
                deck.Add(card);
            }
        }

// Shuffle using random replace.
        for (var i = deck.Count-1; i > 0; i--)
        {
            Random rnd = new Random();
            var j = rnd.Next(0, i);
            var temp = deck[i];
            deck[i] = deck[j];
            deck[j] = temp;
        }
            
        // THESE ARE THE SETTINGS
        int NumberOfPlayers = 4;
        int NumberOfCardsPerPlayer = 5;

        // NEW UP A LIST OF PLAYER HANDS
        List<PlayersHand> hands = new List<PlayersHand>();
        for (var i = 1; i <= NumberOfPlayers; i++)
        {
            PlayersHand newHand = new PlayersHand()
            {
                Cards = new List<Card>()
            };
            hands.Add(newHand);
        }

        // HERE IS THE TRICK TO DEALING OUT THE TOP CARD
        int deal = 0;

        // GOING AROUND THE TABLE 5 TIMES BECAUSE EACH PLAYER HAS 5 CARDS
        for (var i = 0; i < NumberOfCardsPerPlayer ; i++)
        {
            // FOR EACH PLAYER GET THE TOP CARD USING THE 'DEAL' INT
            for (var p = 0; p < NumberOfPlayers; p++) 
            {
                var player = hands[p];
                player.Cards.Add(deck[deal]);
                // INCREMENT DEAL
                deal++;
            }
        }

This approach works for me.

-1

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;
thkala
  • 84,049
  • 23
  • 157
  • 201
  • 1
    This does not perform a fair shuffle. – dkarp Jan 06 '11 at 18:10
  • Why not?? Assuming rnd.Next functions gives totally random values between 1 and 52 – Rohit Feb 19 '13 at 02:29
  • How long do you have to shuffle to get 'random enough'? – Christopher Stevenson Sep 27 '13 at 00:58
  • It does perform a fair shuffle: Any element of a finite permutation group (here a shuffle is an element of the permutation group of 52 elements) can be decomposed into a product of transpositions (a permutation only switching two elements and fixing the rest) [link](http://en.wikipedia.org/wiki/Parity_of_a_permutation) – EluciusFTW Feb 19 '14 at 09:52
-1
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());
        }
    }
Steve
  • 1