/****************************************************************************
 University of Toronto                              Computer Science CSC 209S
 St. George Campus                                              24 March 2000
                                  Assignment 3
 ****************************************************************************/

/**
 **        Last name: Anderson
 **       First name: Alexandr
 **     CDF username: a209ande
 ** Tutorial section: James Fung
 **   Course section: 0101
 **/

/*
 * FILE: ipttt.c
 *
 * Inter-Process Tic-Tac-Toe.
 * See the assignment handout for more detailed description of the program.
 */


#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#if defined(SEMAPHORE) || defined(SIGNAL)
#include <sys/ipc.h>
#include <sys/shm.h>
#endif
#ifdef SEMAPHORE
#include <sys/sem.h>
#include <sys/stat.h>
#endif
#ifdef SIGNAL
#include <signal.h>
#endif
#ifdef SOCKET
#include <sys/socket.h>
#include <sys/un.h>
#endif


#ifdef SOCKET

/* Socket version only definition. */
#define SOCKET_SNAME	"iptttServerSock"	/* Server socket name. */
#define SOCKET_CNAME	"iptttClientSock"	/* Client socket name. */
#define SOCKET_MAX_RETRY  	10	/* Maximum # of connection retries. */
#define SOCKET_RETRY_DELAY	1 	/* Delay between retries. (sec) */

#endif


/* Game board specifications. */
#define BOARD_WIDTH   	3	/* Width of the game board. */
#define BOARD_HEIGHT  	3	/* Height of the game board. */
#define WIN_LINE_LEN  	3	/* Length of a line to win the game. */
#define BOARD_FIGURE_1	'P'	/* What parent will write to the board. */
#define BOARD_FIGURE_2	'C'	/* What child will write to the board. */
#define BOARD_EMPTY_SP	' '	/* Indicator of an empty space on the board. */


/* Return values for function MakeMove(). */
#define MOVE_REG	0	/* See the documentations of function        */
#define MOVE_TIE	1	/* MakeMove() for more detailed description  */
#define MOVE_VIC	2	/* of each of these constants.               */
#define MOVE_FUL	4	/* ... */
#define MOVE_OCP	8
#define MOVE_INV	16


/* ------------------------------------------------------------------------- */


/*
 * STRUCTURE: Move
 *
 * A move in the game.
 */
#ifdef SEMAPHORE
typedef enum { AccessEmpty, AccessFullParent, AccessFullChild } AccessMode ;
#endif
typedef struct {
  char data ; /* character to write on the gameboard */
  int  x,y  ; /* location in which to write character */
#ifdef SEMAPHORE
  AccessMode mode ;
#endif
} Move ;


/*
 * STRUCTURE: Parms
 *
 * Version specific parameters needed for IPC.
 */
typedef struct {
#if defined(SEMAPHORE) || defined(SIGNAL)
  int shmid;    	/* Shared memory identifier. */
  Move *shmptr; 	/* Pointer to the shared memory segment. */
#endif
#ifdef SEMAPHORE
  int semID; 		/* Semaphore identifier. */
  int parent;		/* Boolean variable to tell child from parent. */
#endif
#ifdef SIGNAL
  int opponentpid;	/* Opponent's PID. */
#endif
#ifdef PIPE
  int fdRead[2]; 	/* Pipe from the opponent. */
  int fdWrite[2];	/* Pipe to the opponent. */
#endif
#ifdef SOCKET
  int orig_soc, soc;       	/* Socket descriptors. */
  struct sockaddr_un sname;	/* Server (parent) socket name. */
  struct sockaddr_un cname;	/* Client (child) socket name. */
#endif
} Parms;


/*
 * STRUCTURE: GameBoard
 *
 * A Tic-Tac-Toe game board.
 */
typedef struct {
  char **cell;     	/* Array of characters representing the game board. */
  int  emptySpaces;	/* Number of empty spaces on the board. */
} GameBoard;


/* ------------------------------------------------------------------------- */


#ifdef SIGNAL
/* Signal handling mechanism. */
int readyFlag;   	/* Indicator whether can do I/O on shared memory. */
void sigHandle( int code ) {	/* Signal handler. */
  readyFlag = 1;
}
#endif


#ifdef SEMAPHORE
/*
 * FUNCTION: safeSemOp
 *
 * Perfoms a set of semaphore operation.
 */
void safeSemOp( int semID, struct sembuf *sem, int nops ) {
  int result;
  /* Try again as long as the operation is interrupted by a signal. */
  do {
    result = semop( semID, sem, nops );
    if ( result == -1 && errno != EINTR ) {
      perror( "Semaphore operation failed" );
      semctl( semID, 0, IPC_RMID );
      exit(1);
    }
  } while ( result == -1 );
}

/*
 * FUNCTION: Wait
 *
 * Wait for the semaphore element.
 */
void Wait(int semID)
{
  static struct sembuf semWait[1] = { 0, -1, 0 };

  safeSemOp( semID, semWait, 1 );
}

/*
 * FUNCTION: Signal
 *
 * Release the semaphore element.
 */
void Signal(int semID)
{
  static struct sembuf semSignal[1] = { 0,  1, 0 } ;

  safeSemOp( semID, semSignal, 1 );
}
#endif


/* ------------------------------------------------------------------------- */


/* See below for documentation. */
void InitIPCpre( Parms *p );
void InitIPCpost( Parms *p, int parent );
void SendMove( Move *theMove, Parms *p );
void ReadMove( Move *theMove, Parms *p );
void CleanupIPC( Parms *p, int parent );


/* See below for documentation. */
void initBoard( GameBoard *board );
void cleanupBoard( GameBoard *board );
void printBoard( GameBoard *board );


/* See below for documentation. */
void GenerateMove( Move *theMove, GameBoard *board, int parent );
int MakeMove( Move *theMove, GameBoard *board );
int LastMove( Move *theMove, GameBoard *board );


/* ------------------------------------------------------------------------- */


/*
 * FUNCTION: main
 *
 * Main function.
 *
 * PARAMETERS:
 *    int argc		number of command-line arguments;
 *    char** argv	array of command-line arguments.
 *
 * Preconditions:
 *    none
 *
 * RETURN VALUE:
 *    int	exit status:
 *		0  = successful terminations;
 *		>0 = error.
 */
int main( int argc, char **argv ) {

  Parms myParms ;
  Move  myMove  ;
  int   pid     ;

  int i, j;

  GameBoard board;

  int vic = -1;  /* Indicates who wins the game: 0 is a tie, <0 is child, */
                 /* >0 is parent. Only parent needs it. */

  /* Initialize game board ... */
  initBoard( &board );

  InitIPCpre( &myParms );

  /* Create a child process. */
  if ( (pid = fork()) == -1 ) {
    /* Make sure a child process can be successfully created. */
    perror( "Fork failed" );
    CleanupIPC( &myParms, pid );
    exit(1);
  } else if ( pid == 0 ) {	/* child */
    InitIPCpost( &myParms, pid );

    /* Read and send moves until the game is not over. */
    while ( 1 ) {
      ReadMove( &myMove, &myParms );
      if ( LastMove( &myMove, &board ) ) break;

      GenerateMove( &myMove, &board, pid );

      /* Report the move. */
      printf( "C's move: x=%d, y=%d.\n", myMove.x+1, myMove.y+1 );

      SendMove( &myMove, &myParms );

      /* Check for the end of the game. */
      if ( LastMove( &myMove, &board ) ) break;
    }

    /* Print out the game board. */
    printf( "Child's game board is:\n" );
    printBoard( &board );
  } else {                	/* parent/producer */
    int status;

    InitIPCpost( &myParms, pid );

    /* Send and read moves until the game is not over. */
    do {
      int move;
      GenerateMove( &myMove, &board, pid );

      /* Report the move. */
      printf( "P's move: x=%d, y=%d.\n", myMove.x+1, myMove.y+1 );

      SendMove( &myMove, &myParms );

      /* Check for the end of the game. */
      move = MakeMove( &myMove, &board );
      if ( move == MOVE_TIE ) {
        vic = 0;
        break;
      }
      if ( move == MOVE_VIC ) {
        vic = 1;
        break;
      }

      ReadMove( &myMove, &myParms );
    } while ( !LastMove( &myMove, &board ) );

    /* Wait for the child to finish its work. */
    waitpid( pid, &status, 0 );

    /* Print out the game board. */
    printf( "Parent's game board is:\n" );
    printBoard( &board );
  }

  CleanupIPC( &myParms, pid );	/* both parent and child do this */

  cleanupBoard( &board );

  /* Let the parent announce the result of the game. */
  if ( pid ) {
    if ( vic > 0 )
      printf( "Parent wins the game!\n" );
    else if ( vic < 0 )
      printf( "Child wins the game!\n" );
    else
      printf( "A tie!\n" );
  }

  return 0;

}


/* ------------------------------------------------------------------------- */


/*
 * FUNCTION: InitIPCpre
 *
 * Used for any initialization of IPC mechanism which needs to be done before
 * fork() is executed.
 *
 * Parameters:
 *    Parms *p	pointer to structure representing parameters.
 *
 * Preconditions:
 *    "p" is not NULL and points to a valid "Parms" structure.
 *
 * Postconditions:
 *    The program terminates if any unrecoverable error occurs.
 */
void InitIPCpre( Parms *p ) {
#if defined(SEMAPHORE) || defined(SIGNAL)
  /* Allocate a segment of shared memory. */
  if ( ( p->shmid = shmget( IPC_PRIVATE, sizeof(Move), SHM_R|SHM_W ) ) < 0 ) {
    perror( "Cannot initialize shared memory buffer" );
    exit(1);
  }
  /* Attach a pointer to this segment of shared memory. */
  if ( ( p->shmptr = (Move*)shmat( p->shmid, (void*)0, 0 ) ) == (void*)-1 ) {
    perror( "Cannot attach shared memory buffer" );
    /* Try to remove it anyways before exiting. */
    shmctl( p->shmid, IPC_RMID, (struct shmid_ds*)0 );
    exit(1);
  }
#endif
#ifdef SEMAPHORE
  /* Get a semaphore. */
  if ( ( p->semID = semget( IPC_PRIVATE, 1, S_IRUSR | S_IWUSR ) ) <= 0 ) {
    perror( "Cannot create a semaphore" );
    exit(1);
  }
  /* Set the access mode on shmem to indicate that the buffer is empty. */
  p->shmptr->mode = AccessEmpty;
  Signal( p->semID );
#endif
#ifdef SIGNAL
  /* Install signal handler. */
  if ( sigset( SIGUSR1, sigHandle ) == SIG_ERR ) {
    perror( "Cannot install signal handler" );
    exit(1);
  }
  /* Set the "ready" flag. */
  readyFlag = 0;
#endif
#ifdef PIPE
  /* Create a pipe. */
  if ( pipe( p->fdRead ) || pipe( p->fdWrite ) ) {
    perror( NULL );
  }
#endif
#ifdef SOCKET
  /* Remove any previous sockets. */
  unlink( SOCKET_SNAME );
  unlink( SOCKET_CNAME );

  p->sname.sun_family = p->cname.sun_family = AF_UNIX;
  strcpy( p->sname.sun_path, SOCKET_SNAME );
  strcpy( p->cname.sun_path, SOCKET_CNAME );
#endif
}


/*
 * FUNCTION: InitIPCpost
 *
 * Used for any initialization of IPC mechanism which needs to be done after
 * fork() is executed.
 *
 * Parameters:
 *    Parms *p  	pointer to structure representing parameters;
 *    int parent	a Boolean parameter to specify whether the parent or
 *              	child is running it (>0=parent, 0=child).
 *
 * Preconditions:
 *    "p" is not NULL and points to a valid "Parms" structure.
 *    In versions that use shared memory (signal and semaphore version) parent
 *    should be either 0 to indicate that the child process is calling this
 *    function or process id of the child if the parent is making a call.
 *
 * Postconditions:
 *    The program terminates if any unrecoverable error occurs.
 */
void InitIPCpost( Parms *p, int parent ) {
#ifdef SEMAPHORE
  p->parent = parent;
#endif
#ifdef SIGNAL
  /* Find out the PID of the opponent (parent vs child). */
  p->opponentpid = parent ? parent : getppid();
#endif
#ifdef PIPE
  if ( !parent ) {
    /* Swap write and read pipes for the child. */
    int tmp[2];
    memcpy( tmp, p->fdRead, 2*sizeof(int) );
    memcpy( p->fdRead, p->fdWrite, 2*sizeof(int) );
    memcpy( p->fdWrite, tmp, 2*sizeof(int) );
  }
  /* Close the "opposite" ends of the pipes for both parent and child. */
  close( p->fdWrite[1] );
  close( p->fdRead[0] );
#endif
#ifdef SOCKET
  if ( parent ) {
    int len;
    /* Create an endpoint for socket communication. */
    if ( ( p->orig_soc = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 ) {
      perror( "Server: Cannot create the socket" );
      exit(1);
    }
    /* Bind a name to the socket. */
    strcpy( p->sname.sun_path, SOCKET_SNAME );
    if ( bind( p->orig_soc, (struct sockaddr*)&(p->sname), sizeof(p->sname) )
        == -1 ) {
      perror( "Server: Cannot bind the name to the socket" );
      exit(1);
    }
    /* Creating backlog for connections on the socket. */
    if ( listen( p->orig_soc, 1 ) != 0 ) {
      perror( "Server: Error creating backlog for connections" );
      CleanupIPC( p, parent );
      exit(1);
    }
    /* Accept a connection on the socket. */
    len = sizeof(p->cname);
    if ( ( p->soc = accept( p->orig_soc, (struct sockaddr*)&(p->cname),
        &len ) ) < 0 ) {
      perror( "Server: Cannot establish the connection" );
      CleanupIPC( p, parent );
      exit(1);
    }
  } else {	/* child */
    int retries = 0;
    /* Create an endpoint for socket communication. */
    if ( ( p->soc = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 ) {
      perror( "Client: Cannot create the socket" );
      exit(1);
    }
    /* Bind a name to the socket. */
    if ( bind( p->soc, (struct sockaddr*)&(p->cname), sizeof(p->cname) )
        == -1 ) {
      perror( "Client: Cannot bind the name to the socket" );
      exit(1);
    }
    /* Initiate a connection with the server (parent). */
    while ( connect( p->soc, (struct sockaddr*)&(p->sname), sizeof(p->sname) )
        < 0 ) {
      if ( ++retries >= SOCKET_MAX_RETRY ) {
        perror( "Client: Cannot connect" );
        CleanupIPC( p, parent );
        exit(1);
      }
      /* Wait up and keep trying. */
      sleep( SOCKET_RETRY_DELAY );
    }
  }
#endif
}


/*
 * FUNCTION: CleanupIPC
 *
 * Does the clean up for parent and child processes. Both child and parent
 * should call this function.
 *
 * Parameters:
 *    Parms *p  	pointer to structure representing parameters;
 *    int parent	a Boolean parameter to specify whether the parent or
 *              	child is running it (>0=parent, 0=child).
 *
 * Preconditions:
 *    "p" is not NULL and points to a valid "Parms" structure.
 *
 * Postconditions:
 *    The program terminates if any unrecoverable error occurs.
 */
void CleanupIPC( Parms *p, int parent ) {
#if defined(SEMAPHORE) || defined(SIGNAL)
  if ( parent ) {
    /* Set up the shared memory segment to be removed. */
    if ( shmctl( p->shmid, IPC_RMID, (struct shmid_ds*)0 ) < 0 )
      perror( "Warning: Failed to remove shared memory" );
  }
#endif
#ifdef SEMAPHORE
  if ( parent ) {
    /* Remove the semaphore. */
    if ( semctl( p->semID, 0, IPC_RMID ) == -1 )
      perror( "Warning: Unable to remove semaphore" );
  }
#endif
#ifdef PIPE
  /* Close the open ends of the read and write pipes. */
  close( p->fdWrite[1] );
  close( p->fdRead[0] );
#endif
#ifdef SOCKET
  /* Close and remove the sockets. */
  close( p->soc );
  if ( parent ) {
    close( p->orig_soc );
    unlink( SOCKET_SNAME );
  } else {
    unlink( SOCKET_CNAME );
  }
#endif
}


/* ------------------------------------------------------------------------- */


/*
 * FUNCTION: initBoard
 *
 * Creates and fills the game board with empty spaces.
 *
 * Parameters:
 *    GameBoard *board	pointer to a structure representing the game board.
 *
 * Preconditions:
 *    "board" points to a well-formed non-NULL GameBoard structure.
 */
void initBoard( GameBoard *board ) {
  int i, j;

  /* Allocate a 2D array of characters for the game board. */
  board->cell = (char**)malloc( BOARD_WIDTH * sizeof(char*) );
  if ( !board->cell ) {
    perror( "Failed to initialize the game board" );
    exit(1);
  }
  for ( i = 0; i < BOARD_WIDTH; i++ ) {
    /* Allocate memory for each column. */
    board->cell[i] = (char*)malloc( BOARD_HEIGHT * sizeof(char) );
    if ( !board->cell[i] ) {
      perror( "Failed to initialize the game board" );
      exit(1);
    }
  }

  /* Keep count of empty spaces on the board. */
  board->emptySpaces = BOARD_HEIGHT * BOARD_WIDTH;

  /* Fills the game board with empty spaces. */
  for ( i = 0; i < BOARD_HEIGHT; i++ )
    for ( j = 0; j < BOARD_WIDTH; j++ )
      board->cell[i][j] = BOARD_EMPTY_SP;

}


/*
 * FUNCTION: cleanupBoard
 *
 * Deallocates memory for the game board.
 *
 * Parameters:
 *    GameBoard *board	pointer to a structure representing the game board.
 *
 * Preconditions:
 *    "board" points to an instantiated game board structure in memory.
 */
void cleanupBoard( GameBoard *board ) {
  int i;
  /* Remove rows one by one. */
  for ( i = 0; i < BOARD_WIDTH; i++ )
    free( board->cell[i] );
  free( board->cell );
}


/*
 * FUNCTION: printBoard
 *
 * Prints the contents of the game board to standard output.
 *
 * Parameters:
 *    GameBoard *board	pointer to a structure representing the game board.
 *
 * Preconditions:
 *    "board" points to a well-formed non-NULL GameBoard structure.
 */
void printBoard( GameBoard *board ) {
  int i, j;

  /* Print row by row prepending each row with a white space character. */
  for ( i = 0; i < BOARD_HEIGHT; i++ ) {
    for ( j = 0; j < BOARD_WIDTH; j++ )
      printf( " %c", board->cell[i][j] );
    putchar( '\n' );
  }
}


/* ------------------------------------------------------------------------- */


/*
 * FUNCTION: SendMove
 *
 * Sends a move to the opponent.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    Parms *p  	pointer to structure representing parameters;
 *
 * Preconditions:
 *    "theMove" points to a Move structure in memory.
 *    "p" is not NULL and points to a valid "Parms" structure.
 *
 * Postconditions:
 *    The function returns if and only if a move has been successfully sent.
 */
void SendMove( Move *theMove, Parms *p ) {
  int bytesSent=0;
#ifdef SEMAPHORE
  /* Wait until the shared memory buffer is empty. */
  while ( 1 ) {
    /* Wait for the semaphore to grant access to shared memory. */
    Wait( p->semID );
    /* Check the buffer if it is empty. */
    if ( p->shmptr->mode == AccessEmpty )
      break;
    /* Release the semaphore. */
    Signal( p->semID );
    usleep( 100 );
  }

  /* Copy the move to the shared memory locations. */
  memcpy( p->shmptr, theMove, bytesSent = sizeof(Move) );
  /* Release the semaphore. */
  Signal( p->semID );
#endif
#ifdef SIGNAL
  /* Copy the move to the shared memory locations. */
  memcpy( p->shmptr, theMove, bytesSent = sizeof(Move) );
  readyFlag = 0;
  /* Send a signal to the opponent saying that a move has been sent. */
  if ( kill( p->opponentpid, SIGUSR1 ) < 0 ) {
    perror( "Failed to send a signal" );
    exit(1);
  }
#endif
#ifdef PIPE
  /* Write a move to the pipe. */
  bytesSent = write( p->fdWrite[0], theMove, sizeof(Move) );
#endif
#ifdef SOCKET
  /* Write a move to the socket. */
  bytesSent = write( p->soc, theMove, sizeof(Move) );
#endif
  /* Make sure the move has been successfully read. */
  if ( bytesSent != sizeof(Move) ) {
    perror( "Error occurred during IPC" );
    exit(1);
  }
}


/*
 * FUNCTION: ReadMove
 *
 * Reads a move from the opponent.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    Parms *p  	pointer to structure representing parameters;
 *
 * Preconditions:
 *    "theMove" points to a Move structure in memory.
 *    "p" is not NULL and points to a valid "Parms" structure.
 *
 * Postconditions:
 *    The function returns if and only if a move has been successfully read.
 */
void ReadMove( Move *theMove, Parms *p ) {
  int bytesRead = 0;
#ifdef SEMAPHORE
  /* Wait for the shared memory buffer to be full. */
  while ( 1 ) {
    /* Wait for the safe access to shared memory. */
    Wait( p->semID );
    /* Check the buffer. */
    if ( p->shmptr->mode == (p->parent ? AccessFullParent : AccessFullChild) )
      break;
    /* Release the semaphore. */
    Signal( p->semID );
    usleep( 100 );
  }
  /* Copy the move from the shared memory location. */
  memcpy( theMove, p->shmptr, bytesRead = sizeof(Move) );

  /* Mark the shared memory buffer as empty. */
  p->shmptr->mode = AccessEmpty;
  /* Release the semaphore. */
  Signal( p->semID );
#endif
#ifdef SIGNAL
  /* Pause until the next signal from the opposite player arrives. */
  while ( !readyFlag )
    usleep(100);
  /* Copy the move from the shared memory location. */
  memcpy( theMove, p->shmptr, bytesRead = sizeof(Move) );
  readyFlag = 0;
#endif
#ifdef PIPE
  /* Read a move from the pipe. */
  bytesRead = read( p->fdRead[1], theMove, sizeof(Move) );
#endif
#ifdef SOCKET
  /* Read a move from the socket. */
  bytesRead = read( p->soc, theMove, sizeof(Move) );
#endif
  /* Make sure the move has been successfully read. */
  if ( bytesRead != sizeof(Move) ) {
    perror( "Error occurred during IPC" );
    exit(1);
  }
}


/*
 * FUNCTION: GenerateMove
 *
 * Generates a move to the Tic-Tac-Toe game board.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    GameBoard *board	the Tic-Tac-Toe game board.
 *    int parent	a Boolean parameter to specify whether the parent or
 *              	child is running it (>0=parent, 0=child).
 *
 * Preconditions:
 *    "board" points to a well-formed GameBoard structure in memory.
 *    "theMove" points to a Move structure in memory.
 *    The game board is not completely full.
 *
 * Postconditions:
 *    The new move does not overwrite one of the previous moves.
 */
void GenerateMove( Move *theMove, GameBoard *board, int parent ) {
  int i, j, n;

  /* Make sure the game board has empty spaces. */
  if ( !board->emptySpaces ) return;

  /* Set the seed for random number generation. */
  srand( (int)time(0) );

  /* Randomly take any empty space on the game board. */
  n = rand() % board->emptySpaces;

  /* Find the location (coordinates) of that empty space. */
  for ( i = 0; i < BOARD_HEIGHT; i++ )
    for ( j = 0; j < BOARD_WIDTH; j++ )
      if ( board->cell[i][j] == BOARD_EMPTY_SP && !n-- ) {
        theMove->x = j;
        theMove->y = i;
        theMove->data = parent ? BOARD_FIGURE_1 : BOARD_FIGURE_2;
#ifdef SEMAPHORE
        theMove->mode = parent ? AccessFullChild : AccessFullParent;
#endif
        return;
      }
}


/*
 * FUNCTION: countSame
 *
 * Returns the number of consecutive cells in the game board in the direction
 * given by (dx,dy) that have the same value as that of the move including the
 * move.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    GameBoard *board	the Tic-Tac-Toe game board;
 *    int dx            horizontal step;
 *    int dy          	vertical step.
 *
 * Preconditions:
 *    "board" points to a well-formed GameBoard structure in memory.
 *    "theMove" is a valid move that does not go beyond the boundaries of the
 *    game board.
 *    dx^2 + dy^2 != 0.
 *
 * RETURN VALUE:
 *    int	# of elements that form a line in given direction.
 */
int countSame( Move *theMove, GameBoard *board, int dx, int dy ) {
  int i, j, count = 1;

  /* Find out the location of the initial cell on the game board. */
  i = theMove->x + dx;
  j = theMove->y + dy;

  while ( i >= 0 && i < BOARD_WIDTH && j >= 0 && j < BOARD_HEIGHT ) {
    /* Compare the cell to where the move has been made. */
    if ( board->cell[j][i] == theMove->data )
      count++;
    else
      break;
    /* Go to the next cell. */
    i += dx;
    j += dy;
  }

  return count;
}


/*
 * FUNCTION: MakeMove
 *
 * Makes a move on a Tic-Tac-Toe game board.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    GameBoard *board	the Tic-Tac-Toe game board.
 *
 * Preconditions:
 *    "board" points to a well-formed GameBoard structure in memory.
 *    "theMove" is a valid move that does not go beyond the boundaries of the
 *    game board.
 *
 * RETURN VALUE:
 *    int	MOVE_REG	OK; regular successful move.
 *       	MOVE_TIE	OK; tie occurred.
 *       	MOVE_VIC	OK; the victory of the player.
 *       	MOVE_FUL	Failure; the game board is full.
 *       	MOVE_OCP	Failure; the cell is occupied.
 *       	MOVE_INV	Failure; invalid move.
 *
 * Postconditions:
 *    If the value of MOVE_FUL or MOVE_OCP returned, the move has not been
 *    done. 
 */
int MakeMove( Move *theMove, GameBoard *board ) {
  /* Make sure the move is valid. */
  if ( theMove->x < 0 || theMove->x >= BOARD_WIDTH ||
       theMove->y < 0 || theMove->y >= BOARD_HEIGHT ) {
    return MOVE_INV;
  }

  /* Make sure the game board is not completely full. */
  if ( !board->emptySpaces )
    return MOVE_FUL;

  /* Make sure the cell is not occupied with another move. */
  if ( board->cell[theMove->y][theMove->x] != BOARD_EMPTY_SP )
    return MOVE_OCP;

  /* Make the move. */
  board->cell[theMove->y][theMove->x] = theMove->data;
  if ( theMove->data != BOARD_EMPTY_SP )
    board->emptySpaces--;

  /* Check whether the move makes a complete row. */
  if ( countSame(theMove,board,-1,0) + countSame(theMove,board,1,0) - 1
         >= WIN_LINE_LEN ||
       /* Check whether the move makes a complete column. */
       countSame(theMove,board,0,-1) + countSame(theMove,board,0,1) - 1
         >= WIN_LINE_LEN ||
       /* Check whether the move makes a complete diagonal. */
       countSame(theMove,board,-1,-1) + countSame(theMove,board,1,1) - 1
         >= WIN_LINE_LEN ||
       countSame(theMove,board,1,-1) + countSame(theMove,board,-1,1) - 1
         >= WIN_LINE_LEN ) {
    return MOVE_VIC;
  }

  /* Check whether the game board is full. */
  if ( !board->emptySpaces ) return MOVE_TIE;

  return MOVE_REG;
}


/*
 * FUNCTION: LastMove
 *
 * Makes a move on the game board and checks whether the move ends the game.
 *
 * Parameters:
 *    Move *theMove   	the move;
 *    GameBoard *board	the Tic-Tac-Toe game board.
 *
 * Preconditions:
 *    "board" points to a well-formed GameBoard structure in memory.
 *    "theMove" is a valid move that does not go beyond the boundaries of the
 *    game board.
 *
 * RETURN VALUE:
 *    int	0: the move did not end the game;
 *       	1: the move was the last move for the game.
 */
int LastMove( Move *theMove, GameBoard *board ) {
  /* Read the return status of function MakeMove(). */
  int status = MakeMove( theMove, board );

  return status == MOVE_TIE || status == MOVE_VIC;
}

