Blackjack–A Real-World OOD Example

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.



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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read