iPhone Game Programming Series: Blackjack – Part 1: The Deck

September 9th, 2010 Posted by: - posted under:Tutorials

It has been quite some time since our last iPhone video game series and now we are ready to start a new one. Given the success of our iTennis tutorial series, we will be following along the same line and create a game without using OpenGL ES. If you are interested in OpenGL ES programming, check out Jeff Lamarche’s blog, he’s super rad. In this series we will be creating a simple Blackjack game with the following features/functionality:

  • Deck of cards
  • Basic Blackjack rules/logic
  • Dealer
  • Basic Dealer AI
  • Player
  • Controls for hit/stand
  • Audio
  • User Interface

In this first part of the series we are going to introduce the core of the game; the deck. We will be building a very generic deck of cards that could be used in any type of card game. The deck will be portable enough to just drag and drop into any project that requires a deck of cards. So, let’s begin by creating a view based application and calling it ICBBlackjack.

Creating A Card Object

Before we can create the deck, we need to create a card. Before we dig into the code, let me explain how a card object will work. A card has 2 properties that we care about. They are suit and value. Suit is pretty obvious (Hearts, Diamonds, Spades, Clubs), but the value might not be what you’d expect. For the cases of the numeric cards, the value is simply their value, however it changes a bit for the face cards and ace.

The Ace gets assigned a value of 1 (pretty obvious), while the face cards get values based on their ordering from the 10 card. So a Jack = 11, Queen = 12, and King = 13. Obviously this isn’t the case when playing a game like Blackjack, but we will let another class figure that out. For now, we will use the value as and identifier for the card. Add a new class file to your project called Card.m. Make sure you check the box to include the .h file. Open up Card.h and add the following code:

Card.h

typedef enum {
	Hearts,
	Diamonds,
	Spades,
	Clubs
} Suit;
 
#define Ace   1
#define Jack  11
#define Queen 12
#define King  13
 
@interface Card : NSObject {
	NSInteger value;
	Suit suit;
}
 
@property (nonatomic) NSInteger value;
@property (nonatomic) Suit suit;
 
- (id) initWithValue:(NSInteger) aValue suit:(Suit) aSuit;
 
@end

Ok, so a few things here to point out. The first thing we see at the top is an enum. The enum allows us to declare our own type called Suit. We could have just as easily used constants, but the enum makes things a little more clear. This allows us to do things like myCard.suit = Clubs.

Now, we define the “Special” values which will be assigned to the Ace and face cards. Finally, we declare our value and suit properties and declare and init method to build a card with these values set. Now, it’s time to implement our Card class. Open Card.m and add the following code.

Card.m

#import "Card.h"
 
@interface Card(Private)
 
- (NSString *) valueAsString;
- (NSString *) suitAsString;
 
@end
 
@implementation Card
 
@synthesize value,suit;
 
- (id) initWithValue:(NSInteger) aValue suit:(Suit) aSuit {
	if(self = [super init]) {
		self.value = aValue;
		self.suit = aSuit;
	}
	return self;
}
 
- (NSString *) valueAsString {
	switch (self.value) {
		case Ace:
			return @"Ace";
			break;
		case Jack:
			return @"Jack";
			break;
		case Queen:
			return @"Queen";
			break;
		case King:
			return @"King";
			break;
		default:
			return [NSString stringWithFormat:@"%d",self.value];
			break;
	}
}
 
- (NSString *) suitAsString {
	switch (self.suit) {
		case Hearts:
			return @"Hearts";
			break;
		case Diamonds:
			return @"Diamonds";
			break;
		case Spades:
			return @"Spades";
			break;
		case Clubs:
			return @"Clubs";
			break;
		default:
			return nil;
			break;
	}
}
 
- (NSString *) description {
	return [NSString stringWithFormat:@"%@ of %@",
			[self valueAsString],
			[self suitAsString]];
}
@end

Wow, so this looks like a lot of overkill. The truth is, you only need the init method that I created. The description method allows us to overwrite the printing method for a card and gives us pretty output. The other 2 methods are simply help methods for it (we have declared them as private at the top). You can choose to omit them if you like, but make debugging a lot easier. When you do something like NSLog(@”%@”,myCard), it will print “Ace of Spades”. Great, now you have a Card object to work with. Now we just need to create 52 of these puppies and we are on our way to hundreds of card games.

Creating The Deck

The deck of cards is fairly straight forward. First off, we need an array of cards. The card needs to have 3 methods implemented for correct functionality. They are draw, shuffle, and cardsRemaining. The reason the cardsRemaining method is needed is to prevent users from trying to draw cards from an empty deck (we will discuss this in a bit). Add a new class file to your project called Deck.m and add the following code to Deck.h:

Deck.h

#import "Card.h"
 
@interface Deck : NSObject {
 
@private
	NSMutableArray *cards;
}
 
- (void) shuffle;
- (Card *) draw;
- (NSInteger) cardsRemaining;
 
@end

A few things I want to point out. First, we import Card.h. Normally we would import the models in the .m file if they are not needed in the header. However, we need to declare the draw method which returns a Card object. So, we include the import statement here.

The next thing I want to point out is we have marked the cards array as private. We don’t want anyone mucking around in our deck without going through our methods. The reason for this is to prevent synchronization issues where someone modifies the deck without notifying the class. Kinda trivial, but good practice non the less. Now for the implementation. Add the following code to Deck.m.

Deck.m

#import "Deck.h"
 
@implementation Deck
 
- (id) init {
	if(self = [super init]) {
		cards = [[NSMutableArray alloc] init];
		for(int suit = 0; suit <= 3; suit++) {
			for(int value = 1; value <= 13; value++) {
				Card *card = [[Card alloc] initWithValue:value suit:suit];
				[cards addObject:card];
				[card release];
			}
		}
	}
	return self;
}
 
/*
 * Random sort used from this blog post
 * http://zaldzbugz.wordpress.com/2010/07/16/randomly-sort-nsarray/
 */
int randomSort(id obj1, id obj2, void *context ) {
	// returns random number -1 0 1
	return (arc4random()%3 - 1);
}
 
- (void) shuffle {
	for(int x = 0; x < 500; x++) {
		[cards sortUsingFunction:randomSort context:nil];
	}
}
 
- (Card *) draw {
 	if([self cardsRemaining] > 0) {
		Card *card = [[cards lastObject] retain];
		[cards removeLastObject];
		return [card autorelease];
	}
 
	NSException* myException = [NSException
		exceptionWithName:@"OutOfCardsException"
		reason:@"Tried to draw a card from a deck with 0 cards."
		userInfo:nil];
	@throw myException;
}
 
- (NSInteger) cardsRemaining {
	return [cards count];
}
 
- (NSString *) description {
	NSString *desc = [NSString stringWithFormat:@"Deck with %d cards\n",[self cardsRemaining]];
	for(int x = 0; x < [self cardsRemaining]; x++) {
		desc = [desc stringByAppendingFormat:@"%@\n",[[cards objectAtIndex:x] description]];
	}
	return desc;
}
 
- (void) dealloc {
	[cards release];
	[super dealloc];
}
 
@end

Ok, this file needs a little more explanation. First, we see the init method has been implemented. There are 2 for loops. The outer loop is from 0 to 3 representing the suit of each card. Basically, we want to create 13 cards for each of the 4 suits. The inner loop is from 1 to 13 representing the card’s value. We simply instantiate a Card object with the suit and value and add it to the cards array. Let’s chat about each of the methods.

Shuffle

The shuffle method simply sorts the array based on a random number. If we do it once, the deck will barely be shuffled, which makes sense. So, I have randomly sorted the array 500 times to ensure that it has been effectively shuffled. Note: I stole the randomSort method from http://zaldzbugz.wordpress.com/2010/07/16/randomly-sort-nsarray/ based on a quick/lazy Google search, but it’s a pretty common way of accomplishing this task.

Draw

We first check to see if the deck is empty. If it’s not, we retain the last object, remove it from the array and return an auto release of it. It will be up to the caller to retain the Card. Here is something you don’t see everyday, we are throwing an exception if the user tries to draw from an empty deck. Kind of a jerk move, but it will ensure that they check the cardsRemaining before performing a draw. Again, not totally necessary, but good practice.

cardsRemaining

Returns the length of the cards array I override the description method in this class to print out the deck in its entirety. Again, this is very useful for debugging. Finally, we rock the memory management and release our card deck array when our deck gets cleaned up.

Sample Run/Output

Here is a simple example of how to create a deck, shuffle, and draw.

        Deck *d = [[Deck alloc] init];
	NSLog(@"%@",d);
 
	[d shuffle];
	NSLog(@"%@",d);
 
	NSLog(@"Drew Card: %@",[d draw]);
	NSLog(@"Drew Card: %@",[d draw]);
	NSLog(@"Drew Card: %@",[d draw]);
	NSLog(@"Drew Card: %@",[d draw]);
 
	NSLog(@"%@",d);
 
	[d release];

The output is a little long, so I’ll just past some snippets of it. You can run this for yourself to see the full output.

Deck with 52 cards
Ace of Hearts
2 of Hearts
3 of Hearts
4 of Hearts
5 of Hearts
6 of Hearts
7 of Hearts
8 of Hearts
9 of Hearts
10 of Hearts
Jack of Hearts
Queen of Hearts
King of Hearts
Ace of Diamonds
2 of Diamonds
3 of Diamonds
...
(After shuffle)
Deck with 52 cards
King of Diamonds
6 of Hearts
5 of Spades
9 of Clubs
2 of Diamonds
8 of Clubs
7 of Hearts
Ace of Clubs
10 of Diamonds
Jack of Diamonds
8 of Spades
6 of Diamonds
Ace of Spades
3 of Spades
...
2010-09-07 14:56:37.953 ICBBlackJack[5465:207] Drew Card: Queen of Spades
2010-09-07 14:56:37.953 ICBBlackJack[5465:207] Drew Card: 9 of Spades
2010-09-07 14:56:37.954 ICBBlackJack[5465:207] Drew Card: 9 of Hearts
2010-09-07 14:56:37.955 ICBBlackJack[5465:207] Drew Card: Queen of Clubs

Deck with 48 cards
King of Diamonds
6 of Hearts
5 of Spades
9 of Clubs
2 of Diamonds
8 of Clubs
7 of Hearts
Ace of Clubs
10 of Diamonds
Jack of Diamonds
8 of Spades
6 of Diamonds
Ace of Spades

Note that drawing a card pulls from the end of the array. It doesn’t matter if you pull from the front or the back, just make sure it’s consistent.

Conclusion

Click Here To Download The Code For This Tutorial

And there you have it! A fully functional deck of cards. Please be sure to join me next time when we will start implementing the dealer and some basic Blackjack logic. Click Here To Download The Code For This Tutorial Feel free to post questions in the comments section or @reply them to me on Twitter.