Up to this point we have been laying the foundation for developing a game. So far we've setup a basic structure to handle common routines, we've setup a special class to handle events, and we've also setup a class to handle a few surface functions. In this tutorial we'll take all those things, combine them, and create a tic-tac-toe game. Don't worry, things should be pretty simple. Use the last tutorial to build off of.

The first thing we are going to need to do is plan our game. From experience, we know that tic-tac-toe has a 3x3 grid, where you place X's and O's. So, we know that we will need 3 graphics, one for the grid, one for the X, and one for the O. We don't need multiples of the X or O, because we can draw them in the program as many times as we like. Lets eliminate this first step. Our grid is going to be 600x600, and our X's and O's will be 200x200 (1/3 of the area).

Grid
OX

Now that we have our images, we are going to need a way to load them into our program. Open up CApp.h and make some modifications. Remove the Test Surface, and add three new surfaces.
#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

#include "CEvent.h"
#include "CSurface.h"

class CApp : public CEvent {
    private:
        bool            Running;

        SDL_Surface*    Surf_Display;

    private:
        SDL_Surface*    Surf_Grid;

        SDL_Surface*    Surf_X;
        SDL_Surface*    Surf_O;

    public:
        CApp();

        int OnExecute();

    public:
        bool OnInit();

        void OnEvent(SDL_Event* Event);

                void OnExit();

        void OnLoop();

        void OnRender();

        void OnCleanup();
};

#endif
Also, open up CApp.cpp and make some modifications. Remove the Test Surface again, and add the three new ones again.
#include "CApp.h"

CApp::CApp() {
    Surf_Grid = NULL;
    Surf_X = NULL;
    Surf_O = NULL;

    Surf_Display = NULL;

    Running = true;
}

int CApp::OnExecute() {
    if(OnInit() == false) {
        return -1;
    }

    SDL_Event Event;

    while(Running) {
        while(SDL_PollEvent(&Event)) {
            OnEvent(&Event);
        }

        OnLoop();
        OnRender();
    }

    OnCleanup();

    return 0;
}

int main(int argc, char* argv[]) {
    CApp theApp;

    return theApp.OnExecute();
}
And, you guessed it, open up CApp_OnCleanup.cpp to make some modifications there too. Just like before, get rid of the Test Surface and add the three new ones.
#include "CApp.h"

void CApp::OnCleanup() {
    SDL_FreeSurface(Surf_Grid);
    SDL_FreeSurface(Surf_X);
    SDL_FreeSurface(Surf_O);
    SDL_FreeSurface(Surf_Display);
    SDL_Quit();
}
Now that we have the surfaces setup, lets load them into memory. Open up CApp_OnInit.cpp, and make some changes. Get rid of the test surface (again), and load the three new ones. Be sure to put the correct filenames. Also, change the dimensions of the window to 600x600, the size of the grid. This make sure we don't have any blank space around the window we aren't using.
#include "CApp.h"

bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }

    if((Surf_Display = SDL_SetVideoMode(600, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
        return false;
    }

    if((Surf_Grid = CSurface::OnLoad("./gfx/grid.bmp")) == NULL) {
    return false;
    }

    if((Surf_X = CSurface::OnLoad("./gfx/x.bmp")) == NULL) {
    return false;
    }

    if((Surf_O = CSurface::OnLoad("./gfx/o.bmp")) == NULL) {
    return false;
    }

    return true;
}
You may have noticed a change I made in the filenames. I added ./gfx/ before the filename to specify which folder the graphics are in. As games begin to grow it's very practical to have all the files in one folder. Because of such, from hereon, all images will be put in the gfx folder. Now, let's get the grid showing up on the screen. Open up CApp_OnRender.cpp and change the test surface rendering to make the grid render.
#include "CApp.h"

void CApp::OnRender() {
    CSurface::OnDraw(Surf_Display, Surf_Grid, 0, 0);

    SDL_Flip(Surf_Display);
}
Try compiling your program, and if successful, you should see the grid show up. Remember, there are basically 5 steps to using a surface: declare it, set it to NULL, load it, draw it, and then free it. It's good practice to learn all these 5 steps now, because later on if you neglect one of these steps if can cause problems. For example, neglecting to set a surface to NULL can cause undefined behavior, or neglecting to free a surface can cause a memory leak.

You may have noticed something odd about the graphics we are using, the X and O contain a pink background. There is a reason for it, we are going to implement transparency onto these surfaces. Basically, wherever there is pink, it will show through; we will make the pink color transparent. SDL offers a simple function to do this, SDL_SetColorKey. To implement this, open up CSurface.h so we can add a new function.
#ifndef _CSURFACE_H_
    #define _CSURFACE_H_

#include <SDL.h>

class CSurface {
    public:
        CSurface();

    public:
        static SDL_Surface* OnLoad(char* File);

        static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y);

        static bool OnDraw(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src, int X, int Y, int X2, int Y2, int W, int H);

        static bool Transparent(SDL_Surface* Surf_Dest, int R, int G, int B);
};

#endif
Now, to implement this function, open up CSurface.cpp and add the function:
bool CSurface::Transparent(SDL_Surface* Surf_Dest, int R, int G, int B) {
    if(Surf_Dest == NULL) {
        return false;
    }

    SDL_SetColorKey(Surf_Dest, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(Surf_Dest->format, R, G, B));

    return true;
}
Notice 3 extra arguments being passed besides the surface. These are the 3 color values that we want to make transparent, it doesn't have to be just pink. For instance, it we wanted red to be transparent it would be 255, 0, 0.

This function first checks to see if we have a valid surface. If so, we set a color key (transparency) for a color. The first argument is the surface to apply the color key to, the second is some flags telling SDL how to perform the operation, and the third is the color to make transparent. The flags being applied are basic, the first tells SDL to apply the color key to the source (the surface being passed) and the second tells SDL to try to use RLE acceleration (basically, try to make drawing later on faster). The third argument is a little bit more complex; we are using SDL_MapRGB in order to create a color. SDL_MapRGB takes a surface, and your requested color (R, G, B), and tries to match it as close as it can to that surface. You might be thinking why this is useful. Not all surfaces have the same color palette. Remember the old NES days where there was only a few colors that could be used? Same idea here, SDL_MapRGB takes a color and matches it with the closest color on that surface palette.

Lets apply this new function to our surfaces now, open up CApp_OnInit.cpp and make the following changes:
#include "CApp.h"

bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }

    if((Surf_Display = SDL_SetVideoMode(600, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
        return false;
    }

    if((Surf_Grid = CSurface::OnLoad("./gfx/grid.bmp")) == NULL) {
        return false;
    }

    if((Surf_X = CSurface::OnLoad("./gfx/x.bmp")) == NULL) {
        return false;
    }

    if((Surf_O = CSurface::OnLoad("./gfx/o.bmp")) == NULL) {
        return false;
    }

    CSurface::Transparent(Surf_X, 255, 0, 255);
    CSurface::Transparent(Surf_O, 255, 0, 255);

    return true;
}
Everything for the surfaces should be set. The next thing we have to do is figure out a way to draw these X's and O's. We can't just draw them everywhere on the grid because they won't always be in the same spots. What we are going to have to do is make an array of 9 containers, the values in this array will tell us the values for each cell on the grid. So, spot 0 would be the top left, 1 would be the top middle, 2 the top right, 3 the middle left, and so on. Create this array by adding it to CApp.h:
#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

#include "CEvent.h"
#include "CSurface.h"

class CApp : public CEvent {
    private:
        bool            Running;

        SDL_Surface*    Surf_Display;

    private:
        SDL_Surface*    Surf_Grid;

        SDL_Surface*    Surf_X;
        SDL_Surface*    Surf_O;

    private:
        int        Grid[9];

    public:
        CApp();

        int OnExecute();

    public:
        bool OnInit();

        void OnEvent(SDL_Event* Event);

            void OnExit();

        void OnLoop();

        void OnRender();

        void OnCleanup();
};

#endif
We know that each cell can have three possible values: Empty, X, and O. These tell us what is currently in that cell. To make things a little neater than having 0, 1, 2 as values, we'll use an enum instead. If you are umfamiliar with how an enum works, try finding a quick tutorial on them. Just know that GRID_TYPE_NONE = 0, GRID_TYPE_X = 1, and GRID_TYPE_O = 2. Go back to CApp.h and add the following underneath the Grid array:
enum {
    GRID_TYPE_NONE = 0,
    GRID_TYPE_X,
    GRID_TYPE_O
};
Note, up to this point I have been displaying practically all the code when I am referring to different files. From hereon, I expect you to know where code goes. Most of the time I will tell you where to place it by, and sometimes I might display all the code. Now that we have a way to know what a cell is, we're going to need a way to reset the board. Let's create a new function at the bottom inside of CApp.h called Reset.
public:
    void Reset();
Open up CApp.cpp and add the following function just before int main:
void CApp::Reset() {
    for(int i = 0;i < 9;i++) {
        Grid[i] = GRID_TYPE_NONE;
    }
}
This loop will set every cell in the grid to GRID_TYPE_NONE, meaning that all the cells are empty. We're going to have to do this at the very beginning when our program is loaded, so make a call to this function within the CApp_OnInit.cpp file:
//...

CSurface::Transparent(Surf_X, 255, 0, 255);
CSurface::Transparent(Surf_O, 255, 0, 255);

Reset();
So far so good. The next thing we are going to have to do is make the ability to place X's and O's on the screen. Lets create a new function that will handle this. Open up CApp.h again and add the function just below Reset.
void SetCell(int ID, int Type);
Now, open back up CApp.cpp and add the function:
void CApp::SetCell(int ID, int Type) {
    if(ID < 0 || ID >= 9) return;
    if(Type < 0 || Type > GRID_TYPE_O) return;

    Grid[ID] = Type;
}
This function takes two arguments, the first is the cell ID to change, and the second is the type to change it to. We have to conditions here, the first is to make sure we don't go outside the bounds of the array (if we did it would likely crash our program), and the second is to make sure we passed an appropriate type. Simple enough. Now, let's implement a way to draw the X's and O'. Open up CApp_OnRender and add the following code just after the grid:
for(int i = 0;i < 9;i++) {
    int X = (i % 3) * 200;
    int Y = (i / 3) * 200;

    if(Grid[i] == GRID_TYPE_X) {
        CSurface::OnDraw(Surf_Display, Surf_X, X, Y);
    }else
    if(Grid[i] == GRID_TYPE_O) {
        CSurface::OnDraw(Surf_Display, Surf_O, X, Y);
    }
}
This is a little bit more complex than we have been used to so far. Firstly, we are looping through each cell in the grid. Next, we are translating that grid ID over to X and Y coordinates. We do this in two different ways. To find X, we take the remainder of i to 3. This will give us 0 when i is 0, 1 when i is 1, 2 when i is 2, 0 when i is 3, and so on. We multiply it by 200 because each cell is 200x200 pixels. To find Y, we divide by 3, this causes Y to be 0 when i is 0, 1, 2, Y to be 1 when i is 3, 4, 5, and so on. We then multiply that by 200 as well. I encourage you to really try to understand what is going on here because this sort of method is used for tile based games.

Next, all we have to do is check the cell type, and then draw the correct surface at that cell.

Now that we have our surfaces drawing, we'll need a way to communicate from the user to the computer. We'll use mouse events for this. When the users clicks a cell it will set the cell appropriately. We are going to need to overload one of the CEvent functions for this. Open up CApp.h and add the following function just below OnEvent and next to OnExit.
void OnLButtonDown(int mX, int mY);
Now, open up CApp_OnEvent.cpp and add the function:
void CApp::OnLButtonDown(int mX, int mY) {
    int ID    = mX / 200;
    ID = ID + ((mY / 200) * 3);

    if(Grid[ID] != GRID_TYPE_NONE) {
        return;
    }

    if(CurrentPlayer == 0) {
        SetCell(ID, GRID_TYPE_X);
        CurrentPlayer = 1;
    }else{
        SetCell(ID, GRID_TYPE_O);
        CurrentPlayer = 0;
    }
}
First, we are doing the reverse of what we did with translating to X and Y from an ID, this time we are translating to an ID. We then make sure that that cell hasn't already been taken, if it has, we return out of the function. Next, we are checking which players turn it is, set the cell appropriately, and then switch turns. CurrentPlayer is a new variable to specify whose turn it is, so we'll need to add this. Open up CApp.h and add the variable below the grid array:
int CurrentPlayer;
Also, set the default value for this variable in CApp.cpp:
CApp::CApp() {
    CurrentPlayer = 0;

    Surf_Grid = NULL;
    Surf_X = NULL;
    Surf_O = NULL;

    Surf_Display = NULL;

    Running = true;
}
Try compiling the program and you should have a mostly working version of tic-tac-toe. Congratulations!

This is where you take the rest of it yourself. We have a solid foundation for our game, and most the work done. I encourage you to take this a step further. Try adding a "X Won", "O Won", and "Draw" at the end of each game (extra images here). Think, how are you going to check who won (a function for this purpose would fit well)? Try adding a way to reset the game after it's done. If you are brave, try to add some generic AI that will play against the user. And if you are even braver, try adding the ability to play player vs. player, or player vs. computer.

When you are ready, and have a firm grasp of this tutorial, move on to the next lesson to look at frame animation.