Introduction
This article uses the Random Distribution System (RDS) library I published the last days here on Code Project. You can look it up here: Part I and Part II.
I thought about another use case for the library and came up with the idea of creating a simple card deck class with a shuffle and deal method.
Supported are standard deck sizes (at least what is considered "standard" here in the middle of europe ... ), which are:
- Ace to Ten 20 card deck
- Ace to Seven 32 card deck
- Ace to Deuce Standard 52 card deck (Poker deck)
- Rummy (104 cards + jokers)
- Canasta (156 cards + jokers)
There are two enums in this library, one for the decks and one for the suits of the cards ("Suit.None" applies to Jokers).
public enum Decksize
{
AceToTen20Cards = 5,
AceToSeven32Cards = 8,
Poker52Cards = 13,
Rummy104Cards = 26,
Canasta156Cards = 39
}The strange numbers in this enum are just the number of characters I need to take from my cardstring "AKQJT98765432*" when creating the RDSTable object, we'll discuss this in a moment when I show you how the table is filled with the cards generated.
public enum Suit
{
None = 0,
Spades = 1,
Hearts = 2,
Diamonds = 3,
Clubs = 4
}The library contains only of two classes: Card and CardDeck. A CardDeck consists of Cards of course,
and a Card is a RDSObject, and that allows me to add them directly to my RDSTable object created inside my CardDeck.
A good benefit of making this via RDS is, that I do not need a Shuffle-Algorythm, as RDS picks cards at random from the deck (which is basically the same as shuffling
them at random and then picking them one-by-one from the top of our virtual Deck).
The Card class
This class is really simple, it holds the card's value string,
where A is an Ace, K is a King and so on, down to deuces and the * representing a Joker.
A card's suit is displayed by the Suit enum, where Jokers hold
Suit.None as their suit.
The important parts of this class are the overriden RDS methods:
First, there's an internal boolean IsDealt to keep track
of the cards, whether it has already been dealt or not.
The overrides of the Hit event and the PreResultEvaluation then keep track
of those field and set the card rdsEnabled or not.
public class Card : RDSObject
{
#region CONSTRUCTOR
public Card(string cardvalue, Suit suit)
{
if (CardDeck.stringcards.Contains(cardvalue))
{
mvalue = cardvalue;
if (cardvalue != "*")
msuit = suit;
else
msuit = Suit.None;
}
else
throw new InvalidOperationException("\"" + cardvalue + "\" is not a valid card value character.");
}
#endregion
...
...
...
This is the constructor of the card class, taking a value and a suit parameter. Nothing really complicated.
But this part here is the one where we utilize RDS and its events during result evaluation.
#region INTERNAL MEMBERS AND OVERRIDES FOR THE CARD DECK
internal bool IsDealt = false;
public override void OnRDSPreResultEvaluation(EventArgs e)
{
base.OnRDSPreResultEvaluation(e);
rdsEnabled = !IsDealt;
}
public override void OnRDSHit(EventArgs e)
{
base.OnRDSHit(e);
IsDealt = true;
rdsEnabled = false;
}
#endregion
The boolean IsDealt is set to true in the Hit override which prevents the card from dropping again, because in the PreResultEvaluation we set rdsEnabled = !IsDealt and the card gets disabled.
The funny part here is that the Shuffle() method of the Card Deck class does not more than resetting all the IsDealt flags in the deck back to false.
The Card Deck
A CardDeck is constructed with a DeckSize and a desired number of Jokers that shall be put into the deck.
A loop then sets up the cards needed in a private RDSTable.
#region STATIC STRINGS AND CONSTRUCTORS
internal static string stringsuits = " ????";
internal static string stringcards = "AKQJT98765432*";
private RDSTable mdeck = new RDSTable();
public CardDeck(Decksize deck) : this(deck, 0) { }
public CardDeck(Decksize deck, int numjokers)
{
int ccount = (int)deck;
int multiples = (ccount <= 13 ? 1 : ccount / 13);
int maxcount = (ccount <= 13 ? ccount : 13);
for (int multi = 0; multi < multiples; multi++)
{
for (int s = 1; s <= 4; s++)
{
for (int c = 0; c < maxcount; c++)
{
mdeck.AddEntry(new Card(stringcards[c].ToString(), (Suit)s), 1);
}
}
}
for (int i = 0; i < numjokers; i++)
mdeck.AddEntry(new Card("*", Suit.None), 1);
}
#endregion
The Shuffle() method, as mentioned aboved just resets all the cards in the Deck and the Deal() method deals you any number of cards.
public void Shuffle()
{
foreach (Card c in mdeck.rdsContents)
c.IsDealt = false;
}
public IEnumerable<Card> Deal(int numcards)
{
mdeck.rdsCount = numcards;
return mdeck.rdsResult.Cast<Card>().ToList();
}
The conversion with .ToList() of the result in the Deal method is to avoid re-evaluation of the result in case you run more than one foreach-loop over the result
of Deal().
I made a very small Console Application and let me deal some cards to show you how this runs.
This is the code of the application:
class Program
{
static void Main(string[] args)
{
CardDeck cd = new CardDeck(Decksize.AceToTen20Cards);
Console.WriteLine("20 card deck: Dealing 4 x 5 cards");
for (int i = 0; i < 4; i++)
{
foreach (Card c in cd.Deal(5))
Console.Write(c.ShortDisplayString + " ");
Console.WriteLine();
}
Console.WriteLine("\r\n\r\n");
cd = new CardDeck(Decksize.Poker52Cards);
Console.WriteLine("Poker Deck: Dealing 10x Hold'em hands (2 cards)");
for (int i = 0; i < 10; i++)
{
foreach (Card c in cd.Deal(2))
Console.Write(c.ShortDisplayString + " ");
Console.WriteLine();
}
Console.Write("\r\n\r\nDealing Flop : ");
foreach (Card c in cd.Deal(3))
Console.Write(c.ShortDisplayString + " ");
Console.WriteLine();
Console.Write("\r\n\r\nDealing Turn : ");
Console.WriteLine(cd.Deal(1).First().ShortDisplayString);
Console.Write("\r\n\r\nDealing River: ");
Console.WriteLine(cd.Deal(1).First().ShortDisplayString);
Console.ReadKey();
}
}And here are some of the outputs to show:
20 card deck: Dealing 4 x 5 cards
T♥ T♦ T♠ K♥ T♣
J♠ A♦ A♥ K♣ J♥
Q♦ Q♠ K♦ K♠ J♣
J♦ A♣ A♠ Q♣ Q♥
Poker Deck: Dealing 10x Holdem hands (2 cards)
T♦ J♦
2♣ 6♦
K♣ 4♠
5♦ 7♠
Q♣ 4♦
5♥ 8♣
T♣ A♦
8♦ 2♠
8♥ 9♠
K♠ 4♥
Dealing Flop : 3♠ Q♠ J♠
Dealing Turn : Q♦
Dealing River: 6♣
OK, this was another short example of what you can do with RDS and there may be a time when you do some card (or: "stack-of-things")
simulation and then maybe you will remember this one here and pick up some ideas.
Have fun with it!
History
2012-07-17: First published.