onstwedder.com

All that you have is your soul

0 notes &

Cocoa Coding:BlackJack tutorial - PART 11

And here it is. The (near) final part of the blackjack tutorial. In which i change nearly all the files, except the xibs, the AppDelegates and the deck class. The rest is changed - so this is one heck of an episode in the tutorial.

The main thing I’ve done now, is implement a real MVC-model. That means that the BlackJackModel class needs to do everything that has to do with blackjack. The controller needs only to receive the notifications from the view (a click on a button) and needs to set the view, based on signals from the BlackJackModel.

The BlackJackView is the least changed. All I did here is add a method that can set the HiddenStatus of the three button. I figured these buttons’ hidden status are kind of always set together, so i made one method:

-(void) hideHitBtn:(BOOL)hideHit standBtn:(BOOL)hideStand restartBtn:(BOOL)hideRestart
{
    [self.hitBtn setHidden:hideHit];
    [self.standBtn setHidden:hideStand];
    [self.restartBtn setHidden:hideRestart];
}

With one call, you can give three BOOL-parameters setting the three buttons. One could argue about the name restartBtn, because it displays “start” in it’s button. It’s careless of my part and I decided to not correct it.

Now, let me guide you to the BlackJackModel. One of the most important things changed is that the BlackJackModel now knows about the BlackJackController. In other words: there’s a property, that’s set in a new initWithController: that sets a blackJackController instance of class BlackJackController.

Also everything that had anything to do with the game blackJack in the controller has been moved to methods in the BlackJackModel. The new .h file looks like this:

#import <Cocoa/Cocoa.h>
@class Deck;
@class Hand;
@class BlackJackController;


@interface BlackJackModel : NSObject {
    Deck *deck;
    Hand *playerHand;
    Hand *tableHand;
    BlackJackController *blackJackController;
}

@property (assign) Deck *deck;
@property (retain) Hand *playerHand;
@property (retain) Hand *tableHand;

-(id) initWithController:(BlackJackController *) aController;
-(void) setupGame;
-(void) openTableHand;
-(void) drawCardForHand:(Hand *)hand;
-(BOOL) isHandBusted:(Hand *)hand;
-(BOOL) isHandBelowPipMinimal:(Hand *)hand;
-(BOOL) isTablePipLessThenPlayerPip;
-(void) playTable;
-(void) playerDrawsCard;
-(void) endGame;

The BlackJackController.h has two extra methods:

#import <Cocoa/Cocoa.h>
@class BlackJackModel;
@class Hand;

@interface BlackJackController : NSViewController {
@private
    BlackJackModel *blackJackModel;
}

-(IBAction) startGame:(id)sender;
-(IBAction) hitCard:(id)sender;
-(IBAction) stand:(id)sender;
-(void) displayHand:(Hand *)hand;
-(void) hideHitBtn:(BOOL)hideHit standBtn:(BOOL)hideStand restartBtn:(BOOL)hideRestart;

@end

You can imagine what the last method does. It merely calls the same method in the BlackJackView with the same parameters. The displayHand will take a hand instance and pass it to the BlackJackView’s showHand. It seems sloppy these two have different names and that’s because it is sloppy. It is also because I was looking for a good name for it. It draws the images of the cards in the hand on the view - but any combination with hands and draw make you think about drawing a card from the deck. That’s why I picked displayHand. But apparently not always.

The blackJackController has the most code removed. It now looks like this:

#import "BlackJackController.h"
#import "BlackJackModel.h"
#import "BlackJackView.h"
#import "Deck.h"
#import "Hand.h"

@implementation BlackJackController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Initialization code here.
    }
    
    return self;
}

-(IBAction) startGame:(id)sender{
    
    for (NSImageView *timageview in [(BlackJackView *)self.view imageviews]) {
		[timageview removeFromSuperview];
	}
	[[(BlackJackView *)self.view imageviews] removeAllObjects];
	
    
    blackJackModel = [[BlackJackModel alloc] initWithController:self]; 
    [blackJackModel setupGame];  
}

-(IBAction) hitCard:(id)sender{
    [blackJackModel playerDrawsCard];
}

-(IBAction) stand:(id)sender{
	[blackJackModel playTable];
}

-(void)displayHand:(id)hand
{
    [(BlackJackView *)self.view showHand:hand]; 
}

-(void) hideHitBtn:(BOOL)hideHit standBtn:(BOOL)hideStand restartBtn:(BOOL)hideRestart
{
    [(BlackJackView *)self.view hideHitBtn:hideHit 
                               standBtn:hideStand 
                                restartBtn:hideRestart];
}

- (void)dealloc
{
    [super dealloc];
}

@end

What it does now, is whenever someone clicked on a button, it calls a method in the blackJackModel. In the startGame there is some code

  for (NSImageView *timageview in [(BlackJackView *)self.view imageviews]) {
		[timageview removeFromSuperview];
	}
	[[(BlackJackView *)self.view imageviews] removeAllObjects];

That actually removes all the images (or imageviews, really) of a previous game from the NSView and then removes those objects from the instance imageviews) - This was already here, in the last tutorial - and I didn’t feel it would be more beautiful to put it to the model. However, it could be that in the next tutorial, I will move it towards the BlackJackView class and make a method “cleanView”-call here. or something.

Anyway, The model is now updated with knowledge about the game, and it looks like this:

#import "BlackJackModel.h"
#import "Deck.h"
#import "Hand.h"
#import "Card.h"
#import "BlackJackController.h"

@implementation BlackJackModel
@synthesize deck, tableHand, playerHand;

- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        self.deck = [[Deck alloc] init];
        self.tableHand = [[Hand alloc] initWithHandType:Table];
        self.playerHand =[[Hand alloc] initWithHandType:Player];
    }
    
    return self;
}

- (id)initWithController:(BlackJackController *)aController
{
   self = [self init];
    if (self) {
        // Initialization code here.
        blackJackController = aController;
    }
    return self;
}

-(void) setupGame{
	[self.deck shuffle];
    
    [self.playerHand addCard:[self.deck drawCard] open:YES];
    [self.tableHand addCard:[self.deck drawCard] open:YES];
    [self.playerHand addCard:[self.deck drawCard] open:YES];
    [self.tableHand addCard:[self.deck drawCard] open:NO];
    
    
    [blackJackController displayHand: [self playerHand]]; 	
    [blackJackController displayHand: [self tableHand]]; 

    [blackJackController hideHitBtn:NO standBtn:NO restartBtn:YES];
}

-(void) openTableHand{
    for (Card *tCard in self.tableHand.cardsInHand) {
		tCard.open=YES;
	} 
}

-(void) drawCardForHand:(Hand *)hand
{
    [hand addCard:[self.deck drawCard] open:YES];   
}

-(BOOL) isHandBusted:(Hand *)hand
{
   if ([hand getPipValue] > 21)
       return YES;
    else
       return NO;
}

-(BOOL) isHandBelowPipMinimal:(Hand *)hand
{
    if ([hand getPipValue] < 16)
        return YES;
    else
        return NO;
}

-(BOOL) isTablePipLessThenPlayerPip;
{
    if ([self.tableHand getPipValue] < [self.playerHand getPipValue]) 
        return YES;
    else
        return NO;
}

-(void) playTable
{
    
    [blackJackController hideHitBtn:YES standBtn:YES restartBtn:YES];
    [self openTableHand];
    [blackJackController displayHand:self.tableHand];
   
    // while table should draw 
	// then draw card
    while ([self isHandBelowPipMinimal:self.tableHand]) {
		[self drawCardForHand:self.tableHand];
        [blackJackController displayHand:self.tableHand];
 	}
	
    	
	// while table >16 and < player 
	// then draw card
	while ([self isTablePipLessThenPlayerPip] ) {
		[self drawCardForHand:self.tableHand];
       [blackJackController displayHand:self.tableHand];
	}
    
    [blackJackController hideHitBtn:YES standBtn:YES restartBtn:NO];

}

-(void)playerDrawsCard{
    [self drawCardForHand:self.playerHand];
    [blackJackController displayHand:[self playerHand]];
    
    if ( [self isHandBusted:self.playerHand] ) {
        [self endGame];
    }

    
}

-(void) endGame
{
    [blackJackController hideHitBtn:YES standBtn:YES restartBtn:NO];
}

- (void)dealloc
{
    [super dealloc];
}

@end

As you can see it’s still rather clear (at least to me :-) ) For example the setupGame: method does what you would expect: it shuffles the deck, it draws the cards for the two hands, it tells now to the controller that the controller should tell the view to display the hands and then it tells the controller that the controller should tell the view to set the hidden status’ of the buttons.

After that the models wait for a method that the controller will send after the user clicks a button in the view. It will eventually lead to either the playerDrawsCard method or the playTable method.

So all of this was basically done, to be more inline with the MVC-model.

Then there was the the annoying ace-card. Up until now I have not looked into the a hand holding one (or more) ace(s). So that would leave a blackjack-player frustrated, when his hand was (ace), (five) he would probably be inclined to draw an extra card. E.g. he drew a (ten) and my game showed him - he busted with pipValue 26. While according to normal blackjack he’d have a pip value of 16. As the ace just counts as a 1 instead of 11.

It wasn’t all that tricky. Here’s what I did.

In the hand.m I’ve updated the getPipValue method by counting the aces in the current hand and then checking if the pipValue of the hand is over 21 and has any aces. If it has aces it substracts 10 points of the pipvalue and lowers the countedaces by 1.

In code it looks like this;

-(NSInteger) getPipValue {
	NSInteger aValue = 0;	
    NSInteger countAces = 0;
	for (Card *tCard in cardsInHand) {
            aValue = aValue + [tCard pipValue];
            if ([tCard isAce])
            {
                countAces=countAces+1;
            }
	}
    while ((countAces>=1) && (aValue > 21)) 
    {
        aValue = aValue - 10;
        countAces = countAces - 1;
    }
	return aValue;
}

Of course to make the above work, I had to updated the Card.m and Card.H - so that it could reply to the IsAce-method. The declaration in the Card.h is simple: -(BOOL) isAce; The card.m implementation for this isAce method looks as follows:

-(BOOL) isAce{
	switch (self.cardId) {
		case 1:
			return YES;
			break;
        default:
            return NO;
            break;
    }
}

And now - it really acts like a normal blackJack - ace.

In the next part I might keep count of the number of wins and loses. I move that tiny bit of code I mentioned earlier from the controller to the view. 

But honestly - this is rather finished already.

This is a part of tutorial to create blackJack game in Objective-C for mac OSX.

The next part can be found here: Blackjack tutorial - part 12
The previous part is to be found here: Blackjack tutorial - part 10

By the way: the real previous part is this one: Blackjack tutorial - part 10.5 (Though this just contains more background info on MVC and thoughts about what to fix yet).

Filed under blackjack mac objective-c osx coding programming Cocoa Tutorial Xcode