SDL Tutorial – Tic Tac Toe
SDL Tutorials November 3rd, 2007Up 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 3×3 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 600×600, and our X’s and O’s will be 200×200 (1/3 of the area).



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.
#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.
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.
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 600×600, the size of the grid. This make sure we don’t have any blank space around the window we aren’t using.
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.
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.
#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:
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:
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:
#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:
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.
void Reset();
Open up CApp.cpp and add the following function just before int main:
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.
Now, open back up CApp.cpp and add the function:
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:
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 200×200 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.
Now, open up CApp_OnEvent.cpp and add the function:
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:
Also, set the default value for this variable in CApp.cpp:
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.
SDL Tutorial – Tic Tac Toe – Tutorial Files:
Win32: Zip, Rar
Linux: Tar (Thanks Gaten)












November 15th, 2007 at 7:19 am
Very nice tutorial. I like your style – it’s really easy to follow along.
Keep up the good work!
January 11th, 2008 at 6:34 pm
Great work man! Looking forward to more!
February 23rd, 2008 at 5:33 am
Awesome work man! Really helpful! Keep it up
February 28th, 2008 at 12:04 am
Linux source: http://gaten.homelinux.net/sdltuts/tic_tac_toe.tar.gz
March 28th, 2008 at 4:07 am
gaten down?
March 28th, 2008 at 5:52 pm
Sorry about that, it’s all fixed now. Thanks for letting me know.
April 5th, 2008 at 7:24 pm
Excellent tutorial. These are extremely helpful, keep it up!
May 6th, 2008 at 7:07 pm
I’m running into a problem with the transparencies for this tutorial.
After searching online, it seems that i need to make the call to SDL_SetColorKey(…) (in our transparent function) before the call to SDL_DisplayFormatAlpha(..) (in the OnLoad function).
Noting this, would it be best to make another OnLoad function that takes a transparent value and calls SDL_SetColorKey(…) just before the call to SDL_DisplayFormatAlpha?
also, i’m curious if anyone knows why i need to do this compared to everyone else.
May 7th, 2008 at 7:30 pm
The reason you need to call SDL_SetColorKey before SDL_DisplayFormatAlpha, is because the SDL_SetColorKey will make an alpha (transparency layer) on your surface. Otherwise, you would use SDL_DisplayFormat for surfaces without an alpha layer. With that in mind, I recommend dumping SDL_SetColorKey all together, as it is not necessary if you use PNG files. Take a look at my SDL_image side tutorial, and by making alpha layers in a paint program in the PNG directly, you won’t have to mess with it using SDL_SetColorKey. Also, by using PNGs you can have the alpha layer be any value you want (such as 50% transparent). Which is a good way to make lights. I hope that helps.
May 8th, 2008 at 2:20 pm
Thank you Tim. That makes sense. In fact i think that’s why i was experiencing my original problem.
I had done your SDL_Image tutorial before i attempted this tic-tac-toe . So when i did this tutorial i used png files and the SDL_DisplayFormatAlpha, and didn’t notice that you were still using BMP files and the SDL_DiaplyFormat call.
So the way i understand it now is that you can call our transparency function on surfaces AFTER the SDL_DisplayFormat call (if used), but when using SDL_DisplayFormatAlpha we must make the SDL_SetColorKey call BEFORE.
But, as you are saying, just using an image that already has the alpha channel set and calling SDL_DisplayFormatAlpha is our best method.
May 16th, 2008 at 10:54 am
Yep! It’s best to just do away with the SDL_SetColorKey, and just use PNGs, you’ll save yourself some extra coding.
June 2nd, 2008 at 5:56 am
Hi!
Could someone post correct function witch would check if anyone won ?
I try do it by simple if conditions but it doesn’t work (game exit when first move is made, witch should happen only when someone wins).
June 2nd, 2008 at 2:01 pm
I’ll try writing one now.
June 2nd, 2008 at 2:29 pm
Here’s my attempt (which works):
To the class definition of CApp, add the following attributes:
private int WinsX;
private int WinsY;
and the following methods:
public void SetWinner();
public void CongratulateWinner(int);
In CApp::CApp(), set WinsX and WinsY to 0.
In CApp::OnLoop(), call the function SetWinner().
Create the function CApp::SetWinner() under CApp::SetCell(). Here is the code for it:
void CApp::SetWinner() {
int Winner = 0;
if (Grid[0] == Grid[1] && Grid[1] == Grid[2] && Grid[2] != GRID_TYPE_NONE) {
Winner = Grid[0];
}
if (Grid[3] == Grid[4] && Grid[4] == Grid[5] && Grid[5] != GRID_TYPE_NONE) {
Winner = Grid[3];
}
if (Grid[6] == Grid[7] && Grid[7] == Grid[8] && Grid[8] != GRID_TYPE_NONE) {
Winner = Grid[6];
}
if (Grid[0] == Grid[3] && Grid[3] == Grid[6] && Grid[6] != GRID_TYPE_NONE) {
Winner = Grid[0];
}
if (Grid[1] == Grid[4] && Grid[4] == Grid[7] && Grid[7] != GRID_TYPE_NONE) {
Winner = Grid[1];
}
if (Grid[2] == Grid[5] && Grid[5] == Grid[8] && Grid[8] != GRID_TYPE_NONE) {
Winner = Grid[2];
}
if (Grid[0] == Grid[4] && Grid[4] == Grid[8] && Grid[8] != GRID_TYPE_NONE) {
Winner = Grid[0];
}
if (Grid[2] == Grid[4] && Grid[4] == Grid[6] && Grid[6] != GRID_TYPE_NONE) {
Winner = Grid[2];
}
if (Winner != 0) CongratulateWinner(Winner);
}
It creates a variable Winner which is set to 0 by default, so that if no winner is chosen (i.e. if the game is still going on and no one has three in a row), nothing happens (by default). Then it checks each of the rows and columns and diagonals, and if there is a three in a row somewhere of something that is NOT GRID_TYPE_NONE (i.e. an empty space), it sets Winner to whatever is in one of those occupied grids, and at the end calls the function CApp::CongratulateWinner(), with a parameter of whoever the Winner was (either 1 or 2, which are X and O respectively).
Create the function CApp::CongratulateWinner() under CApp::SetWinner(), and here’s the code:
void CApp::CongratulateWinner(int Winner) {
if (Winner == 1) {
WinsX++;
Reset();
} else {
WinsO++;
Reset();
}
}
It simply checks who the Winner was from CApp::SetWinner() and increments their score up by one, and then resets the board. Easy enough.
Finally, if you want, you can modify CApp::OnExit(), after the Running = false; line, to display an image, display text, output to a text file, or scream the name of (with sound haha) the TOTAL winner (whoever had the highest score in terms of WinsX and WinsY).
June 3rd, 2008 at 2:16 pm
Hey these r great im learning alot best tut`s around but i have ran into a problem with this one i get an error message like:
\CApp.cpp `amp’ undeclared (first use this function)
i done all the other tutorials and didnt have any problems
anyone help me? please
June 3rd, 2008 at 2:27 pm
I think you accidentally putting in amp instead of the ampersand sign &.
June 3rd, 2008 at 2:29 pm
Just thought I would mention this, if you are having trouble with code in some area, download the project files, and compare with that code. I always ensure that the code I put online compiles, so if you cannot get it to compile than you most likely mistyped something somewhere. Sometimes you’ll have just a simple syntax error, like putting a + instead of a *, or forgetting a character. Be careful when typing your code.
June 3rd, 2008 at 3:43 pm
Nice reference to [one of] my mistake[s] earlier
June 3rd, 2008 at 4:58 pm
oooh thanks i got it fixed i was copy and pasting the code but this line failed while(SDL_PollEvent(&Event)) { i changed it to while(SDL_PollEvent(&Event)) { and it worked fine thank you for your help
June 4th, 2008 at 2:10 am
THX Arseniy.
I have one more question why:
Grid[2] == Grid[4] && Grid[4] == Grid[6] works fine, but this:
Grid[2] == Grid[4] == Grid[6] don’t ??
June 4th, 2008 at 6:47 am
Arseniy, Haha, yes.
irishstevie, No problem. For people reading out there, he had an & amp; instead of a single &.
June 4th, 2008 at 6:50 am
przemo_li,
Grid[2] == Grid[4] == Grid[6] is made up of two interconnected logical operators, but C++ doesn’t allow that; you have to put two together with &&, ||, etc.
June 14th, 2008 at 10:39 am
my game works fine when i run it from inside codeblocks, but if i just run the executable the game window disappears almost instantly, can anyone tell me what i am doing wrong?
June 18th, 2008 at 5:02 am
Hello and thank you for these excellent tutorials.
I’ve been following these tutorials for some weeks now and here is my contribution :
http://www.zaroui.net/myCase.zip
The archive is in 7z format so just change the extension to get it open.
I contains VS2005 source code, project file, gfx stuff and a compiled win32 version.
This checks for game end and highlights the winning line. I also has basic buttons to choose the players (not yet implemented).
WIP for the IA part, I’ll post soon my brainy TicTacToe.
Yassine – France
June 18th, 2008 at 10:20 am
hans, your codeblocks compiles to a subdirectory called “release” or “debug” and when you run from codeblocks it uses paths relative to source code location and not executable location.
So to get your compiled file to work outside codeblocks, just copy it to where your .cpp files are.
Yassine
June 18th, 2008 at 3:00 pm
Hi,
one more beginner here (beginner in programming in general)!
i’ve also tried to make function to report winner and ofcourse something is wrong
first in CApp class i added:
SDL_Surface* X_wins;
SDL_Surface* O_wins;
and function:
int CheckWinner();
which looks something like:
int CApp::CheckWinner (){
if (Grid[0]==Grid[1] && Grid[1]==Grid[2] && Grid[0]!=GRID_TYPE_NONE) return Grid[0];
if (Grid[3]==Grid[4] && Grid[4]==Grid[5] && Grid[3]!=GRID_TYPE_NONE) return Grid[3];
if (Grid[6]==Grid[7] && Grid[7]==Grid[8] && Grid[6]!=GRID_TYPE_NONE) return Grid[6];
if (Grid[0]==Grid[3] && Grid[3]==Grid[6] && Grid[0]!=GRID_TYPE_NONE) return Grid[0];
if (Grid[1]==Grid[4] && Grid[4]==Grid[7] && Grid[1]!=GRID_TYPE_NONE) return Grid[1];
if (Grid[2]==Grid[5] && Grid[5]==Grid[8] && Grid[2]!=GRID_TYPE_NONE) return Grid[2];
if (Grid[0]==Grid[4] && Grid[4]==Grid[8] && Grid[0]!=GRID_TYPE_NONE) return Grid[0];
if (Grid[2]==Grid[4] && Grid[4]==Grid[6] && Grid[2]!=GRID_TYPE_NONE) return Grid[2];
return 0;
}
and in OnRender i added something like this:
if (CApp::CheckWinner()== GRID_TYPE_X) {
CSurface::OnDraw(Surf_Display, X_wins, 75, 100);
}else
if (CApp::CheckWinner()== GRID_TYPE_O) {
CSurface::OnDraw(Surf_Display, O_wins, 75, 100);
}
i’ve also added loading images X_wins and O_wins in OnInit
it does not work, so my question wold be if this could work like this at all (calling the function within OnRender and drawing appropriate image)?
June 18th, 2008 at 4:15 pm
Why are you using the :: qualifier to call it
if (CApp::CheckWinner()== GRID_TYPE_X)
Is it a static function?
June 19th, 2008 at 2:02 am
sorry, i forgot deleting that, just ignore it.
…that was not originaly there, i was checking for posible mistakes so i was just trying it out (even though i knew it wouldn’t help)
(btw- sorry for my not so good english)
June 19th, 2008 at 2:32 am
…it is working (for now)!
i checked that the names of images (while loading) were correct like 10 times and of course they were still wrong
they are okey now! finaly!
June 20th, 2008 at 6:48 am
Yassine Zaroui,
Thanks for your contribution and helping others out! Seems that people have been busy posting this last week.
cassiopeia,
Glad that you got it working; and keep on programming!
September 23rd, 2008 at 12:42 pm
heyy i was just adding more to this, like checking for winner but every way i try it never puts in the last image eg. O | O | then it does the congrates function without doing the last drawing, even if i put in a SDL_Delay(1000);
Bit of help please.
Many thanks!!
September 23rd, 2008 at 2:36 pm
to the problem above i checked through the code i was calling OnLoop() before OnRender() in the CApp::OnExecute() function. Swapping them around fixed it
September 24th, 2008 at 10:02 am
Nice!
Glad you figured it out.
December 9th, 2008 at 10:14 pm
Most excellent tutorials here, they’ve helped me immensely. I’ve written some simple AI and a Reset and a way to switch from Single to Multiplayer. The AI is very similar to the winner checking function, except that it sometimes just picks a random square.
AI.cpp:
#include "CApp.h"
void CApp::AI_Loop(){
{
if (Number_Of_Players < 2) {
//The computer takes O.
while(CurrentPlayer == 1){
if (Grid_count >
break;}
if (Grid_count > 2) {
if ((Grid[0] != GRID_TYPE_NONE) && (Grid[0] == Grid[4]) && (Grid[8] == GRID_TYPE_NONE))
{
Move(8);
return;}
else if ((Grid[4] != GRID_TYPE_NONE) && (Grid[4] == Grid[8]) && (Grid[0] == GRID_TYPE_NONE))
{
Move(0);
return;}
else if ((Grid[2] != GRID_TYPE_NONE) && (Grid[2] == Grid[4]) && (Grid[6] == GRID_TYPE_NONE))
{
Move(6);
return;}
else if ((Grid[6] != GRID_TYPE_NONE) && (Grid[4] == Grid[6]) && (Grid[2] == GRID_TYPE_NONE))
{
Move(2);
return;}
else if ((Grid[0] != GRID_TYPE_NONE) && (Grid[0] == Grid[8]) && (Grid[4] == GRID_TYPE_NONE))
{
Move(4);
return;}
else if ((Grid[2] != GRID_TYPE_NONE) && (Grid[2] == Grid[6]) && (Grid[4] == GRID_TYPE_NONE))
{
Move(4);
return;}
else{
for (int ID = 0; ID < 7; ID++){
int row = ID / 3;
int col = ID % 3;
if (col == 0) {
if ((Grid[ID] != GRID_TYPE_NONE) && (Grid[ID] == Grid[ID + 1]) && (Grid[ID+2] == GRID_TYPE_NONE))
{
Move(ID+2);
return;}
else if ((Grid[ID+1] != GRID_TYPE_NONE) && (Grid[ID+1] == Grid[ID+2]) && (Grid[ID] == GRID_TYPE_NONE))
{
Move(ID);
return;}
else if ((Grid[ID +2] != GRID_TYPE_NONE) && (Grid[ID] == Grid[ID+2]) && (Grid[ID+1] == GRID_TYPE_NONE))
{
Move(ID+1);
return;}
}
else if (row == 0) {
if ((Grid[ID] != GRID_TYPE_NONE) && (Grid[ID] == Grid[ID+3]) && (Grid[ID+6] == GRID_TYPE_NONE))
{
Move(ID+6);
return;}
else if ((Grid[ID+3] != GRID_TYPE_NONE) && (Grid[ID+3] == Grid[ID+6]) && (Grid[ID] == GRID_TYPE_NONE))
{
Move(ID);
return;}
else if ((Grid[ID+6] != GRID_TYPE_NONE) && (Grid[ID] == Grid[ID+6]) && (Grid[ID+3] == GRID_TYPE_NONE))
{
Move(ID+3);
return;}
}
}
}
srand(time(NULL));
for (int i = rand() % 9; i<9; i++){
if(Grid[i] == GRID_TYPE_NONE){
Move(i);
break;
}
else{ i = rand() % 9;}
}
}
//Should happen when all else fails. Replace with C rand function later.
else {
for (int i = rand() % 9; i<9; i++)
{
if(Grid[i] == GRID_TYPE_NONE){
Move(i);
break;
}
else { i = rand() % 9;}
}
}
}
}
}
I added a variable Number_Of_Players to CApp. To switch players during gameplay, there’s this function (added to OnEvent):
void CApp::OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode) {
if (sym == SDLK_1) {
Number_Of_Players = 1;
}
if (sym == SDLK_2) {
Number_Of_Players = 2;
}
if (sym == SDLK_ESCAPE) {
OnExit();}
else;
}
And Move looks like this:
void CApp::Move(int Where) {
if (CurrentPlayer == 1)
{ SetCell(Where, GRID_TYPE_O);
CurrentPlayer = 0;
}
else if (CurrentPlayer == 0)
{ SetCell(Where, GRID_TYPE_X);
CurrentPlayer = 1;
}
++Grid_count;
}
I added these to the end of CApp::Reset()
EndGame = false;
Grid_count = 0;
Winner = 0;
Where EndGame is used to check several things, Grid_count is the number of pieces in the grid, and Winner is TIE, PLAYER_ONE, or PLAYER_TWO (using an enum). I probably added a bit more than this, so sorry if anything turned out unclear. It might be a little sloppy in places, but it works! You can try it from here: http://superamateur.web44.net/Tic%20Tac%20Toe%20Tut.zip
December 15th, 2008 at 10:56 am
Marty,
Thanks for the contribution!
December 16th, 2008 at 10:53 pm
Hey Tim
Quick question, when working with png files, do you need to specify the transparent color at all if you save that information in the png itself? In your later tutorials when you make the switch to SDL_image, it seems that SDL_SetColorKey makes no difference if it is called or not. Can you elaborate?
December 17th, 2008 at 10:31 am
XP,
No, you do not have to specify anything within SDL for PNG transparency, it’s automatically picked up. However, when you load them, be sure to use SDL_DisplayFormatAlpha instead of SDL_DisplayFormat.
January 29th, 2009 at 4:11 pm
Hi guys!
First of all: GREAT tutorials, REALLY helpful. However I have a problem, I guess it’s more with C++ than SDL but hopefully you will find a solution.
Here’s some code:
CApp.h:
enum GameStatus {
playing = 0,
won1,
won2,
draw
};
GameStatus status;
///////////E O F ///////////////
CApp.cpp:
#include “CApp.h”
///[...]
CApp::CApp() {
//[..]
status = playing;
///[...]
}
////////////E O F/////////////
CApp_OnRender.cpp:
#include “CApp.h”
///[...]
if(status == playing){///instructions }
//////////E O F//////////////////
Problem is, even though I set up my status to playing in the constructor of the CApp, in the onRender functions – it’s not on playing. Why? I can put even in OnExecute status = playing; but still – not helping. If I put status = playing; in the onRender.cpp IT WORKS, but it’s not what I want to do.
Any suggestions – anyone? ;(
January 30th, 2009 at 9:27 am
Max,
Glad you like the tutorials! From what you have shown me, I don’t see anything suspicious. Can you email me all of your code so that I can look over it (my gmail)?
January 30th, 2009 at 10:56 am
Oh, sorry Tim! I did not notice, that in function checking game status I did not return playing as default. That is why the status variable was getting at NULL at every loop. Hard to find such bugs ;J
However, now I can move onto next tutorials and I am sure they are going to be excellent as these
Greetings from Poland!
February 21st, 2009 at 8:36 pm
Nice tut
April 29th, 2009 at 12:21 pm
What is the CAPP thing
April 29th, 2009 at 12:28 pm
Canadian Associate For Petroleum Producers
April 29th, 2009 at 1:02 pm
o well im not canadian so i dont have that =] what do i have to do just copy it onto a action thing
April 29th, 2009 at 1:03 pm
CApp is the name of the class, meaning, it’s the main core application class. C to denote class, and App to say it’s the core of the program. What are you having trouble with exactly?
April 29th, 2009 at 1:05 pm
I guess I dont know what I’m doing. Im a little confused its for a class project where i have to make a game and im confused but thank you for trying to help me
April 29th, 2009 at 1:07 pm
Exactly how much programming experience do you have, and what class are you taking?
April 29th, 2009 at 1:08 pm
Mx studio its my first year doing this.
April 29th, 2009 at 1:12 pm
By Mx Studio, do you mean Action Script? And if so, what version? Do you have any C/C++ programming experience? Also, do you mean Mx studio is the class you are taking, that’s what you have experience in, or both?