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();
     }          
}

Recommended Answers

All 6 Replies

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.

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?

#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;

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).

#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

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.

does anybody know how to add a second player?

#include<iostream.h>
using namespace std;

/*
This program controls a Tic Tac Toe game.
It introduces the concept of arrays.
A 1D array is just one row or one column.
A 2D array is like an Excel Spreadsheet.
a 3D array is like a Rubik's cube.

We use a 3x3 2D array to hold user data.
Each cell has a Row [R] and a Column [C].
C++ arrays count from zero (0 to (n-1)).
Our 3x3 array goes from 0-2 on any side.

 [R][C] [R][C] [R][C]
 [0][0] [0][1] [0][2]
 [1][0] [1][1] [1][2]
 [2][0] [2][1] [2][2] 

Below is our chosen user interface.
On the left is the board for moves.
On the right is a guide for numpad.
Player uses numpad to input moves.

 0123456789012345678 
0+-----------------+0 border row
1|                 |1 blank  row
2|  | |     7|8|9  |2 [0] data row
3| -+-+-    -+-+-  |3 middle row
4|  | |     4|5|6  |4 [1] data row
5| -+-+-    -+-+-  |5 middle row
6|  | |     1|2|3  |6 [2] data row
7|                 |7 blank  row
8+-----------------+8 border row
 0123456789012345678 
  [0] data column
    ^  middle column
    [1] data column
      ^  middle column
      [2] data column

For ASCII codes see http://www.asciitable.com/      
*/

// Let us set up some global constants
// 1D arrays for output
const int iLineSize = 19; // length of 1D output array
char cSpcsLine[iLineSize]; // string for blank  row
char cDataLine[iLineSize]; // string for data   row
char cMidlLine[iLineSize]; // string for middle row
char cBrdrLine[iLineSize]; // string for border row
// 2D array for data
const int iGridSize =  3; // sides of 2D data array
char cDataGrid[iGridSize][iGridSize]; // grid for data
char cCell; // used for cell contents of data array

// SetupBrdrRow() function
// Setup string for border row
// 0123456789012345678 
// +-----------------+ border row
void SetupBrdrRow(bool bShow)
{
    // loop through each column in on a row
    for(int iCol = 0; iCol < iLineSize; iCol++)
    {
        // Are we in any center position?
        if(iCol > 0 && iCol < iLineSize - 1)
        {
            // put '-' in cell
            cCell = char(196);
        }
        else // we are on the border
        {
            // put '+' in cell
            cCell = char(197);            
        }
        // put cell in array
        cBrdrLine[iCol] = cCell;
    }
    
    if(bShow)
    {
        cout << "Border row: " << cBrdrLine << endl;
    }    
}

// SetupSpcsRow() function
// Setup string for blank row
// 0123456789012345678
// |                 | blank  row
void SetupSpcsRow(bool bShow)
{
    // loop through each column in on a row
    for(int iCol = 0; iCol < iLineSize; iCol++)
    {
        // Are we in any center position?
        if(iCol > 0 && iCol < iLineSize - 1)
        {
            // put ' ' in cell
            cCell = ' ';
        }
        else // we are on the border
        {
            // put '|' in cell
            cCell = char(179);            
        }
        // put cell in array
        cSpcsLine[iCol] = cCell;
    }
    
    if(bShow)
    {
        cout << "Spaces row: " << cSpcsLine << endl;
    }    
}

// SetupDataRow() function
// Setup string for data row
// 0123456789012345678 
// |  | |     #|#|#  | [0] data row
void SetupDataRow(bool bShow)
{
    // loop through each column in on a row
    for(int iCol = 0; iCol < iLineSize; iCol++)
    {
        // Are we in any center position?
        if(iCol > 0 && iCol < iLineSize - 1)
        {
            switch(iCol)
            {
                case  3:
                case  5:
                case 12:
                case 14:
                    // put '|' in cell
                    cCell = char(179);
                    break;
                case 11:
                case 13:
                case 15:
                    // put '#' in cell
                    cCell = '#'; 
                    break;
                default:
                    // put ' ' in cell
                    cCell = ' ';
                    break;
            }
        }
        else // we are on the border
        {
            // put '|' in cell
            cCell = char(179);            
        }
        // put cell in array
        cDataLine[iCol] = cCell;
    }
    
    if(bShow)
    {
        cout << "Data   row: " << cDataLine << endl;
    }
}

// SetupMidlRow() function
// Setup string for middle row
// 0123456789012345678 
// | -+-+-    -+-+-  | middle row 
void SetupMidlRow(bool bShow)
{
    // loop through each column in on a row
    for(int iCol = 0; iCol < iLineSize; iCol++)
    {
        // Are we in any center position?
        if(iCol > 0 && iCol < iLineSize - 1)
        {
            switch(iCol)
            {
                case  2:
                case  4:
                case  6:
                case 11:
                case 13:
                case 15:
                    // put '-' in cell
                    cCell = char(196);
                    break;
                case  3:
                case  5:
                case 12:
                case 14:
                    // put '+' in cell
                    cCell = char(197); 
                    break;
                default:
                    // put ' ' in cell
                    cCell = ' ';
                    break;
            }
        }
        else // we are on the border
        {
            // put '|' in cell
            cCell = char(179);            
        }

        // put cell in array
        cMidlLine[iCol] = cCell;
    }
    
    if(bShow)
    {
        cout << "Middle row: " << cMidlLine << endl;
    }
}

// SetupBoard() function
// Prepares to start new game
void SetupBoard()
{
    // Initialize board data
    // loop through each row    
    for(int iRow = 0; iRow < iGridSize; iRow++)
    {
        // loop through each colum on a row        
        for(int iCol = 0; iCol < iGridSize; iCol++)
        {
            // put ' ' in cell
            cCell = ' ';
            // put cell in array
            cDataGrid[iRow][iCol] = cCell;
        }
    }
}

// ShowBoard() function
// Puts data into display lines
// Puts display lines to screen
// 0123456789012345678 
// |  | |     #|#|#  | [0] data row
void ShowBoard()
{
    int iCnt; // holds cell count
    int iDat; // holds data position
    int iKey; // holds num pad key
    int iGde; // holds guide position
     // holds line position
    // put new line to screen
    cout << endl << "T i c  T a c  T o e " << endl;
    // put Border row to screen
    cout << cBrdrLine << endl;
    // put Spaces row to screen
    cout << cSpcsLine << endl;
    // loop through each row
    for(int iRow = 0; iRow < iGridSize; iRow++)
    {
        // loop through each column on a row        
        for(int iCol = 0; iCol < iGridSize; iCol++)
        {
            // get cell from array
            cCell = cDataGrid[iRow][iCol];
            iCnt = (iRow * iGridSize) + iCol;
            switch(iCnt)
            {
                case 0: iDat = 2; iKey = 7; iGde = 11; break;
                case 1: iDat = 4; iKey = 8; iGde = 13; break;
                case 2: iDat = 6; iKey = 9; iGde = 15; break;
                case 3: iDat = 2; iKey = 4; iGde = 11; break;
                case 4: iDat = 4; iKey = 5; iGde = 13; break;
                case 5: iDat = 6; iKey = 6; iGde = 15; break;
                case 6: iDat = 2; iKey = 1; iGde = 11; break;
                case 7: iDat = 4; iKey = 2; iGde = 13; break;
                case 8: iDat = 6; iKey = 3; iGde = 15; break;
            }            
            // put cell in array
            cDataLine[iDat] = cCell;
            // put key in array
            cDataLine[iGde] = char(iKey + 48); 
        }
        // put Data   row to screen
        cout << cDataLine << endl;
        
        // Do we need middle row?
        // are we in row 0 or 1?
        if(iRow == 0 || iRow == 1)
        {
            // put Middle row to screen
            cout << cMidlLine << endl;
        }
    }
    // put Spaces row to screen
    cout << cSpcsLine << endl;    
    // put Border row to screen
    cout << cBrdrLine << endl;    
    // put new line to screen
    cout << endl;    
}

// this is the main function of the program
int main()
{
    char cInput; // used for user input
    bool bValid; // used for user validation
    int  iRow;
    int  iCol;
    bool bDisplay = false; // true shows stuff
    SetupBrdrRow(bDisplay);    
    SetupSpcsRow(bDisplay);
    SetupDataRow(bDisplay);
    SetupMidlRow(bDisplay);
    SetupBoard();
    // Main program loop
    do
    {
        // Input loop
        do
        {
            bValid = true;
            ShowBoard();            
            cout << "Press nuumber key (1-9): ";
            cin >> cInput;
            cout << endl;
            switch(cInput)
            {
                case '1': iRow = 2; iCol = 0; break;
                case '2': iRow = 2; iCol = 1; break;
                case '3': iRow = 2; iCol = 2; break;
                case '4': iRow = 1; iCol = 0; break;
                case '5': iRow = 1; iCol = 1; break;
                case '6': iRow = 1; iCol = 2; break;
                case '7': iRow = 0; iCol = 0; break;
                case '8': iRow = 0; iCol = 1; break;
                case '9': iRow = 0; iCol = 2; break;
                default:
                    bValid = false;
                    cout << cInput << " is not valid input." << endl;  
                    break;
            }
            if(bValid)
            {
                // get cell from array
                cCell = cDataGrid[iRow][iCol];
                // is the cell vacant?
                if(cCell == ' ')
                {
                    // put 'X' in cell
                    cCell = 'X';
                    // put cell in array
                    cDataGrid[iRow][iCol] = cCell;
                }
                else
                {
                    bValid = false;
                    cout << cInput << " has already been taken." << endl;  
                }
            }
        }while(bValid);

    }while(true);

    cin >> cInput; // use to pause screen
    return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.