Cocos2d-x Cross Platform Video Player – Part 1 iOS

Cross Platform (iOS and Android) Video playing solution is very much possible in cocos2d-x 2.1.5. But as far as I searched there is not single tutorial / forum discussion that explains it with ease. So here I am summarising how I achieved it. There is no Video Player control in cocos2d-x. So we need to fall back to native video players of iOS and Android platforms. Android Video Playback in 2nd part of this tutorial.

Objective:

To write a single function that plays a video in either iOS / Android, given its file name. Also, the video should be made skippable, by tapping on it. The game’s sound track should be muted when the video gets played, since it might have its own soundtrack and the game’s sound track will be resumed, when the video is stopped.

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-ios/archive/master.zip Complete Project can be found in GITHUB.

1. Generic Wrapper Class

To handle Features, which require separate implementations on iOS and Android, I implemented wrapper class(WrapperClass.h, WrapperClass.cpp), which will be called from the game code. Then there will be separate Platform dependent wrapper classes, which will get called from WrapperClass. The only functionality of WrapperClass is to forward the flow to platform dependent functions. Here is the code of the Wrapper Class:

//WrapperClass.h
class WrapperClass : public cocos2d::CCObject {
public:
    void                            playVideo(const char *vidPath);
    static WrapperClass             *getShared();
};

//WrapperClass.cpp
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "misc/iOSWrapper.h"
#endif

static WrapperClass *instance   =   NULL;

void WrapperClass::playVideo(const char *vidPath) {
    AudioManager::sharedManager()->stopBG();
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
    iOSWrapper::getShared()->playVideo(vidPath);
#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
    CCApplication::sharedApplication()->playVideo(vidPath);
#endif
}

WrapperClass *WrapperClass::getShared() {
    if (!instance) {
        instance                =   new WrapperClass();
    }
    return                          instance;
}

The `playVideo` function just calls platform specific function from here. Since playback in Android requires JNI calls to pass the flow to JAVA side, I implemented the required function in CCApplication, which already has few JNI calling functions. Also, this function stops the background music, since the Video will be having its own Audio Track. Game’s sound track will be resumed when the video gets over.

2. Playing Video in iOS

The `iOSWrapper` singleton (iOSWrapper.h, iOSWrapper.mm) called in above code is another wrapper that bridges the CPP side of cocos2d-x and Objective-C part of native `UIView` code. The `playVideo` function of `iOSWrapper` class is as follows:

void iOSWrapper::playVideo(const char *vidPath) {
    NSString                        *string, *stringPath;
    stringPath                  =   [NSString stringWithUTF8String:vidPath];
    string                      =   [[NSBundle mainBundle] pathForResource:stringPath ofType:@"mp4"];
    [[EAGLView sharedEGLView] playTutorialVideo:string];
}

The above code converts the date type of file name from `char *` to `NSString` and then find its path in the Bundle and calls another custom function `playTutorialVideo()` written in `EAGLView`. That function is responsible for actual video play. Add the following in `EAGLView` to complete the iOS part.

 (void) playTutorialVideo:(NSString *)path {

    NSURL                           *url;

    url                         =   [NSURL fileURLWithPath:path];
    player                      =   [[MPMoviePlayerController alloc] initWithContentURL:url];
    player.view.frame           =   CGRectMake(0, 0, self.frame.size.height, self.frame.size.width);
    player.fullscreen           =   YES;
    player.scalingMode          =   MPMovieScalingModeNone;
    player.controlStyle         =   MPMovieControlStyleNone;
    [self                           addSubview:player.view];
    [player                         play];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(removeVideo)
                                                 name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
}

Now we are completely into iPhone Native part, where we use Objective-C and deal with UIView and UIViewControllers, completely away from cocos2d-x land. The above function creates a `MPMoviePlayerController`,  loads the video with given path and plays it. MPMoviePlayerController throws notifications instead of calling delegate callback functions, in case of internal events. So the above code also registers with NSNotificationCenter to capture video pause / stop events.

(void) removeVideo {
    if (player.playbackState == MPMoviePlaybackStatePaused || player.playbackState ==   MPMoviePlaybackStateStopped) {
        [player.view removeFromSuperview];
        [player release];
        player = nil;

        [[NSNotificationCenter defaultCenter] removeObserver:self
        name:MPMoviePlayerPlaybackStateDidChangeNotification
        object:nil];

        AudioManager::sharedManager()->playBG();
    }
}

The above function is registered to handle notifications of MPMoviePlayerController and will get invoked on any change in state. We need to remove the controller, only when the video playback is over and so we check for the Stopped/Paused state and when they happen, we remove the controls, release from memory, resumes the game background music.

Skip on Touch

Not all players will sit through the videos / instructions, but they want to directly get hooked up into the gameplay. So, we will make the video stop and disappear, when user taps on it. To do it, just add the following block of code inside `touchesBegan` function of EAGLView, immediately after `if (isKeyboardShown_)` block:

if (player) {

    [player stop];
    [player.view removeFromSuperview];
    [player release];
    player = nil;

    [[NSNotificationCenter defaultCenter] removeObserver:self
    name:MPMoviePlayerPlaybackStateDidChangeNotification
    object:nil];

    AudioManager::sharedManager()->playBG();

    return;
}

To keep the posts short, I will write about Android part of the Video Player in next post. Once all this is done, setup a Layer (HelloWorldScene.hHelloWorldScene.cpp) with a menu button, whose handler has the following one line of code:

WrapperClass::getShared()->playVideo("sampleVideo");

If everything is done right, you will have a button in screen, which when clicked, stops the game sound track, and plays the video. Once the video is over or skipped, the game sound track resumes.

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-ios/archive/master.zip Complete Project can be found in GITHUB.

Android

Android Video playback is explained in the second part of this tutorial.

Cocos2dx – Spinning Globe by Masking technique

In this post, lets see how we can implement a spinning world in cocos2d-x using masking technique. See the video below for sample output:


Low FPS is because of my Mac Mini; In device, it runs at 60 FPS

Downloads

1. Full Project

1. Create a Cocos2d-x Project

I am not going to discuss about how to setup a cocos2d-x project for iOS here. For info on how to setup cocos2d-x project, refer the official wiki.

All our code will go into `HelloWorld` class that comes with the template.

2. Masking Functionality

The hardest part in achieving our goal would be ‘Masking’ and we resort to Pavel Hancak’s tutorial on cocos2d-x masking. Please check the tutorial on how to mask a sprite. Based on that code, I rewrote the function as below:

CCSprite* HelloWorld::maskedSpriteWithSprite(CCSprite* pTextureSprite, CCSprite* pMaskSprite, float xoffset, float yoffset)
{
    // store the original positions of both sprites
    CCPoint textureSpriteOrigPosition(pTextureSprite->getPosition().x, pTextureSprite->getPosition().y);
    CCPoint maskSpriteOrigPosition(pMaskSprite->getPosition().x, pMaskSprite->getPosition().y);

    // convert the texture sprite position into mask sprite coordinate system
    pTextureSprite->setPosition(ccp(pTextureSprite->getContentSize().width/2 - pMaskSprite->getPosition().x + pMaskSprite->getContentSize().width/2 - xoffset, pTextureSprite->getContentSize().height/2 - pMaskSprite->getPosition().y + pMaskSprite->getContentSize().height/2 + yoffset));

    // position the mask sprite so that the bottom left corner lies on the (o,o) coordinates
    pMaskSprite->setPosition(ccp(pMaskSprite->getContentSize().width/2, pMaskSprite->getContentSize().height/2));

    CCRenderTexture* rt = CCRenderTexture::renderTextureWithWidthAndHeight((int)pMaskSprite->getContentSize().width, (int)pMaskSprite->getContentSize().height);

    ccBlendFunc bfMask = ccBlendFunc();
    bfMask.src = GL_ONE;
    bfMask.dst = GL_ZERO;
    pMaskSprite->setBlendFunc(bfMask);

    // turn off anti-aliasing around the mask sprite
    pMaskSprite->getTexture()->setAliasTexParameters();

    ccBlendFunc bfTexture = ccBlendFunc();
    bfTexture.src = GL_DST_ALPHA;
    bfTexture.dst = GL_ZERO;
    pTextureSprite->setBlendFunc(bfTexture);

    rt->begin();
    pMaskSprite->visit();
    pTextureSprite->visit();
    rt->end();

    // generate the resulting sprite
    CCSprite* pOutcome = CCSprite::spriteWithTexture(rt->getSprite()->getTexture());
    pOutcome->setFlipY(true);

    // restore the original sprite positions
    pTextureSprite->setPosition(textureSpriteOrigPosition);
    pMaskSprite->setPosition(maskSpriteOrigPosition);
    pOutcome->setPosition(maskSpriteOrigPosition);

    return pOutcome;
}

I added two parameters `xoffset` and `yoffset` to the original code. These parameters are to offset the position of underlying map. By varying this offset parameter in gameloop, we move the map linearly over a period of time.

3. Setup Sprites

Add two sprites `map` and `mask`, where

2d world map

`map` contains the full map. Only a part of will be seen at any time

When one edge of the scrolls into the keyhole (mask), we dont want the background to be visible, but the other edge to wrap and fit the gap. For this purpose, we duplicated the map horizontally, and when one edge is within keyhole, the duplicated part will fit in and when the edge crosses the keyhole completely, we reset the map to initial position. (Will see implementation details in subsequent sections).

mask for world map

`mask` picture is like a circular keyhole through which we sees part of a map.

Code to add these two sprites is as follows. Dont add the sprite as child of layer yet.

    
    // in init() function
    map = CCSprite::create("worldmap2d.jpg");
    map->setPosition( ccp(512, 768/2) );
    map->retain();

    mask = CCSprite::create("worldmapmask.png");
    mask->setPosition( ccp(512, 768/2) );
    mask->retain();

Apart from the `map` and `mask`, a star background is added behind.

4. Masking

With Pavel Hancak’s tutorial, masking is now very simple as following:

    // in init() function
    masked = maskedSpriteWithSprite(map, mask, 0, 100);
    addChild(masked);

Only the masked sprite should be added to the `layer`. I found the offsets on trial and error basis 😛 At this point, you should see circular part of the map and not the whole. We masked the map, but still it looks like a flattened map without any depth. To add some depth, add a `shade` picture, above all the sprites as follows

    // in init() function
    shade = CCSprite::create("worldmapshade.png");
    shade->setPosition( ccp(512, 768/2) );
    addChild(shade);
shade over world map

shade to be applied over the world map to create an illusion of depth

5. Spinning Animation

We dont have any real `3D` object here and so we achieve spinning animation by moving the map horizontally behind the keyhole (mask) infinitely. Add this final piece of code and we are done:

    // in init() function
    this->scheduleUpdate();

    void HelloWorld::update(float dt) {
        CCSprite *m;
        xoff += dt * 100;
        if (xoff > 1024) {
            xoff = 0;
        }
        m = maskedSpriteWithSprite(map, mask, xoff, 120);
        masked->setTexture(m->getTexture());
    }

In above code, `xoff` determines the amount of distance that the map should be moved. `Update` is the gamploop, where we increment `xoff` by small amounts in each frame, thus moving the map. When the `xoff` reaches its limit, we reset it. Changing `xoff` doen’t do anything magically. We create a new sprite based on offset position and apply the texture to our `masked` sprite.

Run the project and see the world spinning. Thats the end of the tutorial. Hope it helped :-)

Source can be browsed @ https://github.com/saiy2k/cocos2dx-masking 

Cocos2dx – Platformer Physics Tutorial

I tried implementing the platforming physics logic in cocos2d-x and it came out pretty well as per my expectations. This tutorial is about how I made it. It might not be the best solution out there, but it is something that works. Before diving in, here is the demo video showcasing the final output:

Downloads

1. Mid Project (with just the platforms render)
2. Full Project (all functionalities)

1. Create a Cocos2d-x Project

I am not going to discuss about how to setup a cocos2d-x project for iOS here. I will directly dwelve into the platforming mechanism here. For info on how to setup cocos2d-x project, refer the official wiki.

All our code will go into `HelloWorld` class that comes with the template.

2. Define the Platforms

Add the following struct in HelloWorld.h to model a Line

typedef struct {
    CCPoint p1, p2;
} Line;

Declare a pointer to Line struct in HelloWorld, which will be an array of all platforms and randomly add a few lines in it:

// in HelloWorld.h
Line *lines;

// in init function of HelloWorld.cpp
lines = (Line *)malloc(sizeof(Line) * COUNT);

lines[0].p1 = ccp(100, 100);
lines[0].p2 = ccp(400, 100);

lines[1].p1 = ccp(500, 600);
lines[1].p2 = ccp(700, 600);

lines[2].p1 = ccp(400, 400);
lines[2].p2 = ccp(600, 500);

lines[3].p1 = ccp(500, 200);
lines[3].p2 = ccp(1000, 200);

The platforms are defined well with their points. Now is the time to render them on screen. Add the `draw` function to the `HelloWorld` scene.

// in header file
virtual void draw();

// in cpp file
void HelloWorld::draw() {
    glLineWidth(4.0);
    for (int i = 0; i < COUNT; i++) {
        ccDrawLine( lines[i].p1, lines[i].p2 );
    }
    CCLayer::draw();
}

The above code sets the line width to 4 pixels, loops through our platform array and draws the lines. Our screen should look like this:

3. Character Sprite and Controls

The platforms are setup right. Now lets add the hero sprite which will run through the platforms and two button controls to make the character move. To add the hero and controls, do this:

// in header file
CCSprite* pSprite;
void menuCloseCallback(CCObject* pSender);

// in init function of HelloWorld.cpp
CCMenuItemImage *pCloseItem = CCMenuItemImage::create("CloseNormal.png",
                                                      "CloseSelected.png",
                                                      this,
                                                      menu_selector(HelloWorld::menuCloseCallback) );
pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width - 60, 60) );
pCloseItem->setTag(1);
pCloseItem->setScale(2.5f);

CCMenuItemImage *pCloseItem1 = CCMenuItemImage::create("CloseNormal.png",
                                                       "CloseSelected.png",
                                                       this,
                                                       menu_selector(HelloWorld::menuCloseCallback) );
pCloseItem1->setPosition( ccp(60, 60) );
pCloseItem1->setTag(2);
pCloseItem1->setScale(2.5f);

// controls for left and right movement
CCMenu* pMenu = CCMenu::create(pCloseItem, pCloseItem1, NULL);
pMenu->setPosition( CCPointZero );
this->addChild(pMenu, 1);   

// hero sprite
pSprite = CCSprite::create("CloseNormal.png");
pSprite->setPosition( ccp(600, 700) );
pSprite->setTag(-1);
this->addChild(pSprite, 0);

By now our screen should look like this:

4. Basic Movement

The stage is set with all the objects put in place. Now is the time to start moving the objects. Add two variables `speedX` and `speedY` that controls the movement of our hero and schedule the update function of CCLayer to get into the game loop.

// in header file
virtual void update(float dt);
float speedX, speedY;

// in init function of HelloWorld.cpp
speedX = 0;
speedY = 0;
this->scheduleUpdate();

// in HelloWorld.cpp
// moves the hero around the screen as per the speedX and speedY values
void HelloWorld::update(float dt) {
    CCPoint p = pSprite->getPosition();    
    pSprite->setPosition( ccp( p.x + speedX * dt, p.y + speedY * dt ) );
}

// makes the hero to move to left / right based on the touched control
void HelloWorld::menuCloseCallback(CCObject* pSender) {
    if (((CCNode *)pSender)->getTag() == 1) {
        speedX += 100;
    } else {
        speedX -= 100;
    }
}

Now touching the left control button, increases the movement of the hero in left direction and the same with the right control button. As of now, Movement happens only in x-axis and there is no gravity (falling down), friction and collision. The project at this point can be downloaded here for reference.

5. Physics Magic

First replace the `update` function with following code.

void HelloWorld::update(float dt) {
    CCPoint p = pSprite->getPosition();

    speedX *= 0.95; // friction reduces the speed over time

    if (pSprite->getTag() == -1) { // free falling
        CCLog("searching for platform... hero is falling");
        // iterate through all platforms
        for (int i = 0; i < COUNT; i++) {
            // if hero position lies between the x bounds of any platform
            if ( lines[i].p1.x < p.x && lines[i].p2.x > p.x ) {
                // also it is closer than 100 pixels to the platform
                if ( p.y - lines[i].p1.y < 100 && p.y - lines[i].p1.y > 0) {
                    // assign the platform to the hero
                    pSprite->setTag(i);
                    break;
                }
            }
        }
        // gravity pull decreases the y-speed
        speedY -= 10.0;

        // not allowing hero to go down indefinitely, by making him swap to the top
        if ( p.y < 0 ) {
             p.y = 768;
        }
    } else { // resting in or closer to some platform
        int tag = pSprite->getTag();
        CCLog("in platform %d", tag);

        // make sure if hero is still in the x bounds of the platform or moved away
        if ( lines[tag].p1.x < p.x && lines[tag].p2.x > p.x ) {
            float x2, y2;
            x2 = p.x;
            // platform may be a sloppy one, so interpolate the y value based on end points of platform and current hero position 
            y2 = (x2 - lines[tag].p1.x) * (lines[tag].p2.y - lines[tag].p1.y) / (lines[tag].p2.x - lines[tag].p1.x) + lines[tag].p1.y;
            if (p.y - y2 > 0) { // if hero is above platform, make him fall down
                speedY -= 10.0;
            } else {            // nullify the y speed and rest on platform
                speedY = 0;
                p.y = y2;
            }
        } else { // switch to free fall mode, if hero is moved away
            pSprite->setTag(-1);
        }
    }

    pSprite->setPosition( ccp( p.x + speedX * dt, p.y + speedY * dt ) );
}

I believe the above code is self-explanatory with all the comments put in. So I will just add few notes that deserve some explanation here.

Tag

`tag` property of Hero is used to refer to the platform he is in. If he is free falling (i.e., not attached to any platform) then `tag` will be -1.

Friction

 speedX *= 0.95;

Above code decrements the x speed of the hero by 5% on each update, thus simulating the friction effect.

Interpolation

If all the lines are perfectly horizontal, then y values of the end points or any other point in that line would be equal. If the lines are sloppy, things would be different and we need to calculate the y-value of any arbitrary point in the given line for the given x. If its confusing, see the picture below and read on.


For any platform, we have the end points A (x1, y1) and B (x2, y2) and we have the position of the hero represented here as H(hx, hy). But to check if the hero is above or below the line, we need the point that lies in the line which is P(x, y). Of course, x would be same as hx, but how to find y? Not to worry, we can interpolate the y value with the following formula:

y = y1 + (hx - x1) * (y2 - y1) / (x2 - x1)

The above formula is translated to CPP in our code with our variables as follows:

y2 = (x2 - lines[tag].p1.x) * (lines[tag].p2.y - lines[tag].p1.y) / (lines[tag].p2.x - lines[tag].p1.x) + lines[tag].p1.y;

Thats the end of the tutorial and here is the full project. This project can be easily extended to 2d side scrolling platformer games. I will try to write a post on that in future. There might be several places where a beginner can easily get lost. Post the queries in comments and I will try to resolve.

C program to implement evaluation of Postfix expression using Stack

Program:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct node {
    double element;
    struct node *next;
} *head;

void push(int c);      // function to push a node onto the stack
int pop();             // function to pop the top node of stack
void traceStack();      // function to //print the stack values

int main() {
    int i = 0, j = 0;   // indexes to keep track of current position for input output strings
    char *exp = (char *)malloc(sizeof(char)*100);
    double res = 0;
    char tmp;
    head = NULL;

    printf("Enter the postfix expression: ");
    scanf("%s", exp);

    while( (tmp=exp[i++]) != '\0') {    // repeat till the last null terminator
        // if the char is operand, pust it into the stack
        if(tmp >= '0' && tmp <= '9') {
            int no = tmp - '0';
            push(no);
            continue;
        }

        if(tmp == '+') {
            int no1 = pop();
            int no2 = pop();
            push(no1 + no2);
        } else if (tmp == '-') {
            int no1 = pop();
            int no2 = pop();
            push(no1 - no2);
        } else if (tmp == '*') {
            int no1 = pop();
            int no2 = pop();
            push(no1 * no2);
        } else if (tmp == '/') {
            int no1 = pop();
            int no2 = pop();
            push(no1 / no2);
        }
    }

    printf("Result of the evalution is %d", pop());
    return 0;
}

void push(int c) {
    if(head == NULL) {
        head = malloc(sizeof(struct node));
        head->element = c;
        head->next = NULL;
    } else {
        struct node *tNode;
        tNode = malloc(sizeof(struct node));
        tNode->element = c;
        tNode->next = head;
        head = tNode;
    }
}

int pop() {
    struct node *tNode;
    tNode = head;
    head = head->next;
    return tNode->element;
}

Sample Output:

Enter the postfix expression: 65*3+
Result of the evalution is 33

C program to implement infix to postfix expression

Program:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct node {
    char element;
    struct node *next;
} *head;

void push(char c);      // function to push a node onto the stack
char pop();             // function to pop the top node of stack
int precedence(char c); // function to find the precedence of an operator
void traceStack();      // function to //print the stack values

int main() {
    int i = 0, j = 0;   // indexes to keep track of current position for input output strings
    char *exp = (char *)malloc(sizeof(char)*100);
    char *res = (char *)malloc(sizeof(char)*100);
    char tmp;
    head = NULL;

    printf("Enter the infix expression: ");
    scanf("%s", exp);

    while( (tmp=exp[i++]) != '\0') {    // repeat till the last null terminator
        // if the char is operand, copy it to output string
        if(tmp >= 97 && tmp <= 122) {
            res[j++] = tmp;
            continue;
        }

        if(tmp == '(' || tmp == '[' || tmp == '{') {
            push(tmp);
            continue;
        }

        if(tmp == ')' || tmp == ']' || tmp== '}') {
            char cl, tp;
            if(tmp == ')') cl = '(';
            if(tmp == '}') cl = '{';
            if(tmp == ']') cl = '[';

            tp = pop();

            while(tp != cl) {
                res[j++] = tp;
                tp = pop();
            }
            continue;
        }

        // if char is operator
        if(tmp == '+' || tmp == '-' || tmp == '*' || tmp == '/') {
            if(head == NULL) {
                push(tmp);
            } else {
                // if operator at top of stack has high precedence, pop it and
                // add to output string, else just push the operator
                while(precedence(tmp) <= precedence(head->element) && head->element != '(' && head->element != '[' && head->element != '{') {
                    char tp = pop();
                    res[j++] = tp;
                    if(head == NULL)
                        break;
                }
                push(tmp);
            }
        }
    }

    // pop all the operators from stach to output string
    while (head != NULL) {
        res[j++] = pop();
    };
    res[j++] = '\0';

    printf("Postfix expression is %s\n\n", res);
    return 0;
}

void push(char c) {
    if(head == NULL) {
        head = malloc(sizeof(struct node));
        head->element = c;
        head->next = NULL;
    } else {
        struct node *tNode;
        tNode = malloc(sizeof(struct node));
        tNode->element = c;
        tNode->next = head;
        head = tNode;
    }
}

char pop() {
    struct node *tNode;
    tNode = head;
    head = head->next;
    return tNode->element;
}

int precedence(char c) {
    if (c == '*' || c == '/')
        return 2;
    else if (c == '+' || c == '-')
        return 1;
    else
        return 5;
}

void traceStack() {
    struct node *tNode;
    tNode = head;
    while(tNode != NULL) {
        printf("%c --> ", tNode->element);
        tNode = tNode->next;
    }
    printf("\n");
}

Sample Output:

Enter the infix expression: (a+b)*(d+e)
Postfix expression is ab+de+*

C Program to Check Leap Year

Program Logic:

If the year is divisible by 4, it is a leap year. But, if it is divisible by 100, it is not. If the year is divisible by 400, it is a leap year.

Examples:

1. 1996 is a leap year. (divisible by 4)
3. 2100 is a not leap year. (divisible by 100)
2. 2000 is a leap year. (divisible by 100 but also divisible by 400)

Program:

#include<stdio.h>
#include<conio.h>

void main()
{
int year;
clrscr();
printf(“Enter a Year to check : “);
scanf(“%d”, &year);

if(year % 400 == 0)
printf(“%d is a Leap Year.”, year);
else if(year % 100 == 0)
printf(“%d is not a Leap Year.”, year);
else if(year % 4 == 0)
printf(“%d is a Leap Year.”, year);
else
printf(“%d is not a Leap Year”, year);

printf(“\nPress any key to Quit…”);
getch();
}