/* This is a simple program to play Tic-tac-toe.  It
   uses minimax with alpha-beta pruning.  The default
   depth of the pruning is 4.  See function max and
   min for more details.
*/

#include <stdio.h>


/* Each operator acts on a certain row and certain column */

typedef struct _Operator {
    int row, col;
} Operator;

/*
   Each call to max or to min returns a Move structure.
   The "op" field of the Move structure contains the best
   operation or worst operation for the computer.  The
   value field indicates how bad or how good the "op"
   is the computer.
*/

typedef struct _Move {
    Operator op;
    int value;
} Move;

/* The state of the game is represented by showing
   the state of each cell.  If the cell contains a 0,
   we assume it is a blank, if the cell contains a 1,
   we assume it is an X, if the cell contains a 2, we
   assume it contains an O.
*/

typedef struct _State {
    int cell[3][3];
} State;

/* In this program, the human always plays first (X), the
   computer plays second (O)
*/

#define BLANK   0
#define USER    1
#define PROGRAM 2
#define MAXEVAL 1000

Move max ( State s, int d, int alpha, int beta );
Move min ( State s, int d, int alpha, int beta );
void initialize_state ( State *s );
int isTerminal ( State s );
int isValid ( State s, Operator operator );
int makeMove ( State *s, Operator operator, int who );
void print_state ( State s );


/* The main program.  Here is the algorithm for the main program:

   1.  Intialize the current state to all blanks.
   2.  Repeat
       a.  Ask the user for move.
       b.  Make the move (if valid), to get new current state.
       c.  If Terminal state, then state winner, or tie.  Stop.
       d.  Use the new current state as the FIRST STATE OF THE REST OF THE
GAME
           and call the max function.
       e.  Get the best operator from the Move returned by max.
       f.  Make the move and update current state
       Until current state is terminal.
*/

int main (int argc, char*argv[]) {

    State current;
    Operator operator;
    Move m;
    int done;

    initialize_state ( &current );

    do {
        print_state ( current );

        do {
            printf("Please input your move: ");

            printf("Row (1-3): ");
            scanf("%d", &operator.row);

            printf("Column (1-3): ");
            scanf("%d", &operator.col);

            operator.row--;
            operator.col--;
        } while ( ! isValid ( current, operator ) );

        makeMove ( &current, operator, USER );

        print_state ( current );

        if ( done = isTerminal ( current ) ) {
            if ( done == 1 )
                 printf("You win!\n");
            else 
                 printf("Tie!\n");
            print_state ( current );
            exit(0);
        }

        m = max ( current, 0, -MAXEVAL, MAXEVAL );

        makeMove ( &current, m.op, PROGRAM );

    } while ( ! isTerminal ( current ) );

    print_state ( current );
    printf("I win!\n");
}


/* Function initialize_state: Initializes all cells to BLANK */

void initialize_state ( State *current ){
    int i, j;

    for (i=0; i<3; i++)
        for (j=0;j<3;j++)
            current->cell[i][j] = BLANK;
}


/* Function isValid: returns 1 if move is valid, 0 otherwise */

int isValid ( State s, Operator operator ){

    if ( ( operator.row > 2 ) || ( operator.row < 0 ) ||
        ( operator.col > 2 ) || ( operator.col < 0 ) )
        return 0;

    if ( s.cell[operator.row][operator.col] == BLANK )
        return 1;
    else
        return 0;
}


/* Function makeMove: makes the indicated move.  Returns 1 if
   successful, 0 otherwise.
*/

int makeMove ( State *s, Operator operator, int who ){
    if ( ! isValid ( *s, operator ) )
        return 0;

    s->cell[operator.row][operator.col] = who;

    return 1;
}


/* Function isTerminal:  

       Returns 1 if the state is a terminal state (someone won)
       Returns 2 if the state is a terminal state (was a tie)
       Returns 0 otherwise.

   It checks if any row contains 3 non-BLANK entries of the same type.
   It checks if any col contains 3 non-BLANK entries of the same type.
   If any of the two diagonals contains 3 non-BLANK entries of the same
type.
*/

int isTerminal ( State s ){
    int line;
    int row, col;

    for ( line = 0; line < 3; line++ ){

        /* Check for a row completion */

        if ( ( s.cell[line][0] != BLANK ) &&
            ( s.cell[line][0] == s.cell[line][1] ) &&
            ( s.cell[line][1] == s.cell[line][2] ) )

            return 1;

        /* Check for a column completion */

        if ( ( s.cell[0][line] != BLANK ) &&
            ( s.cell[0][line] == s.cell[1][line] ) &&
            ( s.cell[1][line] == s.cell[2][line] ) )

            return 1;
    }

    /* Check for leading diagonal */

    if ( ( s.cell[0][0] != BLANK ) &&
        ( s.cell[0][0] == s.cell[1][1] ) &&
        ( s.cell[1][1] == s.cell[2][2] ) )

        return 1;

    /* Check for other diagonal */

    if ( ( s.cell[0][2] != BLANK ) &&
        ( s.cell[0][2] == s.cell[1][1] ) &&
        ( s.cell[1][1] == s.cell[2][0] ) )

        return 1;

    for ( row = 0 ; row < 3; row ++ )
        for ( col = 0 ; col < 3 ; col ++ )
            if ( s.cell[row][col] == BLANK )
                return 0;

    return 2;
}


/*  function print_state:  prints the status of the board */

void print_state ( State s ){
    int i, j, i1;

    for (i1=0; i1<13; i1++)
        printf("-");

    puts("");

    for (i=0; i<3; i++ ){
        for (j=0;j<3;j++)
            switch (s.cell[i][j]){
            case BLANK: 
                printf("|   "); 
                break;
            case USER:  
                printf("| X "); 
                break;
            case PROGRAM: 
                printf("| O ");
                break;
            }
        printf("|\n");
        for (i1=0; i1<13; i1++)
            printf("-");

        puts("");
    }
}


/* function eval:  provides information about the "goodness" of state
   for the program.  Is used by max and min function.
*/

int eval ( State s );

int max_depth=4;  /* how deep the search can go */

/* Function undo:  undoes the effect of the given operator */

void undo (State *s, Operator operator);

/* Function max:  Takes as input the current state and a depth measure
   d and returns the best Move by looking at all moves within the maximum 
   permitted search depth of max_depth.

   Compare this to the MAX_VALUE function in Russell and Norvig.

   Here is the algorithm for max (min is similar):

   1.  If we have reached the maximum possible depth or if the state is
terminal
          return the value of the state
   2.  For each possible row, column pair
       a.  If is valid to place a mark in the (row,column) cell
           A. make the move
           B. call min to find how good the move is
           C. If min says that it is better than the best move so far,
note it.
           D. If alpha is greater than beta cutoff, return it
   3.  Return the best move

*/

Move max ( State s, int d, int alpha, int beta ){
    Move m, bestmove;
    Operator operator;
    int i, j;

    if ( (d == max_depth) || ( isTerminal (s) )){
        m.value = eval (s);
        return m;
    }

    bestmove.value = alpha;

    for (i=0; i<3; i++)
        for (j=0; j<3; j++){
            operator.row = i;
            operator.col = j;
            if ( isValid (s, operator) ){
                makeMove ( &s, operator, PROGRAM );
                m = min ( s, d + 1, bestmove.value, beta );
                undo (&s, operator );
                if ( m.value > bestmove.value ){
                    bestmove.value=m.value;
                    bestmove.op.row = i;
                    bestmove.op.col = j;
                    if ( m.value > beta ){
                        bestmove.value = beta;
                        return bestmove;
                    }
                }
            }
        }

    return bestmove;
}


Move min ( State s, int d, int alpha, int beta ){
    Move m, bestmove;
    Operator operator;
    int i, j;


    if ( (d == max_depth) || ( isTerminal (s) )){
        m.value = eval (s);
        return m;
    }

    bestmove.value = beta;

    for (i=0; i<3; i++)
        for (j=0; j<3; j++){
            operator.row = i;
            operator.col = j;
            if ( isValid (s, operator) ){
                makeMove ( &s, operator, USER );
                m = max ( s, d + 1, alpha, bestmove.value );
                undo (&s, operator );
                if ( m.value < bestmove.value ){
                    bestmove.value=m.value;
                    bestmove.op.row = i;
                    bestmove.op.col = j;
                    if ( m.value < alpha ){
                        bestmove.value = alpha;
                        return bestmove;
                    }
                }
            }
        }

    return bestmove;
}



void undo ( State *s, Operator operator ){
    if ( ( operator.row <= 2 ) || ( operator.row >= 0 ) ||
        ( operator.col <= 2 ) || ( operator.col >= 0 ) )
        s->cell[operator.row][operator.col] = BLANK;
}



/*  How good is the state for the program? */

int eval ( State s ) {
    int line;

    for ( line = 0; line < 3; line++ ){

        /* Check for a row completion */

        if ( ( s.cell[line][0] == s.cell[line][1] ) &&
            ( s.cell[line][1] == s.cell[line][2] ) )
            if ( s.cell[line][0] == USER )
                return -10;
            else if ( s.cell[line][0] == PROGRAM )
                return 10;

        /* Check for a column completion */

        if ( ( s.cell[0][line] == s.cell[1][line] ) &&
            ( s.cell[1][line] == s.cell[2][line] ) )
            if ( s.cell[0][line] == USER )
                return -10;
            else if ( s.cell[0][line] == PROGRAM )
                return 10;
    }

    /* Check for leading diagonal */

    if ( ( s.cell[0][0] == s.cell[1][1] ) &&
        ( s.cell[1][1] == s.cell[2][2] ) )
        if ( s.cell[0][0] == USER )
            return -10;
        else if ( s.cell[0][0] == PROGRAM )
            return 10;


    /* Check for other diagonal */

    if ( ( s.cell[0][2] == s.cell[1][1] ) &&
        ( s.cell[1][1] == s.cell[2][0] ) )
        if ( s.cell[1][1] == USER )
            return -10;
        else if ( s.cell[1][1] == PROGRAM )
            return 10;

    if ( s.cell[1][1] == PROGRAM ) 
        return 5;

    if ( s.cell[1][1] == USER ) 
        return -5;


    for ( line = 0; line < 3; line++ ){

        if ( ( s.cell[line][0] == s.cell[line][1] ) )
            if ( s.cell[line][0] == USER )
                return -5;
            else if ( s.cell[line][0] == PROGRAM )
                return 5;

        if ( ( s.cell[0][line] == s.cell[1][line] ) )
            if ( s.cell[0][line] == USER )
                return -5;
            else if ( s.cell[0][line] == PROGRAM )
                return 5;

        if ( ( s.cell[line][1] == s.cell[line][2] ) )
            if ( s.cell[line][1] == USER )
                return -5;
            else if ( s.cell[line][1] == PROGRAM )
                return 5;

        if ( ( s.cell[1][line] == s.cell[2][line] ) )
            if ( s.cell[1][line] == USER )
                return -5;
            else if ( s.cell[1][line] == PROGRAM )
                return 5;

    }

    return 0;
}





