I have made an attempt to make a single player mode for the Tic-Tac-Toe game. I am not sure if this is the right way to do it and wanted a second opinion.
// keeps track of all the valid positions.
bool validPos[10] = {false, true, true, true, true, true, true, true, true, true};
char user = 'O', computer = 'X';
int main(void)
{
// A two-dimensional array depicting the game.
char game[3][3] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
char choice;
// Asks user for a valid input.
do
{
printf("X or O ?\n");
scanf(" %c", &choice);
}while(choice != 'X' && choice != 'O' && choice != 'x' && choice != 'o');
// assigns X or O to the player.
if(choice == 'x' || choice == 'X')
{
user = 'X';
computer = 'O';
}
display(game);
printf("You - %c\n", user);
printf("Computer - %c\n", computer);
startGame(game, user);
}
Function startGame
void startGame(char game[3][3], const char turn)
{
int i, j;
int pos;
srand((unsigned)time(NULL));
if(turn == user)
{
// asks user for a valid input.
do
{
printf("Your turn : ");
scanf("%d", &pos);
}while((pos > 9 || pos < 1) || !validPos[pos]);
}
else
{
do
{
// calculates a good and valid move for the computer.
pos = rand() % 10;
}while((pos > 9 || pos < 1) || !validPos[pos]);
pos = calcMove(game, pos);
}
// makes the position chosen invalid.
validPos[pos] = false;
// Assigns 'X' or 'O' to the chosen position.
for( i = 0; i < 3; i++)
for( j = 0; j < 3; j++)
if(game[i][j] - '0' == pos)
game[i][j] = turn;
// prints the game onto the screen.
display(game);
// checks if the player or computer has won or not.
if(gameStatus(game))
{
if(turn == user)
printf("You Win !\n");
else
printf("Computer Wins !\n");
return;
}
// checks if there are any further moves possible.
// also acts as the base case for the game to end.
if(isComplete(game))
{
printf("Game Over!\nNo More Possible Moves\n");
return;
}
// recursive call to continue the game.
if(turn == user)
startGame(game, computer);
if(turn == computer)
startGame(game, user);
}
Function calcMove This is where I am unsure about my method of calculating a good move for the computer as I feel this is redundant.
int calcMove(char game[][3], int pos)
{
int i, j;
int move = 0;
// If there is any move along a row that is a winning move for the computer
// then take it or blocks the winning move of the user along a row.
for(i = 0; i < 3; i++)
{
if(game[i][0] == game[i][1] && validPos[game[i][2] - '0'])
{
move = game[i][2] - '0';
if(game[i][0] == computer)
return move;
}
if(game[i][0] == game[i][2] && validPos[game[i][1] - '0'])
{
move = game[i][1] - '0';
if(game[i][0] == computer)
return move;
}
if(game[i][1] == game[i][2] && validPos[game[i][0] - '0'])
{
move = game[i][0] - '0';
if(game[i][1] == computer)
return move;
}
}
// If there is any move along a coloumn that is a winning move for the computer
// then take it or blocks the winning move of the user along a coloumn.
for(j = 0; j < 3; j++)
{
if(game[0][j] == game[1][j] && validPos[game[2][j] - '0'])
{
move = game[2][j] - '0';
if(game[0][j] == computer)
return move;
}
if(game[1][j] == game[2][j] && validPos[game[0][j] - '0'])
{
move = game[0][j] - '0';
if(game[1][j] == computer)
return move;
}
if(game[0][j] == game[2][j] && validPos[game[1][j] - '0'])
{
move = game[1][j] - '0';
if(game[0][j] == computer)
return move;
}
}
// checks for the winning move along the right diagonal.
// preferance is given to winning move for computer.
if(game[0][0] == game[1][1] && validPos[game[2][2] - '0'])
{
move = game[2][2] - '0';
if(game[0][0] == computer)
return move;
}
if(game[0][0] == game[2][2] && validPos[game[1][1] - '0'])
{
move = game[1][1] - '0';
if(game[0][0] == computer)
return move;
}
if(game[1][1] == game[2][2] && validPos[game[0][0] - '0'])
{
move = game[0][0] - '0';
if(game[1][1] == computer)
return move;
}
// checks for the winning move along the left diagonal.
// preferance is given to winning move for computer.
if(game[0][2] == game[1][1] && validPos[game[2][0] - '0'])
{
move = game[2][0] - '0';
if(game[0][2] == computer)
return move;
}
if(game[0][2] == game[2][0] && validPos[game[1][1] - '0'])
{
move = game[1][1] - '0';
if(game[0][2] == computer)
return move;
}
if(game[1][1] == game[2][0] && validPos[game[0][2] - '0'])
{
move = game[0][2] - '0';
if(game[1][1] == computer)
return move;
}
// if there was any such move in which either the player or
// the computer would win, then return it.
if(move != 0)
return move;
// if there was no such move, then select any of the corners
// or the center randomly.
switch(rand() % 4)
{
case 1 : if(validPos[7] && validPos[3])
return 7;
case 2 : if(validPos[3] && validPos[7])
return 3;
case 3 : if(validPos[1] && validPos[9])
return 1;
case 4 : if(validPos[5])
return 5;
default : return pos;
}
}
P.S I've not included the code snippet for functions disply()
, isComplete()
and gameStatus()
as I think these are simple, and there may not be much to review there