The problem is simple. I made a 3X3 tictactoe game with 3X3 arrays. But the problem is:
array[0][3] = array[1][0]
Which is strange because first of all, the array I made didn't have a fourth column. So array[0][3]
doesn't even exist! And to make matters complicated, it takes the value of [1][0]
I'm having problems when I input co ordinates of my move as: 0 2
void displayBoard(int tictac[3][3])
{
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
cout << tictac[i][j] << " ";
} cout << "\n" ;
} cout << "\n";
}
int Horizontal(int x, int y, int tictac[3][3])
{
if(tictac[x][y+1]==0)
{
tictac[x][y+1]=2;
return 1;
}
if(tictac[x][y-1]==0)
{
tictac[x][y-1]=2;
return 1;
}
if(tictac[x][y-2]==0)
{
tictac[x][y-2]=2;
return 1;
}
if(tictac[x][y+2]==0)
{
tictac[x][y+2]=2;
return 1;
}
return 0;
}
int Vertical(int x, int y, int tictac[3][3])
{
if(tictac[x+1][y]==0)
{
tictac[x+1][y]=2;
return 1;
}
if(tictac[x-1][y]==0)
{
tictac[x-1][y]=2;
return 1;
}
if(tictac[x-2][y]==0)
{
tictac[x-2][y]=2;
return 1;
}
if(tictac[x+2][y]==0)
{
tictac[x+2][y]=2;
return 1;
}
return 0;
}
void AI(int X,int Y,int tictac[3][3])
{
int done = 0;
cout << "\n-------------------------\nComputer plays: \n";
done = Horizontal(X,Y,tictac);
if(done == 0)
{
done = Vertical(X,Y,tictac);
}
}
int main()
{
int tictac[3][3] = {{0,0,0},{0,0,0}, {0,0,0} };
int X, Y;
for(int r=1; r<100; r++)
{
cout << "\n-------------------------\nPlayer play a move: \n";
cin >> X;
cin >> Y;
if(tictac[X][Y]==0)
{
tictac[X][Y] = 1;
displayBoard(tictac);
AI(X,Y,tictac);
displayBoard(tictac);
}
else
{
cout << "Space occupied. Try different cell." << endl;
}
}
}
You need to add bounds checking. For instance when the user inputs the move coordinates you need to ensure they are within the range of 0 to 2. The example below validates the input to ensure only numbers are entered, that both X and Y coordinates are entered on a single line and that the coordinates are within range. It uses std::stringstream
to parse the coordinates instead of having to deal with checking and clearing the fail bits on std::cin
#include <string> // at top of your .cpp file
#include <sstream>
// in main()
// Get en entire input line so we can skip extra characters
// after the cell coordinates
string inputLine;
std::getline(cin, inputLine);
stringstream inputStream(inputLine);
if(!(inputStream >> X) || !(inputStream >> Y))
{
cout << "Please enter the cell coordinates in the form of # #" << endl;
continue;
}
bool invalidCoordinates = false;
if(X < 0 || X > 2)
{
cout << "invalid X location" << endl;
invalidCoordinates = true;
}
if(Y < 0 || Y > 2)
{
cout << "invalid Y location" << endl;
invalidCoordinates = true;
}
// check for invalid input
if(invalidCoordinates) continue;
You also need to do the same thing in your Vertical
and Horizontal
functions when checking if a valid move is possible. For instance if x
is 2 and y
is 2 the following lines from Vertical
will access data outside the bounds of the array.
if(tictac[x+1][y]==0)
{
tictac[x+1][y]=2;
return 1;
}
This is because you are actually accessing the forth element with x+1
. This element technically doesn't exist but with a multi-dimensional array you end up accessing tictac[0][y+1]
instead.
You can get around the bounds checking in Vertical
and Horizontal
by adding some padding around the edges and fill them with a value that indicates they are unusable. In your case increase the size by 3 in each direction.
int tictac[9][9] = {
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
};
You will need to make adjustments to X
and Y
appropriately so they point to the correct location.
X += 3; // Adjust for padding
Y += 3; // Adjust for padding
if(tictac[X][Y]==0)
{
tictac[X][Y] = 1;
displayBoard(tictac);
AI(X,Y,tictac);
displayBoard(tictac);
}
You may need to make adjustments in other parts of your code but the above example should get your started.
There is also a problem in your displayBoard
function. When it prints out the elements of the array i
and j
are reversed so the board appears rotated 90 degrees. Change the following line
cout << tictac[i][j] << " ";
to
cout << tictac[j][i] << " ";
Another problem is that you are using \n
at the end of each line you output without using std::flush
to ensure the line is sent to the console. You can either put << flush;
after those lines or remove the \n
and put << endl;
at the end of the line.
cout << "\n-------------------------\nComputer plays: \n" << flush;
or
cout << "\n-------------------------\nComputer plays: " << endl;
The code below is a complete update of the original code included in your question. It incorporates the above suggestions and makes a couple of other changes. I've also added an endgame check to determine if there are any moves left.
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
static const int BoardSize = 3;
static const int BoardPadding = BoardSize;
static const int ArraySize = BoardSize + (BoardPadding * 2);
void displayBoard(int tictac[ArraySize][ArraySize])
{
for(int y = 0; y < BoardSize; y++)
{
for(int x = 0; x < BoardSize; x++)
{
cout << tictac[BoardPadding + x][BoardPadding + y] << " ";
}
cout << endl ;
}
cout << endl;
}
int Horizontal(int x, int y, int tictac[ArraySize][ArraySize])
{
if(tictac[x][y+1]==0)
{
tictac[x][y+1]=2;
return 1;
}
if(tictac[x][y-1]==0)
{
tictac[x][y-1]=2;
return 1;
}
if(tictac[x][y-2]==0)
{
tictac[x][y-2]=2;
return 1;
}
if(tictac[x][y+2]==0)
{
tictac[x][y+2]=2;
return 1;
}
return 0;
}
int Vertical(int x, int y, int tictac[ArraySize][ArraySize])
{
if(tictac[x+1][y]==0)
{
tictac[x+1][y]=2;
return 1;
}
if(tictac[x-1][y]==0)
{
tictac[x-1][y]=2;
return 1;
}
if(tictac[x-2][y]==0)
{
tictac[x-2][y]=2;
return 1;
}
if(tictac[x+2][y]==0)
{
tictac[x+2][y]=2;
return 1;
}
return 0;
}
void AI(int X,int Y,int tictac[ArraySize][ArraySize])
{
int done = 0;
cout << "\n-------------------------\nComputer plays: " << endl;
done = Horizontal(X,Y,tictac);
if(done == 0)
{
done = Vertical(X,Y,tictac);
}
}
// Check if all moves have been made
bool isEndGame(int tictac[ArraySize][ArraySize])
{
int count = 0;
for(int y = 0; y < BoardSize; y++)
{
for(int x = 0; x < BoardSize; x++)
{
count += tictac[BoardPadding + x][BoardPadding + y] ? 1 : 0;
}
}
return count == (BoardSize * BoardSize);
}
int main()
{
int tictac[ArraySize][ArraySize] = {
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,0,0,0,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
{3,3,3,3,3,3,3,3,3},
};
int X, Y;
while(isEndGame(tictac) == false)
{
cout << "\n-------------------------\nPlayer play a move: " << flush;
// Get en entire input line so we can skip extra characters
// after the cell coordinates
string inputLine;
std::getline(cin, inputLine);
stringstream inputStream(inputLine);
if(!(inputStream >> X) || !(inputStream >> Y))
{
cout << "Please enter the cell coordinates in the form of # #" << endl;
continue;
}
bool invalidCoordinates = false;
if(X < 0 || X >= BoardSize)
{
cout << "invalid X location" << endl;
invalidCoordinates = true;
}
if(Y < 0 || Y >= BoardSize)
{
cout << "invalid Y location" << endl;
invalidCoordinates = true;
}
// check for invalid input
if(invalidCoordinates) continue;
// adjust the coordinates and do our thing
X += BoardPadding;
Y += BoardPadding;
if(tictac[X][Y]==0)
{
tictac[X][Y] = 1;
displayBoard(tictac);
AI(X,Y,tictac);
displayBoard(tictac);
}
else
{
cout << "Space occupied. Try different cell." << endl;
}
}
cout << "game finished...check for winner" << endl;
}
Note: It's a bad idea to use using namespace std;
. It pulls everything from the std
namespace into the current scope (in this case the global namespace) and can cause conflicts. It's best to use fully qualified names such as std::cout
instead to avoid this.