/* 
 * File: tictactoe.c
 * Author: Shankar Chakkere <shankara@erols.com, schakkere@denro.com>
 * Date: 19 Nov 1998
 * Status: Tested 20 Nov 1998
 * Compiles on: Borland C++ v2.0 & GCC cygnus-2.7.2-970404 on WIN95
 * Abstract : This program will play a game of tic tac toe.
 * This program is quite big, but self explanatory.
 * I know a way of reducing the length of this program
 * using modulus arithmetic.
 * Copyright (C) 1999 Shankar Chakkere
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Visit http://www.fsf.org for more information

 *
 */

#include <stdio.h>
#include <stdlib.h> /* for random number */
#include <time.h>

#define NOTOCCUPIED 0
#define NOTTHREATNING NOTOCCUPIED

/*
 * ME cannot be 0, because NOTOCCUPIED is also 0
 */

#define ME 1
#define YOU ( ME + 10 )
#define TIE 4

#define DEBUG 0
#define CONTINUEGAME 0
#define ENDGAME ~CONTINUEGAME
#define LR 5
#define RL 6
#define C1 7
#define C2 8
#define C3 9
#define R1 10
#define R2 11
#define R3 12

int checkwhowon(void);
int nextmove (int player);
int printstatus(void);

void printmove(void);
void occupy ( int direction); /* called after next move */
void getmove (void);

int moves[9];

int main()
{
int row,position,move;
time_t    tnow;
struct tm tmnow;

/* Initialise moves array 
*/
for ( row = 0 ; row <= 8 ; row++)  
    moves[row] = NOTOCCUPIED;

/* Flip coin to find who plays first
 * time() is used as a random generator seed
 */


move = 0;

tnow  = time(NULL);
tmnow = *localtime(&tnow);
srand(tmnow.tm_sec); /* current sec as the seed */

while (move == 0)
{
move = rand() % 3;
}

while (1)
{
    if (move == ME)
        {
        position = nextmove(ME);
        /* if favourable make move and win
         */
         if (position != NOTTHREATNING)
            occupy(position);     
         else 
            {
            occupy(nextmove(YOU));     
            }
        move = YOU;  /* Next move is YOU */
        }
    else
        {
        getmove();          /* get user move */
        move = ME;  /* Next move is COMPUTER */
        }        
   printmove();
   if (printstatus()) /* if game ends, exit */
        break;
    }    
return 0;
}

int checkwhowon()
{
/* Check if there is a winner
 * Status: Checked
 * RETURNS: ME,YOU, TIE or NOTOCCUPIED
 * Ver 2.0 11/19/98
 */

int outcome[8],x;

 /* ROW 1 */
 outcome[0] = moves[0] + moves[1] + moves[2];
 /* ROW 2 */
 outcome[1] = moves[3] + moves[4] + moves[5];
 /* ROW 3 */
 outcome[2] = moves[6] + moves[7] + moves[8];
 /* COL 1 */
 outcome[3] = moves[0] + moves[3] + moves[6];
 /* COL 2 */
 outcome[4] = moves[1] + moves[4] + moves[7];
 /* COL 3 */
 outcome[5] = moves[2] + moves[5] + moves[8];
 /* DIAG 1 */
 outcome[6] = moves[0] + moves[4] + moves[8];
 /* DIAG 2 */
 outcome[7] = moves[2] + moves[4] + moves[6];

 for (x = 0; x <= 7; x++)
     {
     if (outcome[x] == ME + ME + ME )
        return ME;      /* The computer won */
     else if (outcome[x] == YOU + YOU + YOU)
        return YOU;     /* The player won */
    }        
 for (x = 0; x < 3; x++)
    {
    if (outcome[x] != (ME + ME + YOU) && outcome[x] !=(YOU + YOU + ME))
        return NOTOCCUPIED;     /* NO RESULT YET */
    }
        return TIE;     /* TIE if the cells are
                         *  ME + ME + YOU or YOU + YOU + ME
                         */
       
}

void printmove()
{
/* Print the moves so far
 * ver 2.0 11/20/98
 */
int row,col;
char prnchar; /* X is opponent O is computer */

    for ( row = 0 ; row < 3 ; row++)  
        {
        for ( col = 0 ; col < 3 ; col++)
            {
            prnchar = ' ';
            if (moves[row * 3 + col] ==  ME)
                prnchar = 'O';
            if (moves[row * 3 + col] ==  YOU)
                prnchar = 'X';
            printf("%c ",prnchar);
            }
        printf("\n");
        }
}

void occupy ( int direction)
  {
/* Occupy the cell given the direction,
 * This prevent the user from winning in the next move.
 * If no direction is given , occupy the center if available else
 * make a random move. The directions are
 * LR = left to right diagonal
 * RL = Right to left diagonal
 * C1 = column 1
 * C2 = column 2
 * C3 = column 3
 * R1 = row 1
 * R2 = row 2
 * R3 = row 3
 * Status: Tested 11/11/98
 
 */    
int row,col; 
    switch(direction) {
        case LR:
                for ( col = 0 ; col < 3 ; col++)
                    {
                    if (moves[4 * col] == NOTOCCUPIED)
                        {
                        moves[4 * col] = ME;
                        printf ("Computer takes : %d\n",col * 4);
                        break;
                        }
                    }
                break;
    case RL:
            for ( col = 0 ; col < 3 ; col++)
                {
                if (moves[2 + 2 * col] == NOTOCCUPIED)
                    {
                    moves[2 + 2 * col] = ME;
                    printf ("Computer takes : %d\n", 2 + 2 * col);
                    break;
                    }
                }
            break;
    case C1 :
            for ( row = 0 ; row < 3 ; row++)
                {
                if (moves[3 * row] ==NOTOCCUPIED)
                    {
                    moves[3 * row] = ME;
                    printf ("Computer takes : %d\n", row * 3);
                    break;
                    }
                }
            break;
    case C2 :
            for ( row = 0 ; row < 3 ; row++)
                {
                if (moves[row * 3 + 1] ==NOTOCCUPIED)
                    {
                    moves[row * 3 + 1] = ME;
                    printf ("Computer takes : %d\n", row * 3 + 1);
                    break;
                    }
                }
            break;
    case C3 :
            for ( row = 0 ; row < 3 ; row++)
                {
                if (moves[row * 3 + 2] ==NOTOCCUPIED)
                    {
                    moves[row * 3 + 2] = ME;
                    printf ("Computer takes : %d\n",row * 3 + 2);
                    break;
                    }
                }
            break;
    case R1:
            for ( col = 0 ; col < 3 ; col++)
                {
                if (moves[col] ==NOTOCCUPIED)
                    {
                    moves[col] = ME;
                    printf ("Computer takes : %d\n", col);
                    break;
                    }
                }
            break;
    case R2:
            for ( col = 0 ; col < 3 ; col++)
                {
                if (moves[3 + col] ==NOTOCCUPIED)
                    {
                    moves[3 + col] = ME;
                    printf ("Computer takes : %d\n",3 + col);
                    break;
                    }
                }
            break;
    case R3:
            for ( col = 0 ; col < 3 ; col++)
                {
                if (moves[6 + col] ==NOTOCCUPIED)
                    {
                    moves[6 + col] = ME;
                    printf ("Computer takes : %d\n",6 + col);
                    break;
                    }
                }
            break;
    default:
           /* occupy the center of diagonal if available */
            if (moves[4] == NOTOCCUPIED)
                {
                moves[4] = ME;
                printf ("Computer takes : %d\n", 4);
                }
            else
                /* There is no intelligence here yet 
                 * make a random move
                 */
                {
                for ( row = 0 ; row <= 8 ; row++)  
                        if (moves[row] == NOTOCCUPIED)
                            {
                            moves[row] = ME;
                            printf ("Computer takes : %d\n",row);
                            return; /* only one move */
                            }
                }
            break;
            }            

}


int nextmove (int player)
{
/* Finds if the next move of the player will make him win. This routine
 * will find the strategic row, column or diagonal to block or to win
 * The row , column and diagonal are marked as below.
 * LR = left to right diagonal
 * RL = Right to left diagonal
 * C1 = column 1
 * C2 = column 2
 * C3 = column 3
 * R1 = row 1
 * R2 = row 2
 * R3 = row 3
 * Status: Tested 11/11/98
 */    

int outcome[8];

 /* ROW 1 */
 outcome[0] = moves[0] + moves[1] + moves[2];
 /* ROW 2 */
 outcome[1] = moves[3] + moves[4] + moves[5];
 /* ROW 3 */
 outcome[2] = moves[6] + moves[7] + moves[8];
 /* COL 1 */
 outcome[3] = moves[0] + moves[3] + moves[6];
 /* COL 2 */
 outcome[4] = moves[1] + moves[4] + moves[7];
 /* COL 3 */
 outcome[5] = moves[2] + moves[5] + moves[8];
 /* DIAG 1 */
 outcome[6] = moves[0] + moves[4] + moves[8];
 /* DIAG 2 */
 outcome[7] = moves[2] + moves[4] + moves[6];


/* Check if there is threat 
 */
        /* L to R */
       /*  two cells are occupied by the same player and an
        * cell is empty, this row, column, or diagonal is at
        * threat or favourable to win.
        */

  if (player == YOU)
  {
printf("YOU \n");
    if (outcome[6] == (YOU + YOU) )
        return LR;      /* Threat */

        /* R to L */
    if (outcome[7] == (YOU + YOU) )
        return RL;      /* Threat */

 /* COL 1 */
    if (outcome[3] == (YOU + YOU) )
        return C1;      /* Threat */

        /* Column 2 */
 /* COL 2 */
    if (outcome[4] == (YOU + YOU) )
        return C2;      /* Threat */

        /* Column 3 */
    if (outcome[5] == (YOU + YOU) )
        return C3;      /* Threat */

        /* Row 1 */
    if (outcome[0] == (YOU + YOU) )
        return R1;      /* Threat */


        /* Row 2 */
    if (outcome[1] == (YOU + YOU) )
        return R2;      /* Threat */


        /* Row 3 */
    if (outcome[2] == (YOU + YOU) )
        return R3;      /* Threat */
}
/* Check if there is a favourable move 
 */

  if (player == ME)
  {
    if (outcome[0] == (ME + ME))
        return R1; /* Favorable */

    if (outcome[1] == (ME + ME))
        return R2; /* Favorable */

    if (outcome[2] == (ME + ME))
        return R3; /* Favorable */

    if (outcome[3] == (ME + ME))
        return C1; /* Favorable */

    if (outcome[4] == (ME + ME))
        return C2; /* Favorable */

    if (outcome[5] == (ME + ME))
        return C3; /* Favorable */

    if (outcome[6] == (ME + ME))
        return LR; /* Favorable */

    if (outcome[7] == (ME + ME))
        return RL; /* Favorable */

   }
/* Neither Threat nor Favourable next move
*/
return NOTOCCUPIED;            

}

void getmove ()
{
/* Gets user move,converts number to row and array. Validates move
 * and make user move
 * Ver 2.0 11/19/98
*/
int move;

    while (1)
        {
        printf( "\nYour move (X) ? \n");
        printf( "0 | 1 | 2\n");
        printf( "--+---+--\n");
        printf( "3 | 4 | 5\n");
        printf( "--+---+--\n");
        printf( "6 | 7 | 8\n");
        scanf("%d", &move);
        
        if ((move < 0) || ( move > 8)) /* Repeat if out of bound */
            continue;
        
        if (moves[move] != NOTOCCUPIED ) /* Repeat if occupied */
            continue;
        moves [move] = YOU;    
        break;
        }
printf ("Your take : %d \n", move);
}

int printstatus(void)
{
/* returns 1 if end of game else 0
*/
int player;

player = checkwhowon();
switch(player) {
    case ME:
            printf("Computer won !!! \n");
            return ENDGAME;
    case YOU:
            printf("You won !!! \n");
            return ENDGAME;
    case TIE :
            printf("GAME Tied !!! \n");
            return ENDGAME;
    default:
            return CONTINUEGAME;
            }            
}            
