Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Build a Multiplayer Minesweeper Game: Client-Side Creation

Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

This is the second installment of the Minesweeper game mini-series. In this tutorial, you will learn how to fully implement the client side of the application. We will use a native iOS application that connects to the web servers, parses the data, and displays the user interface interaction.


Project Overview

Minesweeper Flag is a multi-player board game that is played between two opponents. Commonly, blue or red is assigned to each player. The board is composed by 256 equal squares, and each board has 51 mines placed in entirely random positions.

Figure 5: Blue player
Preview of the Final Effect

Review of Part 1

Before you start the second part of the series make sure you have the first part fully tested and implemented.


Step 1: Installing Cocos 2D

The first step is to download and install the Cocos 2D game engine. For this tutorial, we used Cocos 2D version 2.

Once downloaded, you need to install it and integrate it with Xcode. Unpack the file downloaded before and you will notice a file called install-templates.sh. Open a terminal window and run that file using the following command

./install-templates -f.

Step 2: Create a New Project

Using Xcode, create a new project: File -> New -> Project.

Figure 1: New cocos2d project

Choose the Cocos2d v2.x on the left side menu and Cocos2d iOS on the right one.


Step 3: Configure Project Settings

Give your project a name and a company identifier. For this tutorial, we use the iPad device family option.

Figure 2: New cocos2d project

Step 4: Verify the Setup

If everything is correct you should see a window similar to the following one:

Figure 3: Project files

Step 5: Add New Classes

Now you should add two new Objective-c classes. To do this, access the menu option File -> New -> File.

Add the following classes:

  • LoadingScene: This class will serve as a loading screen for the user while he / she is waiting for another player to join the game.
  • GameScene: This is the core class of the game; is where the main logic is programmed.

Step 6: Add Resource Files

In order to correctly run the game you should add the resource files to your project. Here you can download the resource files used. Next, copy them to the resources folder.


Step 7: Create the HelloWorldLayer Class

Change your HelloWorldLayer header file to the following:

#import <GameKit/GameKit.h>
#import "cocos2d.h"

@interface HelloWorldLayer : CCLayer {}

@property (nonatomic, retain) NSURLConnection* connectionConnectGame;
@property (nonatomic,retain) NSMutableData* responseData;
@property (nonatomic,retain) NSString* URL;
@property (nonatomic) int playerID;
@property (nonatomic,retain) NSString* playerIDString;
@property (nonatomic) BOOL launchFlag;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* turnID;

+(CCScene *) scene;
-(void) connectTheWebService:(NSString*)id;

@end

Change the HelloWorldLayer implementation file to the following:

#import "HelloWorldLayer.h"
#import "GameScene.h"
#import "LoadingScene.h"
#import "cocos2d.h"
#import "AppDelegate.h"

#pragma mark - HelloWorldLayer

@implementation HelloWorldLayer

@synthesize connectionConnectGame = _connectionConnectGame;
@synthesize URL = _URL;
@synthesize responseData = _responseData;
@synthesize launchFlag = _launchFlag;
@synthesize gameID = _gameID;
@synthesize playerID = _playerID;
@synthesize playerIDString = _playerIDString;
@synthesize turnID = _turnID;

+(CCScene *) scene{
  CCScene *scene = [CCScene node];

  HelloWorldLayer *layer = [HelloWorldLayer node];

  [scene addChild: layer];
  
  return scene;
}

-(id) init {
  if( (self=[super init]) ) {

        [self setLaunchFlag:FALSE];
        
        // CHANGE THE LINK accordingly
        [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/connectGame/"];
        
        CGSize size = [[CCDirector sharedDirector] winSize];
        
        // Background
        CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"];
        background.position = ccp(size.width/2,size.height/2);
        [self addChild:background];
        
        CCSprite* flags = [CCSprite spriteWithFile:@"bandeiras.png"];
        flags.scale = .30;
        flags.position = ccp(size.width - size.width/1.15,size.height - size.height/1.25);
        [self addChild:flags];
        
        // Title Label
        CCLabelTTF *label = [CCLabelTTF labelWithString:@"Minesweeper Flags" fontName:@"Marker Felt" fontSize:64];
        label.position =  ccp( size.width /2 , size.height - size.height/4 );
        [self addChild: label];

        // Player ID generation
        NSDateFormatter *date = [[NSDateFormatter alloc] init];
        [date setDateFormat:@"mm"];
        int minute = [[date stringFromDate:[NSDate date] ]intValue];
        [date setDateFormat:@"ss"];
        int seconds = [[date stringFromDate:[NSDate date]] intValue];
        int pID = minute + seconds + (rand() % 1000000);
        [self setPlayerID:pID];
        
        NSString* playerIDInStringFormat = [NSString stringWithFormat:@"%d", _playerID];
        [self setPlayerIDString:playerIDInStringFormat];
        NSLog(@"ID DO PLAYER %@",_playerIDString);
        [self setResponseData:[NSMutableData data]];
        
    // Menus
    [CCMenuItemFont setFontSize:38];
    CCMenuItem *itemNewGame = [CCMenuItemFont itemWithString:@"New Game" block:^(id sender) {
            
            [self connectTheWebService:playerIDInStringFormat];
    }];

        CCMenu *menu = [CCMenu menuWithItems:itemNewGame ,nil];
    
    [menu alignItemsVerticallyWithPadding:20];
    [menu setPosition:ccp( size.width/2, size.height/2 - 50)];
    
    [self addChild:menu];
  }
  return self;
}

//connect the Web server method
-(void) connectTheWebService:(NSString*)id{
    
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id]                                                             ]];
    _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

//Web server responses.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [_responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
    NSLog(@"HelloWorld responseString %@",responseString);
    
    NSArray* resultArray = [[NSArray alloc] init];
    resultArray = [responseString componentsSeparatedByString:@","];

    if ([[resultArray objectAtIndex:1] isEqualToString:@"no"]) {
        [self setGameID:[resultArray objectAtIndex:0]];
        [self setLaunchFlag:TRUE];
    } else {
        [self setGameID:[resultArray objectAtIndex:0]];
        _playerID = [[resultArray objectAtIndex:1] integerValue];
        [self setPlayerID:[[resultArray objectAtIndex:1] integerValue]];
        [self setLaunchFlag:FALSE];
    }
    
    if (_launchFlag){
        GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerIDString andWithGameID:_gameID andWithTurnID:@"no"];
        [[CCDirector sharedDirector] replaceScene:scene];
    } else {
        LoadingScene* scene = [LoadingScene initializeWithPlayerID:_playerIDString andWithGameID:_gameID];
        [[CCDirector sharedDirector] replaceScene:scene];
    }

}

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

@end

Step 8: Create the LoadingScene Class

Change your LoadingScene header file to the following:

#import "cocos2d.h"

@interface LoadingScene : CCLayer <UIGestureRecognizerDelegate>{ }

@property (nonatomic, retain) NSURLConnection *connectionConnectGame;
@property (nonatomic,retain) NSMutableData* responseData;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* playerID;
@property (nonatomic) BOOL launchFlag;
@property (nonatomic,retain) NSString* URL;

+(CCScene *) scene;
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid;
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid;
-(void) connectTheWebService:(NSString*)id;

@end

Change your LoadingScene implementation file to the following:

#import "LoadingScene.h"
#import "GameScene.h"

@implementation LoadingScene

@synthesize responseData = _responseData;
@synthesize connectionConnectGame = _connectionConnectGame;
@synthesize gameID = _gameID;
@synthesize playerID = _playerID;
@synthesize launchFlag = _launchFlag;
@synthesize URL = _URL;

+(CCScene *) scene{
    CCScene *scene = [CCScene node];
    
    LoadingScene *layer = [LoadingScene node];
    
    [scene addChild: layer];
    
    return scene;
}

-(id) init{
    if( (self=[super init] )) {}
    
    return self;
}

// Custom initializer
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid {
    return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid] autorelease];
}

-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid {
    self = [super init];
    
    if (self) {
        CGSize _size = [[CCDirector sharedDirector] winSize];
        _gameID = [[NSString alloc] init];
        [self setLaunchFlag:FALSE];
        [self setPlayerID:playerid];
        [self setGameID:gameid];
        
        CCSprite* background = [CCSprite spriteWithFile:@"background.jpg"];
        background.position = ccp(_size.width/2,_size.height/2);
        [self addChild:background];
        
        CCLabelTTF* label = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Marker Felt" fontSize:40];
        label.position = ccp(_size.width/2, _size.height/2);
        [self addChild:label];
        CCLabelTTF* label2 = [CCLabelTTF labelWithString:@"Waiting for another player" fontName:@"Marker Felt" fontSize:25];
        label2.position = ccp(_size.width/2, _size.height - _size.height/1.80);
        [self addChild:label2];
        
        [self setResponseData:[NSMutableData data]];
        
        // CHANGE IT ACCORDINGLY
        [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/player2Available/"];
        
        [self schedule:@selector(update:) interval:2];
    }
    return self;
}

-(void) update:(ccTime) dt{
    [self connectTheWebService:_gameID];
}

-(void) connectTheWebService:(NSString*)id{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id]                                                             ]];
    _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [_responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];

    if ([responseString isEqualToString:@"nulo"]) {
        [self setLaunchFlag:FALSE];
    }else {
        [self setLaunchFlag:TRUE];
    }
    
    if (_launchFlag){
        GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerID andWithGameID:_gameID andWithTurnID:_playerID] ;
        [[CCDirector sharedDirector] replaceScene:scene];
    } else { [self schedule:@selector(update:) interval:2];  }
    
}

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

@end

Step 9: Create the GameScene Class

Change your GameScene header file to the following:

#import "cocos2d.h"

@interface GameScene : CCLayer <UIGestureRecognizerDelegate> {
    NSString* turn;
}

@property (nonatomic) int gameGridSize, initialSpriteXXPosition, initialSpriteYYPosition, deslocamentoNoEixoXX, deslocamentoNoEixoYY, tamanhoSprite, playerBlueScore, playerRedScore, lastSpriteTouch;

@property (nonatomic, retain) NSMutableArray *spritesMutableArray;
@property (nonatomic,retain) NSMutableArray* touchedLocations;
@property (nonatomic) CGSize size;
@property (nonatomic) CGPoint initialSpritePositon;
@property (nonatomic, retain) CCSprite* sprites;
@property (nonatomic) int spritePositionInGrid;
@property (nonatomic) int webServerResult;
@property (nonatomic, strong) NSMutableData *responseData;
@property (nonatomic, strong) NSString *resourceImage;
@property (nonatomic, retain) CCSprite* playerBlueSprite;
@property (nonatomic, retain) CCSprite* playerRedSprite;
@property (nonatomic, retain) CCSprite* playerTurnSprite;
@property (nonatomic, retain) CCSprite* youArrow;
@property (nonatomic,retain) CCLabelTTF* playerBlueLabel;
@property (nonatomic,retain) CCLabelTTF* playerRedLabel;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* userID;
@property (nonatomic,retain) NSString* colorPlayer;
@property (nonatomic,retain) CCSprite* positionTouchedSprite;
@property (nonatomic,retain) NSString* turn;
@property (nonatomic, retain) NSMutableString* gameBoardInfoS;

+(CCScene *) scene;
-(void)getMinesAtPosition:(int)pos;
-(void)drawGameBoard;
-(void)setInitialAppValues;
-(void)setArrowonTheRightPlayer;

+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid;
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid;

@end

Change your GameScene implementation file to the following:

#import "GameScene.h"
#import "cocos2d.h"
#import "LoadingScene.h"

@implementation GameScene

@synthesize spritesMutableArray = _spritesMutableArray;
@synthesize touchedLocations = _touchedLocations;
@synthesize size = _size;
@synthesize initialSpritePositon = _initialSpritePositon;
@synthesize sprites = _sprites;
@synthesize spritePositionInGrid = _spritePositionInGrid;
@synthesize webServerResult = _webServerResult;
@synthesize gameGridSize = _gameGridSize;
@synthesize initialSpriteXXPosition = _initialSpriteXXPosition;
@synthesize initialSpriteYYPosition = _initialSpriteYYPosition;
@synthesize deslocamentoNoEixoXX = _deslocamentoNoEixoXX;
@synthesize deslocamentoNoEixoYY = _deslocamentoNoEixoYY;
@synthesize tamanhoSprite = _tamanhoSprite;
@synthesize responseData = _responseData;
@synthesize resourceImage = _resourceImage;
@synthesize playerTurnSprite = _playerTurnSprite;
@synthesize youArrow = _youArrow;
@synthesize playerBlueSprite = _playerBlueSprite;
@synthesize playerRedSprite = _playerRedSprite;
@synthesize playerBlueLabel = _playerBlueLabel;
@synthesize playerRedLabel = _playerRedLabel;
@synthesize playerBlueScore = _playerBlueScore;
@synthesize playerRedScore = _playerRedScore;
@synthesize gameID = _gameID;
@synthesize userID = _userID;
@synthesize colorPlayer = _colorPlayer;
@synthesize turn = _turn;
@synthesize gameBoardInfoS;
@synthesize lastSpriteTouch;

@synthesize positionTouchedSprite = _positionTouchedSprite;

+(CCScene *) scene{
    //+(id)scene{
    CCScene *scene = [CCScene node];
    
    GameScene *layer = [GameScene node];
    
    [scene addChild: layer];
    
    return scene;
}

-(id) init{
    if( (self=[super init] )) {
    }
    return self;
}

//new initialize method to recive parameters such as playerID, turnID, and gameID
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{
    return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid andWithTurnID:turnid] autorelease];
}

//initialize several values and call methods to generate and draw all scene
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{
    self = [super init];
    
    if (self) {
        // Activate touch events
        [super setIsTouchEnabled:YES];
        //set the parameters
        [self setGameID:gameid];
        [self setUserID:playerid];
        [self setTurn:turnid];
        [self setInitialAppValues];
        
        //generate and draw gameBoard
        [self drawGameBoard];
        
        //call the updater one time for second
        [self schedule:@selector(update:) interval:1];
    }
    return self;
}


// detect touchs and which sprite was touched
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    
    //verify if the user have the turn
    if(![_turn isEqualToString:_userID]){
        //in case of the user not have the turn
        return;
    }
    
    UITouch* touch = [touches anyObject];
    CGPoint location = [touch locationInView: [touch view]];
    CGPoint converted = [[CCDirector sharedDirector] convertToGL:location];
    
    _spritePositionInGrid=0;
    for (CCSprite* spr in _spritesMutableArray){
        CCSprite* currentSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid];
        
        if (CGRectContainsPoint([currentSprite boundingBox], converted)){
            
            // Verificar se a posição já foi touched
            BOOL jaExiste =FALSE;
            for (int i = 0 ; i < [_touchedLocations count]; i++){
                if (_spritePositionInGrid == [[_touchedLocations objectAtIndex:i] integerValue])
                    jaExiste=TRUE;
            }
            
            //was touched?
            if([gameBoardInfoS characterAtIndex:_spritePositionInGrid] != '9'){
                return;
            }

            if (!jaExiste){
                [_touchedLocations addObject:[NSNumber numberWithInt:_spritePositionInGrid]];
                lastSpriteTouch = _spritePositionInGrid;
                
                self.responseData = [NSMutableData data];
                
                _positionTouchedSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid];
                
                NSMutableString* enderecoReturnValue = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnValue/%@/%@/%d",_userID,_gameID,_spritePositionInGrid];
                NSLog(@"%@",enderecoReturnValue);
                
                NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:enderecoReturnValue]];
                [[NSURLConnection alloc] initWithRequest:request delegate:self];   
            }
        }
        _spritePositionInGrid++;
    }
}

-(void) update: (ccTime) dt
{
    if(![_turn isEqualToString:_userID]){
        
        NSMutableString* returnUpdateURL = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnUpdates/%@",_gameID];
        
        NSURL *url = [NSURL URLWithString:returnUpdateURL];
        NSData *jsonData = [NSData dataWithContentsOfURL:url];
        
        NSString *value = [[NSString alloc] init];
        NSRange a;
        
        if(jsonData != nil) {
            NSError *error = nil;
            id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
            if(error == nil) {
                NSString* resultString = [result objectForKey:@"Uncover_Matrix"];
                int positionAt, positionDiff=-1;
                
                for(positionAt= 0; positionAt< resultString.length; positionAt++){
                    a = NSMakeRange(positionAt, 1);
                    if(![[gameBoardInfoS substringWithRange:a] isEqualToString:[resultString substringWithRange:a]]){
                        positionDiff = positionAt;
                        value = [[NSString alloc] initWithString:[resultString substringWithRange:a]];
                        break;
                    }
                }
                if(positionDiff != -1){
                    [self setTurn:[result objectForKey:@"Turn"]];
                
                    [gameBoardInfoS replaceCharactersInRange:a withString:value];
                    
                    if([value isEqualToString:@"*"]){
                        _playerBlueScore++;
                        NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore];
                        [_playerBlueLabel setString:stringBlueScore];
                        value = @"9";
                    }
                    
                    if([value isEqualToString:@"#"]){
                        _playerRedScore++;
                        NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore];
                        [_playerRedLabel setString:stringRedScore];
                        value = @"10";
                    }
                    
                    [self setArrowonTheRightPlayer];
                    
                    CGPoint coordenates = [self getReversePositionGameBoard:positionDiff];
                    [self getMinesAtPosition:[value integerValue] withXCoord:coordenates.x andYYpos:coordenates.y];
                }
                
            }
            else
                NSLog(@"error");
        }
    }
    
}

//recive the position and the value returned from Web Services and replace in the board
-(void)getMinesAtPosition:(int)pos withXCoord:(int)xxpos andYYpos:(int)yypos{
    _webServerResult = pos;
        
    switch (_webServerResult) {
        case 0:
            [self setResourceImage:@"0.png"];
            break;
        case 1:
            [self setResourceImage:@"1.png"];
            break;
        case 2:
            [self setResourceImage:@"2.png"];
            break;
        case 3:
            [self setResourceImage:@"3.png"];
            break;
        case 4:
            [self setResourceImage:@"4.png"];
            break;
        case 5:
            [self setResourceImage:@"5.png"];
            break;
        case 6:
            [self setResourceImage:@"6.png"];
            break;
        case 7:
            [self setResourceImage:@"7.png"];
            break;
        case 8:
            [self setResourceImage:@"8.png"];
            break;
        case 9:
            [self setResourceImage:@"flag_b.png"];
            break;
        case 10:
            [self setResourceImage:@"flag_r.png"];
            break;            
        default:
            _resourceImage = @"flag_r.png";
            break;
    }
    
    // New sprite substitution after the WS invocation
    CCSprite* newSprite = [CCSprite spriteWithFile:_resourceImage];
    newSprite.scale = .25;
    newSprite.position = ccp(xxpos,yypos);
    
    [self addChild:newSprite];
}

// communication
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [[self responseData] setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [[self responseData] appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    
    NSString* responseString = [[NSString alloc] initWithData:[self responseData] encoding:NSUTF8StringEncoding];
    
    NSRange range = NSMakeRange(lastSpriteTouch, 1);
    [gameBoardInfoS replaceCharactersInRange:range withString:responseString];
    
    //assign the turn after the request
    if([responseString isEqualToString:@"*"]){
        _playerBlueScore++;
        NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore];
        [_playerBlueLabel setString:stringBlueScore];
        _turn = _userID;
        responseString = @"9";
    }
    else if([responseString isEqualToString:@"#"]){
        _playerRedScore++;
        NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore];
        [_playerRedLabel setString:stringRedScore];
        _turn = _userID;
        responseString = @"10";
    }
    else{
        _turn = @"is not you";
    }
    
    [self setArrowonTheRightPlayer];
    
    [self getMinesAtPosition:[responseString integerValue] withXCoord:_positionTouchedSprite.position.x andYYpos:_positionTouchedSprite.position.y];
}

//put the arrow on the player that holds the turn
-(void)setArrowonTheRightPlayer{
    
    if([_turn isEqualToString:_userID]) {
        if ([_colorPlayer isEqualToString:@"blue"])
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
        else
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48);
    }
    
    if(![_turn isEqualToString:_userID]) {
        if ([_colorPlayer isEqualToString:@"blue"])
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48);
        else
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
    }
}

//calculate the reverse position to change the sprite on gameBoard
-(CGPoint)getReversePositionGameBoard:(int)positionTouched{
    NSInteger linha = (int) positionTouched / 16.0;
    NSInteger y = 688 - _tamanhoSprite * linha;
    NSInteger x = ( (int) positionTouched % 16) * 38 + _initialSpriteXXPosition;
    CGPoint position = CGPointMake(x, y);

    return position;
}

//draw the gameBoard (background, sprites blue (water), and the playerss avatar
-(void)drawGameBoard{
    
    for (int i = 0 ; i < _gameGridSize; i++) {
        _sprites = [CCSprite spriteWithFile:@"water.png"];
        _sprites.scale = .25;
        _initialSpritePositon = CGPointMake(_initialSpriteXXPosition+_deslocamentoNoEixoXX, _size.height-_initialSpriteYYPosition-(_deslocamentoNoEixoYY*_tamanhoSprite));
        _sprites.position = ccp(_initialSpritePositon.x,_initialSpritePositon.y);
        _deslocamentoNoEixoXX+=_tamanhoSprite;
        
        if (((i+1) % 16) == 0){
            _deslocamentoNoEixoXX=0;
            _deslocamentoNoEixoYY++;
        }
        
        [_spritesMutableArray addObject:_sprites];
        [self addChild:_sprites];
    }
    
    
    [self addChild:_playerTurnSprite z:4];
    
    [self addChild:_playerBlueSprite];
    [self addChild:_playerRedSprite];
    [self addChild:_youArrow];
    
    [self addChild:_playerBlueLabel];
    [self addChild:_playerRedLabel];
}

//set initial values
-(void)setInitialAppValues{
    _size = [[CCDirector sharedDirector] winSize];
    _gameGridSize = 256, _tamanhoSprite = 38;
    _initialSpriteXXPosition = 80, _initialSpriteYYPosition = 80;
    _deslocamentoNoEixoXX=0, _deslocamentoNoEixoYY=0;
    _spritesMutableArray = [[NSMutableArray alloc] initWithCapacity:256];
    _touchedLocations = [[NSMutableArray alloc] initWithCapacity:256];
    [self setResourceImage:@""];
    
    CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"];
    background.position = ccp(_size.width/2,_size.height/2);
    [self addChild:background];
    
    //init the gameBoard array
    gameBoardInfoS = [[NSMutableString alloc] init];
    for(int i=0;i<256;i++){
        [gameBoardInfoS appendString:@"9"];
    }
    
    _playerBlueSprite = [CCSprite spriteWithFile:@"player_bue.png"];
    _playerBlueSprite.scale = .5;
    _playerBlueSprite.position = ccp(_size.width/1.30, _size.height/1.25);
    _playerRedSprite = [CCSprite spriteWithFile:@"player_red.png"];
    _playerRedSprite.scale = .5;
    _playerRedSprite.position = ccp(_size.width/1.30, _size.height/1.75);
    
    //identify the players on field
    if([_turn isEqualToString:_userID]){
        _youArrow = [CCSprite spriteWithFile:@"you.png"];
        _youArrow.scale = .3;
        _youArrow.position = ccp(_size.width/1.30, _size.height/1.30);
        [self setColorPlayer:@"blue"];
    }
    else{
        _youArrow = [CCSprite spriteWithFile:@"you.png"];
        _youArrow.scale = .3;
        _youArrow.position = ccp(_size.width/1.30, _size.height/1.85);
        [self setColorPlayer:@"red"];
    }
    
    _playerTurnSprite = [CCSprite spriteWithFile:@"arrowTurn.png"];
    _playerTurnSprite.scale = 1;
    _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
    
    
    _playerBlueLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0];
    _playerBlueLabel.position = ccp(_size.width/1.15, _size.height/1.25);
    _playerRedLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0];
    _playerRedLabel.position = ccp(_size.width/1.15, _size.height/1.75);
    
    [self setPlayerBlueScore:0];
    [self setPlayerRedScore:0];
}

-(void) registerWithTouchDispatcher
{
    NSLog(@"registerWithTouchDispatcher");
    [[[CCDirector sharedDirector] touchDispatcher] addStandardDelegate:self priority:0];
}

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

@end

Step 10: Build & Run the Game

After you include the abovementioned code, the next step is to build and test the application. Use the cmd + b shortcut. If everything is fine you should see the iPad simulator up and running the game.

Figure 4: iPad

Step 11: Enjoy the Game

In this final step the objective is to enjoy the game. Since the game is a two-player game the best way to play it is with a friend!

In the following screens we can see the final game board though the vision of the two-players (blue and red). The arrow above the player avatar indicates the current player turn; the player that is currently playing.

Blue player game board:

Figure 5: Blue player

Red player game board:

Figure 6: Red player

Step 12: Conclusions

This concludes the Part II of how to create a Minesweeper Flag game using both server-side and client-side code. By now, you should have enough knowledge to create a simple Cocos2D game using the same game engine. If you have any questions or comments, please feel free to leave them in the comments section here.

Advertisement