Jump to content
  • Advertisement
Sign in to follow this  
phil67rpg

tictactoe

This topic is 2204 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am working on a tic tac toe game using c++.My question is how do I get the O's to not overwrite the X's on the board.I am using a boolean array to do a simple comparison using two boolean arrays.I just need a little hint.Here is the code I am working on.

void Computer::move_player_O()
{
srand(time(NULL));
player_O = rand()%9+1;

if(array_X[player_O]==false)
{
board[player_O]='O';
}

array_O[player_O]=true;
}

Share this post


Link to post
Share on other sites
Advertisement
Why not just use an array of chars, and have the characters set to X or O whenever you or the computer trys to put an X or an O on them, but before actually setting the character to X or O see if it's already covered by an X or an O. Also, In every game loop, check for win or lose conditions.
CODE:

char Board[9];
//ALL PIECES START AT ONE
for (index = -1; index < 8; ++index)
{
Board[Index] = '1'
}
//WHENEVER SOMEONE TRYS TO MAKE A MOVE
if (Board[WhateverSquareTheyreTryingToUse == 1])
{
//Set The Square on the Board to either X or 0
}
//Check for end of game conditions

Sorry I couldn't flesh it out more, but thats essentially how I programmed tic tac toe.

Share this post


Link to post
Share on other sites

I am working on a tic tac toe game using c++.My question is how do I get the O's to not overwrite the X's on the board.I am using a boolean array to do a simple comparison using two boolean arrays.I just need a little hint.Here is the code I am working on.

void Computer::move_player_O()
{
srand(time(NULL));
player_O = rand()%9+1;

if(array_X[player_O]==false)
{
board[player_O]='O';
}

array_O[player_O]=true;
}



my guess would be to only allow the computer to fill in a blank spot on your tic tac toe board by checking if the spot already contains an 0

Share this post


Link to post
Share on other sites
This piece of code: "rand() % 9 + 1"generates a number between 1 and 10. In C++, arrays begin at 0, so you may not want the +1 there.
(An array with 10 elements, goes from 0 to 9)

srand(time(NULL)); is only supposed to be called once at the beginning of your program - not every function call, or the results won't be properly random. Call srand() once, and rand() as many times as you like.

If 'board[]' is a boolean array, you can't assign it a 'O'. If it's a char array, then it's okay.

If this statement: "if(array_X[player_O]==false)" is valid, then you probably want "array_O[player_O]=true;" within the if() statement also.
if(array_X[player_O]==false)
{
board[player_O]='O';
array_O[player_O]=true;
}


But let's seperate out the two different things you are doing.
A) You are having a board be clicked, and checking if the place is valid, and if valid, marking the board at that spot.
B) You are also doing AI, by making it be the computer doing the move.

The two should be seperated into different functions.
void Computer::do_turn()
{
//The computer's logic goes here.

//Then you place the marker.
place_marker(spot);
}

void Computer::place_marker(int spot)
{
board[spot]='O';
array_O[spot]=true;
}


The question is, what do you do for the computer's logic?
There are two options that come to my mind:
1) Randomly choose a location, and if it's taken, randomly choose a new location, and if that's taken, randomly choose a new location, and so on.
The problem: You can (possibly) have very bad random numbers and (potentially) loop forever accidentally choosing the same bad spots over and over.

In code, this would look like this:
void Computer::do_turn()
{
int spot = 0;
bool good = false;
do
{
spot = rand() % SIZE_OF_BOARD;

//Check that we didn't previously go here.
if(array_O[spot]==false)
{
//Check that the other player didn't previously go here.
if(array_X[spot]==false)
{
//If the place is actually empty, then this spot is good.
good = true;
}
}
}
while(!good)


//Place the marker where the computer decided.
place_marker(spot);
}


But, as mentioned, this is not good, because we could accidentally randomly loop forever if we keep on picking spots that aren't good.

2) The second option is to make sure that the only places that are available to randomly pick, are ones that we know are already empty.
Now there are two ways to do that. One is to keep track of what places haven't been used by either player. Another is to, each time the computer is ready to pick, check which places are available. We're going to try the second option.

void Computer::do_turn()
{
//Count how many empty spaces are left.
int numberOfEmptySpaces = 0;
for(int i = 0; i < SIZE_OF_BOARD; i++)
{
if(board != 'O' && board != 'X')
{
numberOfEmptySpaces++;
}
}

//Randomly generate a random number within the number of empty spaces.
int spot = rand() % numberOfEmptySpaces;

//Keep looping until we find a spot that isn't filled.
while(board[spot] == 'O' || board[spot] == 'X')
{
//Each time we find our spot taken, we move to the next spot.
spot++;

//Unless we reach the end of our array, in which case we loop back around to zero.
if(spot >= SIZE_OF_BOARD)
{
spot = 0;
}
}

//Place the marker where the computer decided.
place_marker(spot);
}

Share this post


Link to post
Share on other sites

This piece of code: "rand() % 9 + 1"generates a number between 1 and 10. In C++, arrays begin at 0, so you may not want the +1 there.
(An array with 10 elements, goes from 0 to 9)

srand(time(NULL)); is only supposed to be called once at the beginning of your program - not every function call, or the results won't be properly random. Call srand() once, and rand() as many times as you like.

If 'board[]' is a boolean array, you can't assign it a 'O'. If it's a char array, then it's okay.

If this statement: "if(array_X[player_O]==false)" is valid, then you probably want "array_O[player_O]=true;" within the if() statement also.
if(array_X[player_O]==false)
{
board[player_O]='O';
array_O[player_O]=true;
}


But let's seperate out the two different things you are doing.
A) You are having a board be clicked, and checking if the place is valid, and if valid, marking the board at that spot.
B) You are also doing AI, by making it be the computer doing the move.

The two should be seperated into different functions.
void Computer::do_turn()
{
//The computer's logic goes here.

//Then you place the marker.
place_marker(spot);
}

void Computer::place_marker(int spot)
{
board[spot]='O';
array_O[spot]=true;
}


The question is, what do you do for the computer's logic?
There are two options that come to my mind:
1) Randomly choose a location, and if it's taken, randomly choose a new location, and if that's taken, randomly choose a new location, and so on.
The problem: You can (possibly) have very bad random numbers and (potentially) loop forever accidentally choosing the same bad spots over and over.

In code, this would look like this:
void Computer::do_turn()
{
int spot = 0;
bool good = false;
do
{
spot = rand() % SIZE_OF_BOARD;

//Check that we didn't previously go here.
if(array_O[spot]==false)
{
//Check that the other player didn't previously go here.
if(array_X[spot]==false)
{
//If the place is actually empty, then this spot is good.
good = true;
}
}
}
while(!good)


//Place the marker where the computer decided.
place_marker(spot);
}


But, as mentioned, this is not good, because we could accidentally randomly loop forever if we keep on picking spots that aren't good.

2) The second option is to make sure that the only places that are available to randomly pick, are ones that we know are already empty.
Now there are two ways to do that. One is to keep track of what places haven't been used by either player. Another is to, each time the computer is ready to pick, check which places are available. We're going to try the second option.

void Computer::do_turn()
{
//Count how many empty spaces are left.
int numberOfEmptySpaces = 0;
for(int i = 0; i < SIZE_OF_BOARD; i++)
{
if(board != 'O' && board != 'X')
{
numberOfEmptySpaces++;
}
}

//Randomly generate a random number within the number of empty spaces.
int spot = rand() % numberOfEmptySpaces;

//Keep looping until we find a spot that isn't filled.
while(board[spot] == 'O' || board[spot] == 'X')
{
//Each time we find our spot taken, we move to the next spot.
spot++;

//Unless we reach the end of our array, in which case we loop back around to zero.
if(spot >= SIZE_OF_BOARD)
{
spot = 0;
}
}

//Place the marker where the computer decided.
place_marker(spot);
}



I think he may be mixed up thinking about a random number generator where u may not want the number to start with 0 and may not exactly have the workings of an array in mind

Share this post


Link to post
Share on other sites

This piece of code: "rand() % 9 + 1"generates a number between 1 and 10. In C++, arrays begin at 0, so you may not want the +1 there.
(An array with 10 elements, goes from 0 to 9)


Nitpick: the number will be between 1 and 9.

About the implementation of the second option. You actually fooled me, as my first impression was you determine a number between 0 and emptySpaces, then make as many steps from the start (skipping filled spots). In that case you should never have to wrap around. But with your approach of starting at the selected spot and then skipping ahead until the first free cell, is the first part even necessary (as in: does it produce better randomized results)?

My impression would be that it might make it less random. If the first 4 spots are filled, you would ways end up with the 5th spot: 5 spots are empty, so spot = rand() % 5 = 0-4. In each case you get spot = 4.

So I think you should either always create numbers 0-8 and skip empty spots as above, or if you do create a number using only the free spots, it would probably be something like this:


int steps = rand() % numberOfEmptySpaces;
int spot = -1;

while (steps >= 0)
{
//If free, decrease step count
if (board[spot] != 'O' && board[spot] != 'X')
{
--steps;
}
++spot;
}

place_marker(spot);


I believe that should create an equal probability for each spot.

Alternatively (and somewhat more cumbersome) you could add an extra layer of indirection by keeping a list of empty spots and randomly picking an index from that list.


//Declared somewhere and initialized with the board
vector<int> freeSpots; //Fill with 0 - sizeOfBoard


const int idx = rand() % freeSpots.size();
place_marker(freeSpots[idx]);

freeSpots.erase(freeSpots.begin() + idx);


Since erasing from anywhere but the end of a vector requires moving stuff in memory, a common method is to not remove a value, but swap it with the end and reducing the size of the vector.

swap(freeSpots[idx], freeSpots[freeSpots.size()-1]);
freeSpots.pop_back();

By not removing the last element and instead keeping track of the size yourself, you can keep reusing the same vector and just need to reset the size


vector<int> freeSpots(SIZE_OF_BOARD); //Initialize once when starting up


//When placing a marker
swap(freeSpots[idx], freeSpots[--numberOfFreeSpots]);

//When resetting the game
numberOfFreeSpots = SIZE_OF_BOARD;


Since all random numbers should be equally likely, it doesn't matter in which order the numbers are stored in freeSpots.

Personally I like this approach for simulating a deck of cards, as you don't need to do any initial "shuffling". Edited by Trienco

Share this post


Link to post
Share on other sites

I think he may be mixed up thinking about a random number generator where u may not want the number to start with 0 and may not exactly have the workings of an array in mind

He's using C++. He's a beginner. He's using the generated value to index into two seperate arrays. Thus, it should start at 0, unless he's intentionally using position 0 for some other purpose.

Chances are, he copied the srand(time(null); rand() % range + 1; from someplace online (which is fine) and didn't full understand it (which is fine). The highly likely chance that it was mistaken is why I commented on it; if it was a mistake, now he knows why. If it wasn't a mistaken, then my comment won't cause him any inconvience.


[quote name='Servant of the Lord' timestamp='1348978138' post='4985255']
This piece of code: "rand() % 9 + 1"generates a number between 1 and 10.

Nitpick: the number will be between 1 and 9.[/quote]
/facepalm
You are absolutely correct. laugh.png

About the implementation of the second option. You actually fooled me, as my first impression was you determine a number between 0 and emptySpaces, then make as many steps from the start (skipping filled spots). In that case you should never have to wrap around.[/quote]
That would definitely be a better idea!

But with your approach of starting at the selected spot and then skipping ahead until the first free cell, is the first part even necessary (as in: does it produce better randomized results)?[/quote]

No, I switched what I was originally going to do halfway through, because it'd require the OP learning too much too fast.
I was originally going to create an array of indices that represent empty spots (like you also suggest), and then just do the random indicing into that:

std::vector<int> validIndices;
for(int i = 0; i < SIZE_OF_BOARD; i++)
{
if(board != 'O' && board != 'X')
{
validIndices.push_back(i);
}
}

int choice = rand() % validIndices.size();

this->place_marker(validIndices[choice]);


But that would require std::vector or other confusions.
I was trying to avoid excessively teaching new things to solve the logic puzzle within the knowledge the OP already had - but I blew it! tongue.png
I avoided using std::count() in my previous solution (counting with a for() loop instead) for the same reason.

Your non-vector no-loop suggestion is much better than my non-vector solution (which as you point out, apparenly has problems with accurate probabillity). Though I think either of us would actually change things at a higher level than just that one function, but that wouldn't help the OP much. Edited by Servant of the Lord

Share this post


Link to post
Share on other sites
thanks servant for all the help,any other help?here is the code I am working on.


void Computer::move_player_O()
{
player_O = rand()%8+1;
board[player_O]='O';
}
void Computer::check_player_O()
{
if(array_O[player_O]==false)
{
board[player_O]='O';
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!