onstwedder.com

All that you have is your soul

2 notes &

Cocoa Coding:BlackJack tutorial - Part 12

Looking back I have made a working blackjack. But there are still a few open ends. So let’s start with the cleaning of the view. This removes all the drawn cards (of both hands) from the window’s NSView. Currently it was done in the BlackJackController’s startGame: method and while I do feel it should be initiated from there, the code actually doing the work should be moved to the BlackJackView class. 

Again this is to keep inline with the MVC-model. The V in MVC, stands for the View, and it should do the stuff that’s needed for displaying the information (or erasing some of it, in this case).

So the startGame: method looked liked this:

-(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];  
}

And after the moving of the actual functionality of removing it will be turned into this:

-(IBAction) startGame:(id)sender{
	[(BlackJackView *)self.view cleanView];
    blackJackModel = [[BlackJackModel alloc] initWithController:self]; 
    [blackJackModel setupGame];  
}

So the controller now knows a little less, it knows it needs to call the cleanView and to create a blackJackModel and then call it’s setup Game.

It seems a bit strange I’ll create a new Model every time someone calls the startGame. In fact it’s just impractical. It was done because in the init of the blackJackModel the deck got also initialized. However I will change the startGame now to the following:

-(IBAction) startGame:(id)sender{
	[(BlackJackView *)self.view cleanView];
    [blackJackModel setupGame];  
}

Of course this also implies differences in other methods. The first one is the initWithNibName: bundle:. In here I will now create the blackJackModel - so there will be just one blackJackModel during the run of the application. (This will come in handy, because I will also keep count of the win:loose ratio, as promised in earlier parts of this tutorial).

The updated method will look like this now:

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

If you build and run right now, it will look like it’s working correctly unless you play more than one game. Because when you click the “start” button after one game, it will continue with the same model and it will keep all the cards (and hands) from the previous game. 

The solution is to clean the blackJackModel up before the start of new game. A good candidate to do that is in the BlackJackModel’s setupGame. So I changed the Model’s init and moved all the initializations of the deck and hands from the init to the setupGame.

-(void) setupGame{
    self.deck = [[Deck alloc] init];
    self.tableHand = [[Hand alloc] initWithHandType:Table];
    self.playerHand =[[Hand alloc] initWithHandType:Player];
    
    [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];
}

In fact, the whole init: method was now empty. And since I’ve already created a initWithController: I’ve decided it would be better to remove the init (now it was empty anyway) and change the initWithController to call [super init] instead of [self init].

So the newly updated  initWithController changed to:

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

Up to the next part: keeping score of the wins and loses.

In the model I’ve added 4 new properties:

@interface BlackJackModel : NSObject {
    Deck *deck;
    Hand *playerHand;
    Hand *tableHand;
    BlackJackController *blackJackController;
    int wins;
    int loses;
    int draws;
    int totalplays;
}

These are internal and don’t need to be synthesized.  In the initWithController and the  endgame: method I have to change stuff to set the counters to 0 and keep count after playing.

So this is what the initWithController: will look like:

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

And this is the endGame:

-(void) endGame
{
    totalplays = totalplays+1;
    if ( [self isHandBusted:self.playerHand] ) {
        loses=loses+1;
    } else
        if ( [self isHandBusted:self.tableHand]) {
            wins=wins+1;
        }
        else
            if ( [self.tableHand getPipValue] > [self.playerHand getPipValue] ) 
            { loses=loses+1; }
            else
            { draws = draws +1; }
    
    [blackJackController updateStatusTotalPlays:totalplays loses:loses wins:wins draws:draws];
    [blackJackController hideHitBtn:YES standBtn:YES restartBtn:NO];
}

Now the playTable and playerDrawsCard (when it checks for isHandBusted) need to call [self endGame];

As you can see in the endGame: method there is a call to blackJackController updateStatusTotalPlays: loses: wins: draws: This needs to be added to the blackJackController. The only thing it will do is pass the method call to the BlackJackView. So this also needs to get this method.

Here’s the BlackJackController’s implementation of it:

-(void) updateStatusTotalPlays:(int)totalplays loses:(int)loses wins:(int)wins draws:(int)draws
{
    [(BlackJackView *)self.view updateStatusTotalPlays:totalplays loses:loses wins:wins draws:draws];
}

and here is the slightly more interesting, but also quite easy method in the BlackJackView:

-(void) updateStatusTotalPlays:(int)totalplays loses:(int)loses wins:(int)wins draws:(int)draws
{
    [self window].title = [NSString stringWithFormat:@"TotalPlays:%d Lost:%d Won:%d Draw:%d", totalplays,loses,wins,draws];
}

And if you’d build and run and then start playing it will look similar to this:

And with this I feel like closing this tutorial. (At least for now).

*This is part of a objective-C, cocoa tutorial, that will result in a working blackjack for a os x*.

Next: You can find the sources right here.
Previous chapter: blackjack tutorial - part 11

Filed under blackjack programming mac osx Cocoa Tutorial Xcode

  1. quantummao posted this