// ticTacToe.cpp
// Plays a 2-user tic tac toe game.

#include "textUtility2.h"
#include <fstream>
#include <cassert>

using namespace std;

typedef char TicTacToeBoard[3][3];

// ----------------------------
// ***** Output functions *****
// ----------------------------
void printBoard(const TicTacToeBoard board,
                ostream& out);
void announceWinner(char winner, ostream& out);
void displayPlayingScreen(const TicTacToeBoard board,
                          bool turnX,
                          bool nag);
void displayFinalScreen(const TicTacToeBoard board,
                        char winner);

// ---------------------------
// ***** Input functions *****
// ---------------------------
bool readMove(int& row, int& column);
void readMoveInRange(int& row,
                     int& column,
                     const TicTacToeBoard board,
                     bool turnX);
void readMoveNotYetTaken(int& row,
                         int& column,
                         const TicTacToeBoard board,
                         bool turnX);

// ---------------------------------
// ***** Computation functions *****
// ---------------------------------
bool isPositionTaken(const TicTacToeBoard board,
                     int row,
                     int column);
bool winnerYet(const TicTacToeBoard board,
               char& winner);
bool isValidBoard(const TicTacToeBoard board);
bool isGameOver(const TicTacToeBoard board,
                char& winner);


/* 
 * Plays a 2-user Tic Tac Toe game.
 * Interacts with the user via the
 * terminal window, and prints a log
 * of all moves to file ticTacToe.txt.
 *
 * Global variables:
 *    cin - an object of class istream,
 *           declared in <iostream>
 *    cout - an object of class ostream,
 *           declared in <iostream>
 */
int main()
{
   ofstream logFile;
   logFile.open("ticTacToe.txt");
   logFile << "Beginning Tic Tac Toe game."
               << endl << endl;

   TicTacToeBoard board = {
                             { ' ', ' ', ' ' },
                             { ' ', ' ', ' ' },
                             { ' ', ' ', ' ' }
                          };

   char winner;  // will be set to 'X' or 'O' to
                 // indicate winner, or ' ' if
                 // there is no winner.

   bool turnX = true;
   do  {
      // Display board and ask user to select move:
      displayPlayingScreen(board, turnX, false);

      // Obtain, from the user, a valid selection
      // of a move to a position not yet taken:
      int row;
      int column;
      readMoveNotYetTaken(row, column, board, turnX);

      // Make the move selected by the user:
      board[row][column] = (turnX ? 'X' : 'O');

      // Print board with current move to text file:
      printBoard(board, logFile);
      logFile << endl;

      // It will now be the other player's turn:
      turnX = ! turnX;

      // Check whether game is over.
      // If so, determine winner, if any:
   }  while ( !isGameOver(board, winner));

   // Display board with announcement of winner
   // and without prompt:
   displayFinalScreen(board, winner);

   // Conclude log file:
   logFile << "End of Tic Tac Toe game." << endl;
   announceWinner(winner, logFile);

   return 0;
}  // function main


/*
 * void printBoard(const TicTacToeBoard board,
 *                 ostream& out)
 *
 * Prints a tic tac toe board to the specified
 * output stream, taking 11 lines of text.
 *
 * Parameters:
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'O', or ' '.
 *    out - a reference to an output stream.
 *       Precondition:  the state of out is true.
 *       Postcondition:  the state of out is still true.
 */
void printBoard(const TicTacToeBoard board,
                ostream& out)
{
   out << "       |       |       " << endl;
   out << "   " << board[0][0] << "   |   "
                << board[0][1] << "   |   " 
                << board[0][2] << endl;
   out << "       |       |       " << endl;
   out << "-------|-------|-------" << endl;
   out << "       |       |       " << endl;
   out << "   " << board[1][0] << "   |   "
                << board[1][1] << "   |   " 
                << board[1][2] << endl;
   out << "       |       |       " << endl;
   out << "-------|-------|-------" << endl;
   out << "       |       |       " << endl;
   out << "   " << board[2][0] << "   |   "
                << board[2][1] << "   |   " 
                << board[2][2] << endl;
   out << "       |       |       " << endl;
}  // function printBoard


/*
 * void announceWinner(char winner, ostream& out)
 *
 * Prints a one-sentence announcement of the
 * winner, if any, or an announcement that
 * there is no winner.
 *
 * Parameters:
 *    winner - an indication of who won:
 *       'X', if the winner is X.
 *       'O', if the winner is O.
 *       A blank space if there is no winner.
 *    out - a reference to an output stream.
 *       Precondition:  the state of out is true.
 *       Postcondition:  the state of out is still true.
 */
void announceWinner(char winner, ostream& out)
{
   if ( (winner == 'X') || (winner == 'O') )
      out << "The winner is "
             << winner << "." << endl;
   else
      out << "Neither player won." << endl;
}  // function announceWinner


/*
 * void displayPlayingScreen(const TicTacToeBoard board,
 *                           bool turnX,
 *                           bool nag)
 *
 * Displays current game board and prompts
 * user to enter a move.
 *
 * Parameters:
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'O', or ' '.
 *    turnX - true if it is now X's turn,
 *            false if it is now O's turn.
 *    nag - true if current player is being
 *            prompted again in response to an
 *            invalid entry.
 *          false if current player is being
 *            prompted the first time within
 *            a given turn.
 *
 * Global variable:
 *    cout - an object of class ostream,
 *           declared in <iostream>
 *
 *
 */
void displayPlayingScreen(const TicTacToeBoard board,
                          bool turnX,
                          bool nag)
{
   if ( !nag )
      cout << endl << "The game so far:" << endl;
   cout << endl;
   printBoard(board, cout);
   const char nextPlayer = (turnX ? 'X' : 'O');
   cout << endl << "It is " << (nag?"still":"now")
                << " " << nextPlayer
                << "\'s turn.  " << nextPlayer <<
                ", select your move:" << endl << endl;
   cout << " 1 | 2 | 3 " << endl;
   cout << "---|---|---" << endl;
   cout << " 4 | 5 | 6 " << endl;
   cout << "---|---|---" << endl;
   cout << " 7 | 8 | 9 " << endl;
   cout << endl << "Enter your selection "
                << "(1, 2, 3, 4, 5, 6, 7, 8, 9):>";
}  // function displayPlayingScreen


/* 
 * void displayFinalScreen(const TicTacToeBoard board,
 *                         char winner)
 *
 * Displays the board and an announcement of
 * the winner.  This function should be called
 * when the game is over.
 *
 * Parameters:
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    winner - 'X', if X is the winner.
 *             'O', if O is the winner.
 *             ' ' otherwise.
 *
 * Global variable:
 *    cout - an object of class ostream,
 *           declared in <iostream>
 *
 *
 */
void displayFinalScreen(const TicTacToeBoard board,
                        char winner)
{
   cout << endl;
   announceWinner(winner, cout);
   cout << endl;
   printBoard(board, cout);
}  // function displayFinalScreen


/*
 * bool readMove(int& row, int& column)
 *
 * Prompts user to select a move, and reads
 * move.  The user should enter a digit in
 * the range [1, 9], which this function
 * then interprets as designating a row
 * and column.
 *
 * Parameters:
 *    row - row of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  row represents the
 *          selected row, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *    column - column of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  column represents the
 *          selected column, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *
 * Global variable:
 *    cin - an object of class istream,
 *           declared in <iostream>
 *       Precondition:  The state of cin is true.
 *       Postcondition:  The state of cin is
 *           still true (because cin has been
 *           used to read characters only),
 *           and a line of text has been
 *           completely read.
 *
 * Returns:
 *    True if user typed a character in range
 *    ['1','9'], false otherwise.
 */
bool readMove(int& row, int& column)
{
   // Read a character:
   char x;
   cin.get(x);

   // Determine whether x is in the
   // appropriate range for a move,
   // i.e. whether it represents a digit
   // between 1 and 9, inclusive:
   const bool valid = ( x >= '1' && x <= '9' );

   // If x is within range, convert it to
   // the represented row and column:
   if ( valid )  {
      int move = toDigitValue(x) - 1;
      row = move / 9;
      column = move % 9;
   }  // if

   // Skip to end of line:
   while ( x != '\n' )    // works on Unix and Windows
      cin.get(x);

   // Assertion:  If we have reached this
   // point, valid is true if, and only if,
   // the first character read represents an
   // integer in the range '1' to '9'.

   return valid;
}  // function readMove


/* 
 * void readMoveInRange(int& row,
 *                      int& column,
 *                      const TicTacToeBoard board,
 *                      bool turnX)
 *
 * Prompts user to enter a move, and reads
 * the move.  If the user's response is illegal
 * in the sense of being out of range, the user
 * is prompted repeatedly until a valid
 * response is obtained.
 *
 * Parameters:
 *    row - row of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  row represents the
 *          selected row, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *    column - column of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  column represents the
 *          selected column, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    turnX - true if it is now X's turn.
 *            false if it is now O's turn.
 *
 * Global variables:
 *    cin - an object of class istream,
 *           declared in <iostream>
 *       Precondition:  The state of cin is true.
 *       Postcondition:  The state of cin is
 *           still true, and a line of text
 *           has been completely read.
 *    cout - an object of class ostream,
 *           declared in <iostream>
 */
void readMoveInRange(int& row,
                     int& column,
                     const TicTacToeBoard board,
                     bool turnX)
{
   while ( ! readMove(row, column) )  {      // uses cin
      cout << endl << "Invalid entry.  You must enter "
             << "an integer in the range [1, 9]." << endl;
      displayPlayingScreen(board, turnX, true);   // uses cout
   }  // while
}  // function readMoveInRange


/* 
 * void readMoveNotYetTaken(int& row,
 *                          int& column,
 *                          const TicTacToeBoard board,
 *                          bool turnX)
 *
 * Prompts user to enter a move, and reads
 * the move.  If the user's response is illegal
 * in the sense of either being out of range
 * or attempting a move already taken, the user
 * is prompted repeatedly until a valid
 * response is obtained.
 *
 * Parameters:
 *    row - row of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  row represents the
 *          selected row, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *    column - column of the user's selected move.
 *       Precondition:  none.
 *       Postcondition:  column represents the
 *          selected column, in range [0, 2],
 *          if user typed a valid selection.
 *          Unchanged otherwise.
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    turnX - true if it is now X's turn.
 *            false if it is now O's turn.
 *
 * Global variables:
 *    cin - an object of class istream,
 *           declared in <iostream>
 *       Precondition:  The state of cin is true.
 *       Postcondition:  The state of cin is
 *           still true, and a line of text
 *           has been completely read.
 *    cout - an object of class ostream,
 *           declared in <iostream>
 */
void readMoveNotYetTaken(int& row,
                         int& column,
                         const TicTacToeBoard board,
                         bool turnX)
{
   while (true)  {
      readMoveInRange(row, column, board, turnX);   // uses cin
                                                    // and cout
      if ( ! isPositionTaken(board, row, column) )
         return;
      cout << endl << "Invalid entry.  You entered a "
             << "position already taken by "
             << board[row][column] << " ." << endl;
      displayPlayingScreen(board, turnX, true);     // uses cout
   }  // while
}  // function readMoveNotYetTaken


/* 
 * bool isPositionTaken(const TicTacToeBoard board,
 *                      int row,
 *                      int column)
 *
 * Determines whether a specified position
 * on the board is already occupied by
 * either X or O.
 *
 * Parameters:
 *    board - a 3 x 3 array of characters
 *            representing the board.  It is
 *            assumed that the characters are all
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    row - row of a position on board,
 *            assumed to be in range [0, 2].
 *    column - column of a position on board,
 *            assumed to be in range [0, 2].
 *
 * Returns:
 *    true if the position indicated by row and
 *       column is already taken by X or O.
 *    false otherwise.
 */
bool isPositionTaken(const TicTacToeBoard board,
                     int row,
                     int column)
{
   return ( (board[row][column] == 'X')
            || (board[row][column] == 'O') );
}  // function isPositionTaken


/* 
 * bool winnerYet( const TicTacToeBoard board,
 *                 char& winner )
 *
 * Determines which player (if any) has won.
 * Checks every column and row, plus the
 * diagonals, to see if they contain three
 * matching symbols (except ' ', which can not win).
 *
 * Parameters:
 *    board - a 3 x 3 array of characters representing
 *            the board.  The characters must all be
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    winner - an indication of who won.
 *       Precondition: none.
 *       Postcondition:  winner is one of the following:
 *          'X' or 'x', if the winner is X.
 *          'O' or 'o', if the winner is O.
 *          A blank space if there is no winner.
 *
 * Global variable:
 *    cout - an object of class ostream,
 *           declared in <iostream>
 *
 * Returns true if there is a winner yet,
 *         false otherwise.
 */
bool winnerYet( const TicTacToeBoard board,
                char& winner )
{
   assert(isValidBoard(board));  // for debugging

   // Loop through 0, 1, 2.  Simultaneously check the
   // corresponding row and column.  After this loop is
   // complete we will know if any player has gotten
   // three in a row horizontally or vertically.

   for ( int i = 0 ; i < 3 ; i++ ) {
      // Case 1: Search row i:
      if ( (board[i][0] == board[i][1])
                  && (board[i][1] == board[i][2])
                  && (board[i][0] != ' ')  )
      {
         winner = board[i][0];
         return true;
      }  // if

      // Case 2: Search column i:
      if ( (board[0][i] == board[1][i])
                  && (board[1][i] == board[2][i])
                  && (board[0][i] != ' ') )
      {
         winner = board[0][i];
         return true;
      }  // if
   } // for i

   // Case 3: Search top-left-to-bottom-right diagonal:
   // There is only one diagonal, so no need for a loop here.
   if ( (board[0][0] == board[1][1])
               && (board[1][1] == board[2][2])
               && (board[1][1] != ' ') )
   {
      winner = board[1][1];
      return true;
   }  // if

   // Case 4: Search top-right-to-bottom-left diaginal:
   if ( (board[0][2] == board[1][1])
               && (board[1][1] == board[2][0])
               && (board[1][1] != ' ') )
   {
      winner = board[1][1];
      return true;
   }  // if

   // If we reach this point, obviously nobody has won.
   // Return false, meaning no player
   // has successfully gotten 3 in a row.
   winner = ' ';
   return false;
} // function winnerYet


/* 
 * bool isValidBoard(const TicTacToeBoard board)
 *
 * Determines whether all the characters
 * on the board are appropriate to a
 * Tic Tac Toe game.  (This function needed
 * for debugging purposes only.)
 *
 * Parameter:
 *    board - a 3 x 3 array of characters
 *            representing the board.
 *
 * Returns true if the characters are all
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *         false otherwise.
 */
bool isValidBoard(const TicTacToeBoard board)
{
   for ( int i = 0; i < 3; i++ )
      for ( int j = 0; j < 3; j++ )
         if ( (board[i][j] != 'x') && (board[i][j] != 'X')
                && (board[i][j] != 'o') && (board[i][j] != 'O')
                && (board[i][j] != ' ') && (board[i][j] != '_') )
            return false;
   return true;
}  // function isValidBoard


/* 
 * bool isGameOver(const TicTacToeBoard board,
 *                 char& winner )
 *
 * Determines whether the game is over.
 * The game is over if either (1) a player
 * has won or (2) all positions on the
 * board are filled with X's and O's.
 *
 * Parameters:
 *    board - a 3 x 3 array of characters representing
 *            the board.  The characters must all be
 *            'X', 'x', 'O', 'o', ' ', or '_'.
 *    winner - an indication of who won.
 *       Precondition: none.
 *       Postcondition:  winner is one of the following:
 *          'X' or 'x', if the winner is X.
 *          'O' or 'o', if the winner is O.
 *          A blank space if there is no winner.
 *
 * Global variable:
 *    cout - an object of class ostream,
 *           declared in <iostream>
 *
 * @return true if the game is over,
 *         false otherwise.
 */
bool isGameOver(const TicTacToeBoard board,
                char& winner )
{
   if ( winnerYet(board, winner) )     // uses cout
      return true;

   winner = ' ';
   for ( int i = 0; i < 3; i++ )
      for ( int j = 0; j < 3; j++ )
         if ( (board[i][j] != 'X') && (board[i][j] != 'O') )
            return false;
   return true;
}  // function isGameOver