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
Replace "\\" with "\" in a String in C#
C# Member Variable Initialization; Best Practice
Handling Warning for Possible Multiple Enumeration of Ienumerable
Does the C# "Finally" Block Always Execute
Oledbcommand Parameters Order and Priority
Using Global Keyboard Hook (Wh_Keyboard_Ll) in Wpf/C#
ASP.NET MVC 5 Group of Radio Buttons
How to Bind an Enum to a Combobox Control in Wpf
How to Parse JSON Without JSON.Net Library
How to Validate Console Input as Integers
Most Efficient Way to Test Equality of Lambda Expressions
Displayname Attribute from Resources
Fire-And-Forget with Async VS "Old Async Delegate"
Weird Error Upgrading ASP.NET MVC from 4 to 5
Adding Placeholder Text to Textbox