#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define COMPUTER 1
#define HUMAN 2
#define SIDE 3
#define COMPUTERMOVE 'O'
#define HUMANMOVE 'X'
// ---------------- Intelligent Moves start
struct Move {
int row, col;
};
char player = 'x', opponent = 'o';
// This function returns true if there are moves
// remaining on the board. It returns false if
// there are no moves left to play.
bool isMovesLeft(char board[3][3])
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i][j] == '_')
return true;
return false;
}
// This is the evaluation function
int evaluate(char b[3][3])
{
// Checking for Rows for X or O victory.
for (int row = 0; row < 3; row++) {
if (b[row][0] == b[row][1]
&& b[row][1] == b[row][2]) {
if (b[row][0] == player)
return +10;
else if (b[row][0] == opponent)
return -10;
}
}
// Checking for Columns for X or O victory.
for (int col = 0; col < 3; col++) {
if (b[0][col] == b[1][col]
&& b[1][col] == b[2][col]) {
if (b[0][col] == player)
return +10;
else if (b[0][col] == opponent)
return -10;
}
}
// Checking for Diagonals for X or O victory.
if (b[0][0] == b[1][1] && b[1][1] == b[2][2]) {
if (b[0][0] == player)
return +10;
else if (b[0][0] == opponent)
return -10;
}
if (b[0][2] == b[1][1] && b[1][1] == b[2][0]) {
if (b[0][2] == player)
return +10;
else if (b[0][2] == opponent)
return -10;
}
// Else if none of them have won then return 0
return 0;
}
// This is the minimax function. It considers all
// the possible ways the game can go and returns
// the value of the board
int minimax(char board[3][3], int depth, bool isMax)
{
int score = evaluate(board);
// If Maximizer has won the game return his/her
// evaluated score
if (score == 10)
return score;
// If Minimizer has won the game return his/her
// evaluated score
if (score == -10)
return score;
// If there are no more moves and no winner then
// it is a tie
if (isMovesLeft(board) == false)
return 0;
// If this maximizer's move
if (isMax) {
int best = -1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j] == '_') {
// Make the move
board[i][j] = player;
int val
= minimax(board, depth + 1, !isMax);
if (val > best) {
best = val;
}
// Undo the move
board[i][j] = '_';
}
}
}
return best;
}
// If this minimizer's move
else {
int best = 1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j] == '_') {
// Make the move
board[i][j] = opponent;
// Call minimax recursively and choose
int val
= minimax(board, depth + 1, !isMax);
if (val < best) {
best = val;
}
// Undo the move
board[i][j] = '_';
}
}
}
return best;
}
}
// This will return the best possible move for the player
struct Move findBestMove(char board[3][3])
{
int bestVal = -1000;
struct Move bestMove;
bestMove.row = -1;
bestMove.col = -1;
// Traverse all cells, evaluate minimax function for
// all empty cells. And return the cell with optimal
// value.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j] == '_') {
// Make the move
board[i][j] = player;
// compute evaluation function for this
// move.
int moveVal = minimax(board, 0, false);
// Undo the move
board[i][j] = '_';
// If the value of the current move is
// more than the best value, then update
// best/
if (moveVal > bestVal) {
bestMove.row = i;
bestMove.col = j;
bestVal = moveVal;
}
}
}
}
// printf("The value of the best Move is : %d\n\n",
// bestVal);
return bestMove;
}
// -----------------------------------Intelligent Moves end
// Function to display the game board
void showBoard(char board[][SIDE])
{
printf("\n\n");
printf("\t\t\t %c | %c | %c \n", board[0][0],
board[0][1], board[0][2]);
printf("\t\t\t--------------\n");
printf("\t\t\t %c | %c | %c \n", board[1][0],
board[1][1], board[1][2]);
printf("\t\t\t--------------\n");
printf("\t\t\t %c | %c | %c \n\n", board[2][0],
board[2][1], board[2][2]);
}
// Function to show the instructions
void showInstructions()
{
printf("\t\t\t Tic-Tac-Toe\n\n");
printf("Choose a cell numbered from 1 to 9 as below "
"and play\n\n");
printf("\t\t\t 1 | 2 | 3 \n");
printf("\t\t\t--------------\n");
printf("\t\t\t 4 | 5 | 6 \n");
printf("\t\t\t--------------\n");
printf("\t\t\t 7 | 8 | 9 \n\n");
printf("-\t-\t-\t-\t-\t-\t-\t-\t-\t-\n\n");
}
// Function to initialise the game
void initialise(char board[][SIDE], int moves[])
{
srand(time(NULL));
// Initially, the board is empty
for (int i = 0; i < SIDE; i++) {
for (int j = 0; j < SIDE; j++)
board[i][j] = ' ';
}
// Fill the moves with numbers
for (int i = 0; i < SIDE * SIDE; i++)
moves[i] = i;
// Randomize the moves
for (int i = 0; i < SIDE * SIDE; i++) {
int randIndex = rand() % (SIDE * SIDE);
int temp = moves[i];
moves[i] = moves[randIndex];
moves[randIndex] = temp;
}
}
// Function to declare the winner of the game
void declareWinner(int whoseTurn)
{
if (whoseTurn == COMPUTER)
printf("COMPUTER has won\n");
else
printf("HUMAN has won\n");
}
// Function to check if any row is crossed with the same
// player's move
int rowCrossed(char board[][SIDE])
{
for (int i = 0; i < SIDE; i++) {
if (board[i][0] == board[i][1]
&& board[i][1] == board[i][2]
&& board[i][0] != ' ')
return 1;
}
return 0;
}
// Function to check if any column is crossed with the same
// player's move
int columnCrossed(char board[][SIDE])
{
for (int i = 0; i < SIDE; i++) {
if (board[0][i] == board[1][i]
&& board[1][i] == board[2][i]
&& board[0][i] != ' ')
return 1;
}
return 0;
}
// Function to check if any diagonal is crossed with the
// same player's move
int diagonalCrossed(char board[][SIDE])
{
if ((board[0][0] == board[1][1]
&& board[1][1] == board[2][2]
&& board[0][0] != ' ')
|| (board[0][2] == board[1][1]
&& board[1][1] == board[2][0]
&& board[0][2] != ' '))
return 1;
return 0;
}
// Function to check if the game is over
int gameOver(char board[][SIDE])
{
return (rowCrossed(board) || columnCrossed(board)
|| diagonalCrossed(board));
}
// Function to play Tic-Tac-Toe
void playTicTacToe(int whoseTurn)
{
// A 3*3 Tic-Tac-Toe board for playing
char board[SIDE][SIDE];
int moves[SIDE * SIDE];
// Initialise the game
initialise(board, moves);
// Show the instructions before playing
showInstructions();
int moveIndex = 0, x, y;
// Keep playing until the game is over or it is a draw
while (!gameOver(board) && moveIndex != SIDE * SIDE) {
if (whoseTurn == COMPUTER) {
char tempBoard[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == 'X') {
tempBoard[i][j] = 'x';
}
else if (board[i][j] == 'O') {
tempBoard[i][j] = 'o';
}
else {
tempBoard[i][j] = '_';
}
}
}
struct Move thisMove = findBestMove(tempBoard);
x = thisMove.row;
y = thisMove.col;
board[x][y] = COMPUTERMOVE;
printf("COMPUTER has put a %c in cell %d %d\n",
COMPUTERMOVE, x, y);
showBoard(board);
moveIndex++;
whoseTurn = HUMAN;
}
else if (whoseTurn == HUMAN) {
int move;
printf("Enter your move (1-9): ");
scanf("%d", &move);
if (move < 1 || move > 9) {
printf("Invalid input! Please enter a "
"number between 1 and 9.\n");
continue;
}
x = (move - 1) / SIDE;
y = (move - 1) % SIDE;
if (board[x][y] == ' ') {
board[x][y] = HUMANMOVE;
showBoard(board);
moveIndex++;
if (gameOver(board)) {
declareWinner(HUMAN);
return;
}
whoseTurn = COMPUTER;
}
else {
printf("Cell %d is already occupied. Try "
"again.\n",
move);
}
}
}
// If the game has drawn
if (!gameOver(board) && moveIndex == SIDE * SIDE)
printf("It's a draw\n");
else {
// Toggling the user to declare the actual winner
if (whoseTurn == COMPUTER)
whoseTurn = HUMAN;
else if (whoseTurn == HUMAN)
whoseTurn = COMPUTER;
// Declare the winner
declareWinner(whoseTurn);
}
}
// Driver program
int main()
{
// Let us play the game with COMPUTER starting first
playTicTacToe(COMPUTER);
return 0;
}