Page 1 of 1

Make BlackJack in Objective C, plus a review of the language For those who've read a few tutorials, but want to see it in actio Rate Topic: ***** 1 Votes

#1 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

Reputation: 119
  • View blog
  • Posts: 690
  • Joined: 07-January 09

Posted 19 April 2009 - 02:35 PM

Salutations, this is CrazyJugglerDrummer back with an objective C tutorial on blackjack.

This tutorial is meant for the programmer who understands C, Object Oriented Programming, and has looked at some objective C tutorials. This tutorial will not introduce all the concepts in objective C, but it will give examples of them and remind you of the differences between objective C and other languages. Objective C is used for Cocoa programming on Mac OS X as well as for the iphone and other Mac OS X based technologies. It is compiled mostly with Xcode, the primary compiler for mac application development.

Some objective C tutorials:
otierney
cocoa dev central
mac rumors
rose india
theo cacao

If you find other tutorials or have comments on which of these was most helpful, please post them!

So on with the code. We are going to make a Foundation Tool project in Xcode, which is basically a command line utility in objective C (only text input output).


Now lets think about what an Object Oriented blackjack game will need: A card class, a deck class, and a hand class. The card will have an int representing the cards value, and the other two will be objects for containing and processing cards. We make a new objective C class Card. Thereís a .h file which will contain the interface for the class (like a java interface or C header file) and a .m file for the implementation. Edit the .h file to look like this:

#import <Cocoa/Cocoa.h> //at the beginning of all obj C header files

@interface Card : NSObject { //make interface for class Card which inherits from NSObject

	int value;  
}

- (id)init : (int)newvalue;  //constructor
- (int)getValue;

@end						 //end of implementation



We use the #import statement to include a header file (which automatically makes sure the file is only included once). The interface declaration begins at @interface and ends at @end. The class inherits from NSObject, like all objective C classes. Instance variables go between the brackets, methods go outside, but before @end. A Ď-í before the method means a regular instance method, while a Ď+í denotes a static/class method. Argument types and return type go in parenthesis, the argument list coming after a Ď:í . id is a pointer to any type of object, and methods that return an object return id, instead of specifying the specific type of object (you get a bunch of warnings if you do otherwise).

So now we need to make an implementation, so we go to Card.m .

#import "Card.h"

@implementation Card

- (id)init : (int)newvalue
{
	[super init];
	value=newvalue;
	return self;
}

-(int)getValue
{
	return value;
}

@end



This is pretty much a similar thing, we #import card and implement each of the methods, a constructor and a read-only accessor. The constructors should always call [super init] before anything else, which makes sure that the super-class is initialized and other operations can be done. The constructor returns Ďselfí, which is analogous to Ďthisí in other languages.

Now we need a Deck to contain the cards.

#import <Cocoa/Cocoa.h>
#import "Card.h"

@interface Deck : NSObject {
	NSMutableArray* cards;

}

- (id)init;
- (id)drawFromDeck;

@end



We use an NSMutableArray* (a subclass of NSArray) to store the cards instead of an NSArray, because an NSArray canít be changed once itís created. We are going to be removing the cards from the array after they are drawn, just like a real deck would. An NSSarray can hold any type of object.

One thing to note is that since objective C is a simple extension of the C language, all of the C functions and types are available. Its good to see familiar faces, and we will take advantage of a few of those, including rand() and scanf(). Objective C and the cocoa framework define their own variable types for most things, like NSNumber and NSString, which are actually objects and have their own methods. Most class arguments and return values of objects in the cocoa framework involve these types. We will just be using C ints instead of NSIntegers, but we will use NSStrings for introductory purposes.

#import "Deck.h"

@implementation Deck

-(id) init
{
	[super init];
	cards= [[NSMutableArray alloc] init];
	for (short x=0; x<4; x++)
	{
		for (short y=1; y<=10; y++)
		{
			[cards addObject: [[Card alloc] init: y]];
		}
		[cards addObject: [[Card alloc] init: 10]];
		[cards addObject: [[Card alloc] init: 10]];
		[cards addObject: [[Card alloc] init: 10]];
		
	}
	return self;
}

-(id)drawFromDeck
{
	srand(time(nil));
	int index = rand()%[cards count];
	Card* C=[cards objectAtIndex:index];
	[cards removeObjectAtIndex: index];
	return C;
}

@end



This is where things start to get interesting. We initialize the superclass and the array first in the constructor, then go about stuffing the array with cards. We use an inner loop that fills the deck with the cards Ace through 10, then add three more cards each time for the jack, queen, and king, which all have values of ten. Repeat 4 times, once for each suit.

Remember when constructor an object, you first send it the message alloc to allocate memory for it, then init to call its constructor. Objective C bracket messaging syntax can be nested or spread into separate statements. We could have done

cards = [NSMutableArray alloc];
[cards init];
//same as
cards = [[NSMutableArray alloc] init];


Notice how alloc is a static method sent to the class NSMutableArray, which returns a pointer to the memory that contains the new object. The init method is an instance method and is sent to the newly allocated object.

To draw a card, we seed our good old C random number generator (using nil, the objective C equivalent of NULL), then use it to get a random card from the deck. We use NSMutableArray methods to return and delete the card from the deck. Since we modulo (%) by the size of the array ( [cards count] ) we can be sure that our random number will always be within the limits of the array, since the size keeps changing as we remove cards.

Now for the Hand class (last one, weíre almost there! :) ).

#import <Cocoa/Cocoa.h>
#import "Deck.h"
#import "Card.h"

@interface Hand : NSObject {
	NSMutableArray* cards;
}

- (id)init: (Deck*) deck;
- (void)draw: (Deck*) deck;
-(int)getTotal;	
-(void)getCards;

@end



#import "Hand.h"

@implementation Hand

- (id)init : (Deck*) deck
{
	[super init];
	cards= [[NSMutableArray alloc] init];
	[self draw: deck];
	[self draw: deck];
return self;
}

- (void)draw: (Deck*)deck;
{
	Card* C= [deck drawFromDeck];
	[cards addObject: C];
}

-(int) getTotal
{
	int total=0;
	bool ace=NO;
	for (Card* C in cards)
	{
		if ([C getValue]==1)
			ace=YES;
		
		total+=[C getValue];
	}
	
	if (total <= 11 && ace)
		total += 10;
		
	return total;
}

-(void)getCards
{
	Card* C;
	for (int x=0; x<[cards count]; x++)
	{
		C = [cards objectAtIndex: C];
		NSLog(@"%d", [C getValue]);
	}
}

@end



The first two methods are simple, we construct the hand by drawing two cards, and to draw a card we get a card from the deck and add it to the array. getTotal shows objective Cís support of the for in loop, like that of javascript or the foreach in C# (if you donít know what that is, just use the loop mechanism that we implement in getCards, which is fundamentally the same thing). We add the value of each card to the total, and if the hand had an ace and its total was less than 11, we make the ace worth 11 instead of 1. getCards doesnít actually return the cards, it just displays each cardís value.

This is the first time weíve outputted anything in the program, so look at the syntax carefully. We use the NSLog function to display a string, which is similar to printf (we also couldíve used printf if we wanted, but I thought you should see the objective C way :D ). The string to be displayed has a @ before it, which is critical as it means an NSString is being displayed. The variables to be displayed in the string use the same format as those do in printf, with %d being an integer.

Note how objective C uses the values YES and NO instead of true and false for bools.


Now we can finally do some stuff in main, since our classes are all nicely laid out. This is long, but its the last code we will have to write in the program (yay!).

#import <Foundation/Foundation.h>
#import "Hand.h"
#import "Deck.h"
#import ďCard.hĒ

int main () {
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	Deck* deck=[[Deck alloc] init];
	Hand* myHand;
	Hand* dealerHand;
	int choice, myTotal, dealerTotal;
	BOOL stay;

	
while(YES)
{
loopStart:
	myHand=[[Hand alloc] init: deck]; //you may get a warning here, explained later
	dealerHand=[[Hand alloc] init: deck];
	[myHand getCards];
	stay=NO;
	dealerTotal=0;
	myTotal=0;
	
	while (!stay)
	{
		NSLog(@"What would you like to do? 1 draw, 2 stay");
		scanf("%d",&choice);
		if (choice==1)
		{
			[myHand draw: deck];
			[myHand getCards];
		}
		else
			stay=YES;
		
		if ([myHand getTotal] > 21)
		{
			NSLog(@"Over 21, you busted!");
			goto loopStart;
			break;
		}
	}

		
	while (dealerTotal<17)
		{
			[dealerHand getCards];
			dealerTotal= [dealerHand getTotal];
		
			if (dealerTotal <= 17)
			{
				[dealerHand draw: deck];
				NSLog(@"Dealer draws");
			}
			else if (dealerTotal > 21)
			{
				NSLog(@"Dealer busts! You win!");
				goto loopStart;
				break;
			}
		}

	myTotal=[myHand getTotal];
	NSLog(@"Youíre total: %d   Dealerís total: %d",myTotal,dealerTotal);
		
	if (dealerTotal > myTotal)
	{
		NSLog(@"You loose!");
	}
	else if (myTotal > dealerTotal)
	{
		NSLog(@"You win!");
	}
	else 
	{
		NSLog(@"It's a tie!");
	}
		
	
}//end infinite loop
	
	NSLog(@"Thanks for playing!");
	[pool drain];
	return 0;
}
 


The first part of main allocates an object (NSAutoReleasePool) that aids in objective Cís automatic garbage collection, and the last part of main calls one of itís methods (this is in the code that first appears in the project when you create, so you can just leave it, or delete it if you want, itís not essential). We make initialize a deck and 2 hand pointers, plus some variables. We enter an infinite loop with a label at the start which will allow us to return to that part of the program. Both hands get initialized fresh at the start of each round, the playerís hand is displayed, and some variables are reset.

Now we are at the part where we must get the input from the user on what they want to do. We continue the input loop until the player either stays or busts. We get the input, process it, display it, and exit the loop with goto if the player busts.

The next loop shows how the dealer will play their hand. If their total is less than 17, they draw, if itís more than 17 and less than 21 they stay, if itís over 21, they bust. We display the totals of both hands, and evaluate the winner. We never actually reach the end of the program, it just keeps running until the user quits or we run out of cards in the deck, in which case youíll get a floating point exception thrown and the program will exit.

So there you have it, blackjack in objective C! Feel free to post any comments, questions, or errors I might have made in the tutorial. All feedback is welcome! :D

Happy Coding!

This post has been edited by crazyjugglerdrummer: 24 April 2009 - 07:44 AM


Is This A Good Question/Topic? 2
  • +

Replies To: Make BlackJack in Objective C, plus a review of the language

#2 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

Reputation: 119
  • View blog
  • Posts: 690
  • Joined: 07-January 09

Posted 19 July 2009 - 06:37 PM

Dream in Code doesn't really have an objective C forum, so there are posts about it in the C++, other languages, and mobile development forum. If you are looking for a site with better objective C coverage try Stack Overflow

This post has been edited by crazyjugglerdrummer: 24 July 2009 - 06:38 PM

Was This Post Helpful? 0
  • +
  • -

#3 gingerbeardman  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 09-February 10

Posted 09 February 2010 - 06:11 PM

In the final/main bit of code, you say you'll explain the warning but then never do.

myHand=[[Hand alloc] init: deck]; //you may get a warning here, explained later


Care to expand?

Thanks,
matt
Was This Post Helpful? 0
  • +
  • -

#4 spadict  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 25
  • Joined: 24-August 09

Posted 04 May 2010 - 04:28 PM

This is an old post but still an awesome help. Thanks!

I really wish DIC would get an Objective C Forum - Stack Overflow is awesome but I only get to it through Google directly. I'm not a huge fan of their navigation.
Was This Post Helpful? 0
  • +
  • -

#5 spadict  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 25
  • Joined: 24-August 09

Posted 04 May 2010 - 05:57 PM

View Postgingerbeardman, on 09 February 2010 - 05:11 PM, said:

In the final/main bit of code, you say you'll explain the warning but then never do.

myHand=[[Hand alloc] init: deck]; //you may get a warning here, explained later


Care to expand?

Thanks,
matt



A little slow but I also figured this issue out.

I believe you get the warning because you are overriding the NSObject's init but trying to also add a parameter.

instead you should declare a new initiation method with a name like this(i slightly renamed some variables to my liking when i tried this out):
@interface hand : NSObject {
	NSMutableArray* cards;
}

- (id)initWithDeck: (deck*)D; // <-much like the method names you see apple using
- (void)draw: (deck*) D;
-(int)getTotal; 
-(void)getCards;

@end



obviously change the name everywhere else appropriate as well. I think you might want to add a [super init] to your new method, but the game runs fine without doing so

This post has been edited by spadict: 04 May 2010 - 05:59 PM

Was This Post Helpful? 0
  • +
  • -

#6 feastduringtheplague  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 27-November 12

Posted 27 November 2012 - 11:04 PM

for getCards, shouldn't it be
C = [cards objectAtIndex: x]; 


instead of
 C = [cards objectAtIndex: C];


????
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1