Click here to Skip to main content
Click here to Skip to main content

rdsCards - A card deck library using RDS

By , 17 Jul 2012
Rate this:
Please Sign up or sign in to vote.

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
 /// <summary>
 /// Initializes a new instance of the <see cref="Card"/> class.
 /// </summary>
 /// <param name="cardvalue">The cardvalue is a 1-character-string describing the card.
 /// The chars for a card are (A)ce, (K)ing, (Q)ueen, (J)ack, (T)en or the number from 9 to 2 or * for a Joker</param>
 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
/// <summary>
/// Card already dealt?
/// </summary>
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();
/// <summary>
/// Initializes a new instance of the <see cref="CardDeck"/> class with no jokers.
/// </summary>
/// <param name="deck">The deck size to generate</param>
public CardDeck(Decksize deck) : this(deck, 0) { }
/// <summary>
/// Initializes a new instance of the <see cref="CardDeck"/> class.
/// </summary>
/// <param name="deck">The deck size to generate</param>
/// <param name="numjokers">The number of jokers in the deck.</param>
public CardDeck(Decksize deck, int numjokers)
{
 // We are using a trick here:
 // The enum cast to int gives us the number of chars of the stringcards string
 // we will use to fill the table.
 // For Rummy and Canasta we do this multiple times
 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mike Barthold
Software Developer (Senior)
Austria Austria
Software Developer since the late 80's, grew up in the good old DOS-Era, switched to windows with Win95 and now doing .net since early 2002 (beta).
Long year c# experience in entertainment software, game programming, directX and XNA as well as SQLServer (DBA, Modelling, Optimizing, Replication, etc) and Oracle Databases in Enterprise environments.

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 17 Jul 2012
Article Copyright 2012 by Mike Barthold
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid