• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Absolut88

Need some help on Tic-Tac-Toe player turn

9 posts in this topic

So I am finally transitioning from console based programming into event-driven / 2d gfx programming. I have been looking through lazyfoos tutorials and they have helped a ton. I started to make a Tic-Tac-Toe game yesterday and it started off pretty good, I think. The problem I am running into right now is how to define if it is playerOne's turn or not.

I am using a bool for playerOne, true means it is playerOnes turn, false means it isn't. For now there are not actually 2 players, but I am trying to make it so when it playerOne == true, the box turns blue when clicked, and when playerOne == false, turn red. Later on I will switch it to images, but I want the logic down.

I suppose it will be easier if I post the code so here it is. Any help will be greatly appreciated:
[source lang="cpp"]#include "SDL.h"
#include "SDL_opengl.h"
#include "SDL_image.h"
#include <iostream>
#include <string>

const int HOLDERS = 9; // Number of holders for images

void setupWindow()
{
// Initialize SDL
SDL_Init(SDL_INIT_EVERYTHING);

// Allocate memory for GL
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

// Caption on the window pane
SDL_WM_SetCaption ("Tic-Tac-Toe", NULL);

// Setup the window
SDL_SetVideoMode(600, 600, 32, SDL_OPENGL);

// Clears the screen to this color
glClearColor(1,1,1,1);

// What is displayed of the screen
glViewport(0,0,600,600);

// Smooth color transition
glShadeModel(GL_SMOOTH);

// Sets to 2D
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

// Disable depth test since not 3D
glDisable(GL_DEPTH_TEST);
}

struct Holder
{
float xPos;
float yPos;
float width;
float height;
bool isVacant;
};


int main (int argc, char* args[])
{
// Function for window setup
setupWindow();

/*---------------------------------------------------------------------------------------------------*/
/*--------------------------- VARIABLE SETUP -----------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
// Handles game loop
bool isRunning = true;
bool playerOne = true;

// Handles SDL events
SDL_Event event;

// Array of Holders type
Holder holders[HOLDERS];

// Setup the holders
for (int a = 0, x = 20, y = 20; a < HOLDERS; a++, x+= 190)
{
if ( x > 400)
{
x = 20;
y += 190;
}
holders[a].xPos = x;
holders[a].yPos = y;
holders[a].width = 180;
holders[a].height = 180;
holders[a].isVacant = true;
}

/*---------------------------------------------------------------------------------------------------*/
/*--------------------------- MAIN LOOP -----------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/

// Game loop
while (isRunning)
{
/*---------------------------------------------------------------------------------------------------*/
/*--------------------------- 1. EVENTS --------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
while (SDL_PollEvent(&event))
{
// If window is closed
if (event.type == SDL_QUIT)
{
isRunning = false;
}
// If escape key is pressed
if (event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE)
{
isRunning = false;
}
// Mouse click
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
// Mouse offsets
int x = 0, y = 0;

x = event.button.x;
y = event.button.y;

for (int a = 0; a < HOLDERS; a++)
{
if ((x > holders[a].xPos) && (x < holders[a].xPos + holders[a].width) &&
(y > holders[a].yPos) && (y < holders[a].yPos + holders[a].height) && holders[a].isVacant == true)
{
OutputDebugString("Spot no longer vacant\n");
holders[a].isVacant = false;
}
}
}
}
}

/*---------------------------------------------------------------------------------------------------*/
/*--------------------------- 2. LOGIC -----------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/



/*---------------------------------------------------------------------------------------------------*/
/*--------------------------- 3. RENDER -----------------------------------------------------*/
/*---------------------------------------------------------------------------------------------------*/
// Clears screen before rendering
glClear(GL_COLOR_BUFFER_BIT);

glPushMatrix(); // Start phase

glOrtho(0,600,600,0,-1,1); // Set the matrix

// THE HOLDERS
glBegin(GL_QUADS);

// Used to display vacant spaces black
for (int n = 0; n < HOLDERS; n++)
{
if (holders[n].isVacant == true)
{
glColor4ub(0,0,0,255);

glVertex2f(holders[n].xPos, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos + holders[n].height);
glVertex2f(holders[n].xPos, holders[n].yPos + holders[n].height);
}

else if (holders[n].isVacant == false && playerOne == true)
{
glColor4ub(0,0,255,255);

glVertex2f(holders[n].xPos, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos + holders[n].height);
glVertex2f(holders[n].xPos, holders[n].yPos + holders[n].height);

}

else if (holders[n].isVacant == false && playerOne == false)
{
glColor4ub(255,0,0,255);

glVertex2f(holders[n].xPos, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos);
glVertex2f(holders[n].xPos + holders[n].width, holders[n].yPos + holders[n].height);
glVertex2f(holders[n].xPos, holders[n].yPos + holders[n].height);
}
}

glEnd();

glPopMatrix(); // End phase

// Draws everything above it to the screen
SDL_GL_SwapBuffers();
}

SDL_Quit;

return 0;
}[/source]
0

Share this post


Link to post
Share on other sites
I also tried to do the 'turns' based on a counter and % 0 == this color, else another color. Couldn't figure that out either.
0

Share this post


Link to post
Share on other sites
Have an enum for the player. Have a variable 'currentPlayer' (of that enum type) start off set to PlayerOne.
[CODE]enum Player {PlayerOne, PlayerTwo};

Player currentPlayer = PlayerOne;[/CODE]

Each time the player clicks, check it like this:
- If that is valid location to click...
- ...then make sure the location has not already been taken...
- ...then set the location to this player's mark...
- ...then change the 'currentPlayer' to the next player's turn.

Also, you'll need another enum. In your struct 'Holder' you have a bool 'isVacant'. It actually needs three different settings (Empty, Player one has marked it, Player two has marked it)
[code]enum Marker {EmptyMarker, PlayerOneMarker, PlayerTwoMarker};

struct Holder
{
float xPos;
float yPos;
float width;
float height;
Marker marker;
};[/code]

When you get a mouse click, you can do something similar to this:
[CODE]
for (int a = 0; a < HOLDERS; a++)
{
//Check if we are within a marker location.
if ((x > holders[a].xPos) && (x < holders[a].xPos + holders[a].width) &&
(y > holders[a].yPos) && (y < holders[a].yPos + holders[a].height))
{
//Check if the location is empty.
if(holder[a].marker == EmptyMarker)
{
//Change the marker, depending on what player's turn it is.
if(currentPlayer == PlayerOne)
{
holders[a].marker == PlayerOneMarker;

//Change who's turn it is.
currentPlayer = PlayerTwo;
}
else if(currentPlayer == PlayerTwo)
{
holders[a].marker == PlayerTwoMarker;

//Change who's turn it is.
currentPlayer = PlayerOne;
}

}
}
}
[/CODE]

Once you get it working, here's something fun to try: Add a third player, and make the grid larger.
1

Share this post


Link to post
Share on other sites
From what I can see it looks like your switching the colors based on the current player and not based on the player that did the selection.
This means that when it's player 1's turn everything (X's and O's) will draw blue and when it's player 2's turn everything will draw red.

You could try something like this:

[source]
struct Holder
{
float xPos;
float yPos;
float width;
float height;
bool isVacant;
bool isX;
};

// set color to draw
if (holders[n].isVacant == true) {
glColor4ub(0,0,0,255); // black
}
else if (holders[n].isX) {
glColor4ub(0,0,255,255); // blue
}
else {
glColor4ub(255,0,0,255); // red
}

// draw rectangle

[/source]
1

Share this post


Link to post
Share on other sites
[quote name='Servant of the Lord' timestamp='1343672413' post='4964569']
Have an enum for the player. Have a variable 'currentPlayer' (of that enum type) start off set to PlayerOne.
.......
[/quote]

Thanks a ton man, that worked perfectly. I forgot all about enums and how well they work. I have almost everything working now including the checking for winner logic.

I am having one problem though:

After a person has been won (3 in a row, duh), i have a bool set up named isGameover that is then set to True. Now after that happens I wanted to load a texture that says "Player One/Two Wins" have a 2000ms Delay, and then exit. However, I am not even able to setup the textures like I did the other ones. I am not sure if there is a certain max size you can have for loading textures onto an unsigned int or not.

xTexture, oTexture, and hTexture all load and work fine. They are 180x180, .png
playerOneTexture and playerTwoTexture are 600x600, .png, and saved the exact same way as the other three.
I am guessing that there is a max image size that can be loaded this way.
[source lang="cpp"] // TEXTURES
// X Texture
unsigned int xTexture = 0;
xTexture = loadTexture("X.png");
// O Texture
unsigned int oTexture = 0;
oTexture = loadTexture("O.png");
// Holder Texture
unsigned int hTexture = 0;
hTexture = loadTexture ("Holder.png");
// PlayerOne texture
unsigned int playerOneTexture = 0;
playerOneTexture = loadTexture ("playerOneWins.png");
// PlayerTwo texture
unsigned int playerTwoTexture = 0;
playerTwoTexture = loadTexture ("playerTwoWins.png");[/source]
0

Share this post


Link to post
Share on other sites
Couple tips:
A) You can just use 'unsigned' instead of 'unsigned int', it's a valid shorthand.
B) You can declare and initialize the variables in the same line.
[code]unsigned hTexture = loadTexture ("Holder.png");[/code]

Also, you aren't loading a texture into an int. An int is only 4 bytes (or more or less, depending on your hardware and OS and stuff), a single pixel is usually about 4 bytes, let alone an entire image. With OpenGL you are loading an image into your videocard's memory, and the function is returning a ID number. That ID number (oftentimes called a 'handle') is being stored in the integer, not the image itself.

Yes, there are maximum sizes for textures, and they depend on what videocard you have... but any videocard made in the past 8 years would be able to hold over 600x600 easily.

My guess as to the problem: The function can't find the images, because playerOneWins.png and playerTwoWins.png are probably not named the exact same way in the folder. Maybe the actual images are named playerOneWin.png without the 's' after 'win'? Or maybe they are .jpegs not .pngs? Or something similar.

To find out what's going on, the function loadTexture() probably does something or another to report an error if something messes up. What does the documentation say for loadTexture()? That's not a SDL or OpenGL function, as far as I know. Did you write it itself or find it online? If so, post the code, please!
If it's part of a library, what does that library's documentation say it does when it fails? Does it return 0 instead of the correct ID? Does it throw an exception? Does it set some error flag somewhere? It definitely does something when it fails, to let you know why it failed.
1

Share this post


Link to post
Share on other sites
the loadTexture() was from a youtube tutorial, http://www.youtube.com/watch?v=UBxxVFjYN3c&feature=plcp.

it is a function at the top of my program:

[source lang="cpp"]GLuint loadTexture(const std::string &fileName)
{
SDL_Surface *image = IMG_Load(fileName.c_str());

SDL_DisplayFormatAlpha(image);

unsigned object(0);

glGenTextures(1, &object);

glBindTexture(GL_TEXTURE_2D, object);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);

//Free surface
SDL_FreeSurface(image);

return object;
}[/source]

The image must have been to big, I just resized it to 180x180 and it worked.
0

Share this post


Link to post
Share on other sites
Don't assume that because it wasn't working and now it is, that the problem is solved or that you understand what the problem was.
Until you actually track down the problem for sure, and not just guess that [i]something might've somewhere could've[/i] been the source of the issue, then your code has a hidden problem that may pop up later at unexpected times.

I strongly strongly doubt that it was a texture size limitation, but it is possible. However, we can be [i]sure[/i] of it by checking the results of the function.
In the function you posted, the code doesn't do any error checking whatsoever, despite calling a bunch of OpenGL functions. That's not good, it just [i]assumes[/i] that everything worked, and when it doesn't work (like in your situation), it doesn't give you any indication of what the problem was.

[b]Step 1:[/b] Print out the value of 'GL_MAX_TEXTURE_SIZE'. If that size is less than 600x600, then we can be confident that the problem has been resolved. If it's greater than 600x600, then the problem still exists somewhere, and proceed to step two.
[code]std::cout << "The maximum texture size is: " << GL_MAX_TEXTURE_SIZE << "x" << GL_MAX_TEXTURE_SIZE << std::endl;[/code]

[b]Step 2:[/b] Error-proof the SDL half of the function, by making sure IMG_Load and SDL_DisplayFormatAlpha didn't fail.
[code]//Try to load the image.
SDL_Surface *image = IMG_Load(fileName.c_str());

//Make sure nothing went wrong.
if(!image)
{
//If something did go wrong, print the error and exit the function.
std::cout << "IMG_Load() failed: " << IMG_GetError());
return 0;
}

//Format the image.
SDL_Surface *formattedImage = SDL_DisplayFormatAlpha(image);

//Make sure nothing went wrong.
if(!formattedImage)
{
//If something did go wrong, print the error and exit the function.
std::cout << "SDL_DisplayFormatAlpha() failed: " << SDL_GetError());
return 0;
}

//Free the old surface, so we don't leak memory.
SDL_FreeSurface(image);

//...all the OpenGL stuff...

//Free the converted image, now that it has been copied by OpenGL onto the videocard.
SDL_FreeSurface(formattedImage);
[/code]

[b]By the way, we discovered the function is actually already doing something (two things!) wrong, just by adding error checking![/b]
[url="http://sdl.beuc.net/sdl.wiki/SDL_DisplayFormatAlpha"]SDL_DisplayFormatAlpha[/url]() [u][i]copies[/i][/u] the surface passed in, converts it to the proper video format, and [u][i]returns[/i][/u] the new surface.
The loadTexture() function isn't catching the return value! This means two things:
1) It's not having any effect at all, since you aren't use the result.
2) You're leaking memory, since you aren't freeing the result either!

Step 3: Error-proof the OpenGL half of the function, by making sure glTexImage2D() and the others didn't fail.
Out of laziness and unfamiliarity with OpenGL, I'm just going to steal the OpenGL error-checking code [url="http://www.lighthouse3d.com/cg-topics/error-tracking-in-opengl/"]from here[/url], though I'm going to modify it to fit our desires better.
Here's my modified version: (basically the same thing, except using std::cout, and return a bool.
[code]#define GL_ErrorOccured() GL_ErrorOccured_private(__FILE__, __LINE__)
bool GL_ErrorOccured_private(char *file, int line)
{
GLenum errorCode;
errorCode = glGetError();

//If an error didn't occur, just return false.
if(errorCode == GL_NO_ERROR)
return false;

//Print the error.
std::cout << "An OpenGL error occured in the file '" << file << "' on line " << line << "\n"
<< "Error: " << gluErrorString(errorCode) << std::endl;
//Return true - an error occured.
return true;
}[/code]

We need to add 'GL_ErrorOccured()' (from the above link) after each OpenGL function call.

Final result:
[code]GLuint loadTexture(const std::string &fileName)
{
//Try to load the image.
SDL_Surface *image = IMG_Load(fileName.c_str());

//Make sure nothing went wrong.
if(!image)
{
//If something did go wrong, print the error and exit the function.
std::cout << "IMG_Load() failed: " << IMG_GetError());
return 0;
}
//Format the image.
SDL_Surface *formattedImage = SDL_DisplayFormatAlpha(image);
//Make sure nothing went wrong.
if(!formattedImage)
{
//If something did go wrong, print the error and exit the function.
std::cout << "SDL_DisplayFormatAlpha() failed: " << SDL_GetError());
return 0;
}
//Free the old surface, so we don't leak memory.
SDL_FreeSurface(image);

//This variable should have a more descriptive name. 'object'? Really? 'textureID' is much better.
unsigned textureID = 0;

//Generate a texture ID and assign it to our variable.
glGenTextures(1, &textureID);

//Bind our textureID as the texture currently being worked on.
glBindTexture(GL_TEXTURE_2D, textureID);
if(GL_ErrorOccured()) return 0;

//Set up the texture generation parameters for glTexImage2D().
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if(GL_ErrorOccured()) return 0;

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if(GL_ErrorOccured()) return 0;
//Generate the texture, taking that data from our SDL_Surface, and moving it into the Video card's memory in the format we've specified to OpenGL.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, formattedImage->w, formattedImage->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, formattedImage->pixels);
if(GL_ErrorOccured()) return 0;

//Free the converted image, now that it has been copied by OpenGL onto the videocard.
SDL_FreeSurface(formattedImage);

return textureID ;
}[/code]

Three notes:
1) This won't actually solve the error, but it will go a long way closer to identifying the error, since right now we don't have a clue what the error is, aside from guesswork.
2) On failure, the function returns 0 instead of the texture ID. It also leaks 'formattedImage' if it fails. If it was code in my own project, I'd handle things still differently, but I don't want to complete rewrite the function and it's usage into something radically different from what you already have.
3) I haven't actually compiled and tested the code (I don't have SDL installed on this machine anymore), so I may have made a dumb mistake or typos.
2

Share this post


Link to post
Share on other sites
Thank you a ton for taking the time to explain all of that to me. I was at work and couldn't test anything, but now that I am home I am going to see if I can find where the error occurred.

..Neither cout or std::cout prints anything to the console for me?

--Nevermind, I'm an idiot, was using an empty project. Edited by Absolut88
0

Share this post


Link to post
Share on other sites
std::cout sends the text to the standard text output. If using a console, it displays it on the console, if using SDL it displays it to a file called sdlout.txt next to your game's executable. The stream can be rerouted to other locations, so for alot of other APIs it is just ignored unless you manually direct it to where you want. Edited by Servant of the Lord
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0