tic tac toe program
hello, i started programming again after a while and i made this just to pass time/relearn the basics and stuff
there is a weird issue that i can't figure out, when i input row 0 column 2 or row 1 column 2 it will fill the spot for both the inputted move and the row above/below it (if i input row 0 column 2, it will also output an X in row 1 column 0 and if i input row 1 column 2 it will also output an X in row 2 column 0)
another thing i was curious about was just general efficiency/neatness and any criticisms on any of that stuff would be greatly appreciated
thanks
#include <iostream>
using namespace std;
//functions
void PrintTurn();
void PrintBoard();
void GetMoveInput();
void CheckResult(int nRow, int nColumn);
void ClearScreen();
void AnnounceWinner();
void PlayAgain();
//variables
bool CurrentPlayerTurn=true; //true=x, false=o
char PlayerMove[2][2]; //first array: row, second: column
int MoveCount=0;
int main()
{
for(int i=0;i<=2;i++) //clear board
{
for(int j=0;j<=2;j++)
PlayerMove[i][j]='_';
}
ClearScreen();
return 0;
}
void PrintTurn()
{
cout<<"Current Player's Turn: ";
if(CurrentPlayerTurn)
cout<<"X";
else
cout<<"O";
cout<<endl;
}
void PrintBoard()
{
cout<<"RC0 1 2"<<endl;
for(int i=0;i<=2;i++)
{
cout<<i<<" ";
for(int j=0;j<=2;j++)
cout<<PlayerMove[i][j]<<" ";
cout<<endl;
}
}
void GetMoveInput()
{
int row, column;
cout<<"Row: ";
cin>>row;
if(row!=0 && row!=1 && row!=2)
{
ClearScreen();
return;
}
cout<<"Column: ";
cin>>column;
if(column!=0 && column!=1 && column!=2)
{
ClearScreen();
return;
}
CheckResult(row,column);
}
void ClearScreen()
{
system("cls");
PrintTurn();
PrintBoard();
cout<<endl<<endl;
GetMoveInput();
}
void CheckResult(int nRow, int nColumn)
{
if(PlayerMove[nRow][nColumn]!='_')
{
ClearScreen();
return;
}
char PlayerTurn;
MoveCount++; //for tie maybe
if(CurrentPlayerTurn)
{
PlayerMove[nRow][nColumn]='X';
PlayerTurn='X';
}
else {
PlayerMove[nRow][nColumn]='O';
PlayerTurn='O';
}
if(PlayerMove[0][0]==PlayerTurn && PlayerMove[0][1]==PlayerTurn && PlayerMove[0][2]==PlayerTurn) { //xxx row 1
AnnounceWinner();
return; }
else if(PlayerMove[1][0]==PlayerTurn && PlayerMove[1][1]==PlayerTurn && PlayerMove[1][2]==PlayerTurn) { //xxx row 2
AnnounceWinner();
return; }
else if(PlayerMove[2][0]==PlayerTurn && PlayerMove[2][1]==PlayerTurn && PlayerMove[2][2]==PlayerTurn) { //xxx row 3
AnnounceWinner();
return; }
else if(PlayerMove[0][0]==PlayerTurn && PlayerMove[1][0]==PlayerTurn && PlayerMove[2][0]==PlayerTurn) { //xxx column 1
AnnounceWinner();
return; }
else if(PlayerMove[0][1]==PlayerTurn && PlayerMove[1][1]==PlayerTurn && PlayerMove[2][1]==PlayerTurn) { //xxx column 2
AnnounceWinner();
return; }
else if(PlayerMove[0][2]==PlayerTurn && PlayerMove[1][2]==PlayerTurn && PlayerMove[2][2]==PlayerTurn) { //xxx column 3
AnnounceWinner();
return; }
else if(PlayerMove[0][0]==PlayerTurn && PlayerMove[1][1]==PlayerTurn && PlayerMove[2][2]==PlayerTurn) { //xxx diag left->right
AnnounceWinner();
return; }
else if(PlayerMove[0][2]==PlayerTurn && PlayerMove[1][1]==PlayerTurn && PlayerMove[2][0]==PlayerTurn) {//xxx diag right->left
AnnounceWinner();
return;
}
//check tie??
CurrentPlayerTurn=!CurrentPlayerTurn;
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
if(PlayerMove[i][j]!='X' && PlayerMove[i][j]!='O')
PlayerMove[i][j]='_';
}
}
ClearScreen();
}
void AnnounceWinner()
{
char Winner;
if(CurrentPlayerTurn)
Winner='X';
else
Winner='O';
cout<<endl<<Winner<<" has won the game!";
PlayAgain();
}
void PlayAgain()
{
char Answer;
cout<<endl<<"Play Again? (y/n) ";
cin>>Answer;
if(toupper(Answer)=='Y')
{
system("cls");
CurrentPlayerTurn=true;
MoveCount=0;
main();
}
}
ixmike88
Junior Poster in Training
54 posts since Sep 2010
Reputation Points: 9
Solved Threads: 4
First things first. Welcome to Daniweb. Your ability to post code with code tags on your first post puts you at the head of the class!
Having said that, there are a couple things I would suggest.
#1) When writing code using standard C++ try to avoid using global variables. The only exception here might be values declared to be const that are to be used throughout the program. There are other formats where avoiding global variables is more difficult to do (here I'm thinking of Windows API) but it is a good habit to get into.
#2) Use const variables when you can rather than magic numbers. In this case using variables such as:
const int NUMROWS = 3;
const int NUMCOLS = 3;
would be useful. Yes, it frequently means you end up typing more, but that is more than made up for in recognizing intent, increasing flexibility of programming and increasing robustness of your programs.
3) PlayerMove should be declared size 3 x 3, not 2 x 2. Using const ints as described above would help you recognize that. As written there are multiple instances of writing beyond the array, which may well be the cause of your error. Fix it first and see what happens.
4) Many experienced coders prefer to use spaces for indenting rather than tabs as they feel it is easier to read as can be most readily experienced with multiple nested loops and control statements.
Lerner
Nearly a Posting Maven
2,382 posts since Jul 2005
Reputation Points: 739
Solved Threads: 396
thanks for the feedback
#1, can you provide any specific replacements that i could make or link me to an alternative method?
#2, i see where i could be doing that instead of i=2, etc. is 'const int' better than #DEFINE? or are they the same thing (at least for my purposes?) or anything like that
#3, why does it need to be 3x3? i realized it needed 3 values but isn't that 0 1 and 2?
ixmike88
Junior Poster in Training
54 posts since Sep 2010
Reputation Points: 9
Solved Threads: 4
#1) I'm not sure what you mean by replacements. I think you did a pretty good job in general with code as posted. The only other suggestion I would make is not to try to write the entire program and then try to debug it. Write only a single action or declare only a single (set of) variable(s) before compiling/running/testing.
#2) In my opinion declaring variables with keyword const is better than #DEFINE, but others may disagree with me.
#3) Arrays indexes are zero based so if you declare an array like this:
const int SIZE = 3;
int myArray[SIZE];
then myArray can hold up to 3 objects of type int (that's called the size or capacity of the array---some people use size to refer to actual number of elements assigned to the array as opposed to the maximum number of elements it could hold) and valid indexes will be 0, 1 and 2. If you declare myArray like this:
int myArray[2];
then valid indexes are 0 and 1 and if you declare an array like this:
const int NUMROWS = 3;
const int NUMCOLS = 3;
char charArray[NUMROWS][NUMCOLS];
Then valid elements are (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1) and (2, 2).
Lerner
Nearly a Posting Maven
2,382 posts since Jul 2005
Reputation Points: 739
Solved Threads: 396
#1 i was referring to replacing the global variables with a different method since you said to avoid them, how would you go about them?
#3 yeah once i set the maximum size to 3 instead of 2 the problem was corrected, thanks again
ixmike88
Junior Poster in Training
54 posts since Sep 2010
Reputation Points: 9
Solved Threads: 4
I would declare NUMROWS and NUMCOLS to be global const ints, though you could make them local to main() if you wish. Since they are const int and they will be used in many functions, then global is okay. I would declare PlayerMoves, MoveCount and CurrentPlayerTurn local to main() and pass them to whatever function needs them to do what they need to do or to get them to functions downstream if needed.
const int NUMROWS = 3;
const int NUMCOLS = 3;
int main()
{
char PlayerMoves[NUMROWS][NUMCOLS];
int MoveCount = 0;
bool CurrentPlayerTurn = true;
ClearScreen(PlayerMoves, MoveCount, CurrentPlayer);
However, I'd really change the flow of the game if it were me. Maybe to something like this:
#include <iostream>
//declare functions here
//declare const variables
const int NUMROWS = 3;
const int NUMCOLS = 3;
int main()
{
char Board[NUMROWS][NUMCOLS];
char currentPlayer = 'x';
int MoveCount = 0;
bool playAgain = true;
bool gameOver = false;
while(playAgain)
{
setBoardToDefault(Board);
while(!gameOver)
{
displayBoard(Board);
getPlayerMove(Board, currentPlayer);
++moveCount;
gameOver = checkForWinner(Board, moveCount, currentPlayer);
if(!gameOver)
currentPlayer = switchPlayer(currentPlayer);
}
displayBoard(Board);
playAgain = askToPlayAgain();
if(playAgain)//reset variables
{
currentPlayer = 'x';
MoveCount = 0;
gameOver = false;
}
}
return 0;
}
//define functions here
Obviously the functions would need to be declared and defined and there will be other functions needed, just like in your version, but my version allows me to minimize passing variables while still keeping them non-global (with noted exceptions) and allows for multiple games to be played without calling main() more than once, since calling main() recursively is considered bad form.
Lerner
Nearly a Posting Maven
2,382 posts since Jul 2005
Reputation Points: 739
Solved Threads: 396