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 enum
s 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 fields 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 no 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 above 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