• Advertisement
Sign in to follow this  

Checking for a Winner - Tic Tac Toe [SOLVED]

This topic is 4616 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

Hello, I'm fairly new to C++ programming, but know a bit, so I took the advice of the many experienced programs I saw and decided to make a simple game - a console-based Tic Tac Toe in C++. Everything works properly except for detecting whether or not there is a winner, and if there is a winner, who the winner is (X or O). Right now the game is simply 2 player - there is no option to play the computer or AI. Here is the code that I am currently using (unsuccessfully):
bool GameClass::IsWin()
{

if ((board[1][1] == board[1][2] && board[1][2] == board[1][3] && board[1][1]!=' ')||
(board[1][1] == board[2][1] && board[2][1] == board[3][1] && board[1][1]!=' ')||
(board[1][3] == board[2][3] && board[2][3] == board[3][3] && board[1][3]!=' ')||
(board[3][1] == board[3][2] && board[3][2] == board[3][3] && board[3][1]!=' ')||
(board[1][3] == board[2][2] && board[2][2] == board[3][1] && board[1][3]!=' ')||
(board[1][1] == board[2][2] && board[2][2] == board[3][3] && board[1][1]!=' '))
   { 
      GameClass::Winner(); return true;
   }
   else return false;
}


GameClass::Winner() knows which player has just placed a mark on the board, so it can then return a "player ID" (a 1 or 2) as well as setting the boolean value "winner" to "true" which triggers my game loop to break. This is seen here:
void GameLoop(void)
{
     while (!game.IsWin() && game.turns < 10)
     {
           game.PrintBoard();
           game.GetInput();
           if(game.IsSpotTaken(game.row, game.col))
           { game.AlreadyTakenErr(); game.GetInput(); }
           game.PlaceMark(game.row, game.col, game.id);
           game.IsWin();           
     }  
     
     if (game.turns < 10)
     {
        std::cout << "Congratulations to Player " << game.wid << "! You are the winner!\n";
     }
     
     else
     {
         std::cout << std::endl;
         std::cout << "Wow, you both played well. It ended up being a tie!\n";
     }
}


My "game board" is a two-dimensional 3x3 char array (board[3][3];) in which I am only using indices 1, 2, and 3 so that they more accurately correspond to a real-world tic-tac-toe board. I've Googled for various implimentations of an algorithm to check to see if there is a winner and tried various ones, but I have not yet gotten anything to work. Sorry that I am so new at this, but I've worked on it for a couple hours, and I'm pretty sure that I'm stuck. Anyone have some tips or help? Thank you very much. [Edited by - villiageidiot on June 2, 2005 7:27:14 AM]

Share this post


Link to post
Share on other sites
Advertisement
if you have something like this:

a b c
. _________
1 |__|__|__|
2 |__|__|__|
3 |__|__|__|

you can check the board positions
for ex: if 1a and 2b and 3c are X (for the 1,2,3,a,b,c you can use arrays) you can check the arrays for the winner.

Share this post


Link to post
Share on other sites
Use a for loop to check for winner, it looks a lot nicer.

If you want to see how its done just dl it Here

Share this post


Link to post
Share on other sites
Your approach should work, but there's a couple of flaws with it that I can see.

1. C++ Arrays are 0 based, not 1 based, i.e the first element of your board is board[0][0] not board[1][1]

2. There are 8 possible winning configurations in Tic Tac Toe. You're only checking 6 of them.

It might be worth breaking up that horrible if statement into a set of functions, e.g one to check for a vertical line, one to check for a horizontal line, and one to check for a diagonal line. These functions could also return a value representing the player that won.

Share this post


Link to post
Share on other sites
I tried that actually...what I had was this:


bool GameClass::IsWin()
{
if ((board[1][1] == board[1][2] && board[1][2] == board[1][3] && board[1][1]=='X' || 'O')||
(board[1][1] == board[2][1] && board[2][1] == board[3][1] && board[1][1]=='X' || 'O')||
(board[1][3] == board[2][3] && board[2][3] == board[3][3] && board[1][3]=='X' || 'O')||
(board[3][1] == board[3][2] && board[3][2] == board[3][3] && board[3][1]=='X' || 'O')||
(board[1][3] == board[2][2] && board[2][2] == board[3][1] && board[1][3]=='X' || 'O')||
(board[1][1] == board[2][2] && board[2][2] == board[3][3] && board[1][1]=='X' || 'O'))
{
{
GameClass::Winner(); return true;
}
else return false;
}



This works - i.e. the program runs, but it does not detect a winner. After every "X" or "O" is placed, I call GameClass::IsWin() in my gameloop to see if there is a winner. I'm honestly lost as to what the problem is...everything seems like I should be working properly yet it's not.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sandman
Your approach should work, but there's a couple of flaws with it that I can see.

1. C++ Arrays are 0 based, not 1 based, i.e the first element of your board is board[0][0] not board[1][1]

2. There are 8 possible winning configurations in Tic Tac Toe. You're only checking 6 of them.

It might be worth breaking up that horrible if statement into a set of functions, e.g one to check for a vertical line, one to check for a horizontal line, and one to check for a diagonal line. These functions could also return a value representing the player that won.


Thats exactly how i did it
CheckHorizontal, CheckVertical, and CheckCross all return true if there is a row.

Modular Coding is your friend :

Share this post


Link to post
Share on other sites

bool CheckVertical(char board[3][3], char letter)
{
for(int it = 0; it < 3; ++it)
{
if((board[it][0] == letter) && (board[it][1] == letter) && (board[it][2] == letter))
return true;
}
return false;
}

bool CheckHorizontal(char board[3][3], char letter)
{
for(int it = 0; it < 3; ++it)
{
if((board[0][it] == letter) && (board[1][it] == letter) && (board[2][it] == letter))
return true;
}
return false;
}

bool CheckCross(char board[3][3], char letter)
{
if(((board[0][0] == letter) && (board[1][1] == letter) && (board[2][2] == letter)) || ((board[2][0] == letter) && (board[1][1] == letter) && (board[0][2] == letter)))
return true;
return false;
}



Share this post


Link to post
Share on other sites
Quote:
Original post by SumDude
*** Source Snippet Removed ***


If you want to know which player won, you could modify these to return a char value 'O', 'X' or 0 instead of true/false, and have them check for any winner rather than call them once for each player.

Share this post


Link to post
Share on other sites
Success!

Thanks for the help you all...I cleaned up the code a little bit using for loops to check wins across the board and vertically, and two if statements to check the diagonals and ... it works!! I'm so excited...the hours finally paid off. :D

The fixed code:


int GameClass::IsWin()
{
// check wins across
for (int i_row = 1; i_row<4; i_row++)
{
if(board[i_row][1] == 'X' && board[i_row][2] == 'X' && board[i_row][3] == 'X')
{
winner = true;
return 1;
}
else if(board[i_row][1] == 'O' && board[i_row][2] == 'O' && board[i_row][3] == 'O')
{
winner = true;
return 2;
}

}
// check wins vertically
for (int j_col = 1; j_col<4; j_col++)
{
if(board[1][j_col] == 'X' && board[2][j_col] == 'X' && board[3][j_col] == 'X')
{
winner = true;
return 1;
}
else if(board[1][j_col] == 'O' && board[2][j_col] == 'O' && board[3][j_col] == 'O')
{
winner = true;
return 2;
}
}

// check diagonals
if(board[1][1] == 'X' && board[2][2] == 'X' && board[3][3] == 'X')
{
winner = true;
return 1;
}
else if(board[1][1] == 'O' && board[2][2] == 'O' && board[3][3] == 'O')
{
winner = true;
return 2;
}

if(board[1][3] == 'X' && board[2][2] == 'X' && board[3][1] == 'X')
{
winner = true;
return 1;
}
else if(board[1][3] == 'O' && board[2][2] == 'O' && board[3][1] == 'O')
{
winner = true;
return 2;
}

return 0;
}




Main game loop:

void GameLoop(void)
{
while (!game.IsWin() && game.turns < 10)
{
game.PrintBoard();
game.GetInput();
if(game.IsSpotTaken(game.row, game.col))
{ game.AlreadyTakenErr(); game.GetInput(); }
game.PlaceMark(game.row, game.col, game.id);
game.IsWin(); // returns 0 unless someone has won - in which case
} // returns 1 if player 1 (X) wins, 2 if player 2 (O) wins

if (game.turns < 10)
{
std::cout << "Congratulations to Player " << game.IsWin() << "! You are the winner!\n"; // prints value returned from GameClass::IsWin() - or the
} // player number

else
{
std::cout << std::endl;
std::cout << "Wow, you both played well. It ended up being a tie!\n";
}
}



Again, thanks a lot. You guys rock!

Share this post


Link to post
Share on other sites
Quote:
Original post by villiageidiot
Success!

Thanks for the help you all...I cleaned up the code a little bit using for loops to check wins across the board and vertically, and two if statements to check the diagonals and ... it works!! I'm so excited...the hours finally paid off.


Have you tested a release build yet? I have a suspicion it may do strange things.

You're still using arrays as though they are 1 based, and while you may be able to get away with it enough for it to appear to work (since you're doing so consistently) you are in fact writing to areas of memory which don't strictly belong to you, which can cause weird bugs and crashes.

Debug builds have buffers at the end of arrays to catch overwriting of this nature - run it in a debugger and you'll probably get all sorts of runtime errors cropping up talking about damage after memory blocks. Release builds aren't so forgiving though, and you can get some really strange bugs with this sort of error.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sandman
Have you tested a release build yet? I have a suspicion it may do strange things.

You're still using arrays as though they are 1 based, and while you may be able to get away with it enough for it to appear to work (since you're doing so consistently) you are in fact writing to areas of memory which don't strictly belong to you, which can cause weird bugs and crashes.

Debug builds have buffers at the end of arrays to catch overwriting of this nature - run it in a debugger and you'll probably get all sorts of runtime errors cropping up talking about damage after memory blocks. Release builds aren't so forgiving though, and you can get some really strange bugs with this sort of error.


Everything seems to work fine, but no I haven't tested a release build yet. I see what you are saying though...so in other words if I am going to continue using arrays as 1 based, change my array to char board[4][4]? Or use the arrays as they are originally intended with a base of 0 and leave the board as char board[3][3]. Would this fix the problem?

Share this post


Link to post
Share on other sites
Quote:
Original post by villiageidiot
so in other words if I am going to continue using arrays as 1 based, change my array to char board[4][4]? Or use the arrays as they are originally intended with a base of 0 and leave the board as char board[3][3]. Would this fix the problem?


Either will work, although the latter solution is definitely preferred. Better to get into the habit of addressing the first element properly than to cheat by allocating a bigger array than you need.

Share this post


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

  • Advertisement