Blackjack--A Real-World OOD Example



Click here for a larger image.

When programmers start creating object-oriented designs, several questions pop up consistently:

  • How do I know what objects to create?
  • What properties should my objects have?
  • What methods do I need to create?
  • How do I know when to overload an operator?
  • How do I structure my classes for inheritance?
  • Etc.

The Objects

Let's take a look at a real-world example and a fun one as well. The game Blackjack lends itself well to object-oriented design because it has physical objects that can be modeled in object-oriented code; for example, players, a dealer, cards, and so on.

These objects have relationships to one another, as well. Players have hands that have cards. The dealer also has a hand that has cards. And there's a shoe from which the cards are dealt into the hands.

  public class Player
  public class Dealer
  public class Hand
  public class Card
  public class Shoe        // A shoe is just many decks of cards,
                           // usually 6 in Las Vegas

For our Blackjack game, we're going to have computer-controlled players as well as human ones. For that, we'll have to have a strategy that the computer players use. So, we can create another object, albeit not a physical one, called Strategy that takes some input and gives advice on what move to make. The Strategy object is going to belong to the Player objects and each player will need an array of Hand objects (players can split pairs so they may have more than one hand).

  public class Player {
    private Strategy plyrStrategy;
    private Hand[] hands;
  .

A hand is just an array of Card objects:

  public class Hand {
    private Card[] cards;
  .

A shoe is also just an array of Card objects:

  public class Shoe {
    private Card[] cards;
  .

Now when we deal the cards, we just go around the table taking cards from the shoe object and adding them to the hand objects for each of the players and the dealer.

for( int k=0; k<2; k++ )
{
  foreach( Player player in players )
  {
    player.GetHands()[0].Add( shoe.Next() );
  }
  dealer.Hand.Add( shoe.Next() );

}

Inheriting Interfaces

When a player splits a pair of aces, each ace receives only one more card:

  if( CurrentPlayer.CurrentHand[0].FaceValue ==
      Card.CardType.Ace )
  {

    NextCard();
    NextHand();
    NextPlayer();

  }

Nice code, huh? Well, that's because there is a lot of supporting code underneath this, particularly to implement the line:

  if(CurrentPlayer.CurrentHand[0].FaceValue ==
     Card.CardType.Ace )

How does the compiler know what CurrentHand[0] means? To use this kind of syntax, we must implement the IList interface. This is the same interface used by the ArrayList class and others that you might be familiar with. This is easily done by changing our class declaration slightly:

  public class Hand : IList

Now there's a little more work to do. When you inherit an interface, you must provide the implementation for all the methods of that interface. For IList, we need to add:

    IsFixedSize
    IsReadOnly
    Add
    Clear
    Contains
    IndexOf
    Insert
    Remove
    RemoveAt

But the most important method to implement is called Item, and it looks like this:

Object IList.this[int index]
{
    get { return cards[index]; }
    set { cards[index] = (Card)value; }
}

This allows us to use array syntax such as CurrentHand[0], which really means nothing until we tell the compiler that this means the card is at position 0 in the array of cards in the hand. Without implementing IList, we would probably have to write something like CurrentHand.GetCard(0), which isn't nearly as cool!

What Methods Do I Create?

Notice that the players and the dealer are responsible for drawing their own hands. This makes it convenient to add code to the form's paint event like this:

dealer.DrawHand( drawingSurface, showDealerDownCard );

foreach( Player player in players )
{
  player.DrawHands( drawingSurface );
}

The players and dealer then loop through each hand, asking the cards to draw themselves:

foreach( Hand hand in hands )
{
  foreach( Card card in hand )
  {
    card.Draw( drawingSurface );
  }
}

Sometimes it's easier to envision the code you want to write and then model your objects to allow it.

Summary

Take a look at the code for this article. This is a full-featured Blackjack game with strategies and graphics and even card counting. But don't let it intimidate you. It really just boils down to the few objects outlined above with a lot of fancy code added to make the game more appealing.

Going back to the design of the objects, you might be wondering why the Dealer and the Player objects don't inherit from some common object. Well, you could do that, but I felt the dealer and the players didn't have enough in common to justify it. The dealer can only have one hand, has no bank, no bet, no strategy, no card counting. This will have to be a judgment call on your part and that's why they pay you the big bucks.

You might also wonder where the Deck object is. Shouldn't the Shoe be composed of many Deck objects which are composed of many Card objects? That may be the case in the real world, but this is an example of where the real world and OOD might better part ways. A deck object would have just introduced an unneeded layer of complexity. The shoe is easier to implement as an array of cards, though it must be a multiple of the number of cards in a deck (52).

Have fun with this game. Take a look at the Readme.doc file for ideas on how you could improve this application.

Downloads

Download source - 953 Kb


Comments

  • Cool Stuff

    Posted by Legacy on 12/03/2003 12:00am

    Originally posted by: Mister Paul

    Very Cool Stuff. However, I could not get the hints to display. I'm using 1.1 of the framework.

    Reply
  • good example!

    Posted by Legacy on 01/06/2003 12:00am

    Originally posted by: seagull

    i think it is very good for me ,thank you

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • With the average hard drive now averaging one terabyte in size, the fallout from the explosion of user-created data has become an overwhelming volume of potential evidence that law-enforcement and corporate investigators spend countless hours examining. Join Us and SANS' Rob Lee for our 45-minute webinar, A Triage and Collection Strategy for Time-Sensitive Investigations, will demonstrate how to: Identify the folders and files that often contain key insights Reduce the time spent sifting through content by …

Most Popular Programming Stories

More for Developers

RSS Feeds