Card Shuffling in C#

C# card shuffle in card deck 52 cards

I'm not sure how your PlayingCard class is coded, but it seems like a large portion of your problems stem from its design. Take this implementation for example:

PlayingCard Class

public class PlayingCard : IComparable<PlayingCard>
{
private int value;
private int suit;
private Bitmap cardImage;

public int Value => value;
public string ValueName => ValueToName(value);

public int Suit => suit;
public string SuitName => SuitToName(suit);

public Bitmap CardImage => cardImage;

public PlayingCard(int value, int suit, Bitmap cardImage)
{
this.value = value;
this.suit = suit;
this.cardImage = cardImage;
}

private string ValueToName(int n)
{
switch (n)
{
case 0:
return "Ace";
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
return (n+1).ToString();
case 10:
return "Jack";
case 11:
return "Queen";
case 12:
return "King";
default:
throw new ArgumentException("Unrecognized card value.");

}
}

private string SuitToName(int s)
{
switch (s)
{
case 0:
return "Clubs";
case 1:
return "Diamonds";
case 2:
return "Spades";
case 3:
return "Hearts";
default:
throw new ArgumentException("Unrecognized card suit");
}
}

public int CompareTo(PlayingCard other)
{
int result = this.Suit.CompareTo(other.Suit);

if (result != 0)
return result;

return this.Value.CompareTo(other.Value);
}

public override string ToString()
{
return String.Format("{0} of {1}", ValueName, SuitName);
}
}

It has all the comparison and value conversion coded within it, so you don't have to worry about creating highly specialized methods to do extraneous conversions. You can use it in a deck like so:

Array Implementation

// How to initialize deck
PlayingCard[] deck = Enumerable.Range(0, 52)
.Select(x => new PlayingCard(x % 13, x / 13, imageListCards[x]))
.ToArray();


// How to shuffle deck
Random r = new Random();
Array.Sort(deck, (a, b) => r.Next(0, 2) == 0 ? -1 : 1);

// How to reset deck
Array.Sort(deck);

// How to display top five cards
pictureBox_Card1.Image = deck[0].CardImage;
pictureBox_Card2.Image = deck[1].CardImage;
pictureBox_Card3.Image = deck[2].CardImage;
pictureBox_Card4.Image = deck[3].CardImage;
pictureBox_Card5.Image = deck[4].CardImage;

List Implementation

// How to initialize deck
List<PlayingCard> deck = Enumerable.Range(0, 52)
.Select(x => new PlayingCard(x % 13, x / 13, imageListCards[x]))
.ToList();


// How to shuffle deck
Random r = new Random();
deck.Sort((a, b) => r.Next(0, 2) == 0 ? -1 : 1);

// How to reset deck
deck.Sort();

// How to display top five cards
pictureBox_Card1.Image = deck[0].CardImage;
pictureBox_Card2.Image = deck[1].CardImage;
pictureBox_Card3.Image = deck[2].CardImage;
pictureBox_Card4.Image = deck[3].CardImage;
pictureBox_Card5.Image = deck[4].CardImage;

EDIT:

Manual Shuffling

If you want to do a shuffle manually, there's a simple algorithm called the Fisher-Yates Shuffle that will do the trick:

private static Random r = new Random();
static void Shuffle<T>(T[] array)
{
for (int i = 0; i < array.Length; i++)
{
int idx = r.Next(i, array.Length);
T temp = array[idx];
array[idx] = array[i];
array[i] = temp;
}
}

(List Implementation)

private static Random r = new Random();
static void Shuffle<T>(List<T> list)
{
for (int i = 0; i < list.Count; i++)
{
int idx = r.Next(i, list.Count);
T temp = list[idx];
list[idx] = list[i];
list[i] = temp;
}
}

Making a shuffle method

The first big problem with your code is that you're creating two instances of Random. The crappy seeding of new Random() means that those instances will most likely return exactly the same sequence.

new Random() seeds using Environment.TickCount, which only changes every few milliseconds. So if you create two instances of Random in quick succession, the time will be the same and thus they output the same sequence.

The proper solution is to create only one instance of Random at the beginning, and use if for all your randomness needs. Just be careful about multi-threading, instances of Random are not thread-safe.

Also note that the upper bound of random.Next is exclusive, so your code will work only on arrays with 11 elements. It's better to use the collection size instead of hardcoding the value.

Another problem with your code is that you didn't implement a proper swap. To swap you need to your swap has two issues: You're using new indices for the second direction, and you don't create a temporary copy in a local variable to avoid reading the overwritten value, instead of original one.

With these issues fixed your code looks like this:

Random random = new Random();//one persistent instance

public void shuffle()
{
for (int i = 0; i < 100000; i++)
{
var i1=random.Next(kaarten.Count);
var i2=random.Next(kaarten.Count);

var temp=kaarten[i1];
karten[i1]=kaarten[i2];
karten[i2]=temp;
}
}

That said, your approach is a bit inefficient, since you iterate 100000 times. The standard shuffling algorithm is a Fisher-Yates shuffle which Jon-Skeet describes at Is using Random and OrderBy a good shuffle algorithm?.

Determining how well a deck is shuffled

One idea for this would be to take the list and populate it with integer values (1 int per card). Make another set of code that takes the int value and converts it into your card value, so you retain which cards belong to which numbers. Randomize the list of integers and then loop through each line in the list and mark how many are within a set of ranges or one range of 1-2 integers from the previous number. At then end, you then set a set of percentage scale for Poor, Good, Great, ect...

This may not be the best solution, but you will be able to have an idea on how spread out the list is.

Shuffling 2D array of cards

Using the Fisher-Yates algorithm:

public static void Shuffle<T>(Random random, T[,] array)
{
int lengthRow = array.GetLength(1);

for (int i = array.Length - 1; i > 0; i--)
{
int i0 = i / lengthRow;
int i1 = i % lengthRow;

int j = random.Next(i + 1);
int j0 = j / lengthRow;
int j1 = j % lengthRow;

T temp = array[i0, i1];
array[i0, i1] = array[j0, j1];
array[j0, j1] = temp;
}
}

Example of use:

CardType[,] cards =
{
{ CardType.Basic, CardType.Basic2, CardType.Basic4 },
{ CardType.Basic, CardType.Basic2, CardType.Basic30 },
{ CardType.Basic, CardType.Basic10, CardType.Basic5 },
{ CardType.Basic, CardType.Basic20, CardType.Basic30 },
};

Random rnd = new Random();
Shuffle(rnd, cards);

Note that you should try to reuse the rnd, and not recreate it!

Note how array.Length is the total Length of the array, X * Y, and how from a "global index" i we split it in a i0, i1 (x, y) by dividing/doing the modulus with the length of a "row" (lengthRow).

How to randomize/shuffle two arrays in the same way in c#

Just create two temporary variables

for (int i = 0; i < cards.Length - 1; i++)
{
int j = rnd.Next(i, cards.Length);

PictureBox tempPictureBox = cards[j]; // One for PictureBox
cards[j] = cards[i];
cards[i] = tempPictureBox;

int tempInt = numValue[j]; // Another one for int
numValue[j] = numValue[i];
numValue[i] = tempInt;
}


Related Topics



Leave a reply



Submit