Wednesday, March 1, 2017

Programming Challenge 7.18 - Tic-Tac-Toe Game

Example file: instructions.txt

/* Tic-Tac-Toe Game - This program allows two players to play a game of
   tic-tac-toe. It uses a two dimensional char array with three rows and
   three columns as the game board. Each element of the array is initialized
   with an asterisk (*).

      * Displays the contents of the board array.
      * Allows player 1 to select a location on the board for an X. The
        program asks the user to enter the row and column number.
      * Allows player 2 to select a location on the board for an O. The
        program asks the user to enter the row and column number.
      * Determines whether a player has isWinner, or a tie has occured. If a
        player has isWinner, the program declares that player the winner and
        end. If a tie has occured, the program says so and end.

   Player 1 wins when there are three Xs in a row on the game board. The
   Xs can appear in a row, in a column, or diagonally across the board.
   A tie occurs when all of the locations on the board are full, but there
   is no winner. */

#include "Utility.h"

/* Global Constants: Position x, Position y, Maximum number of turns,
                     Number of players, Filler */
const int POS_X = 3;
const int POS_Y = 3;
const int MAX_TURNS = 9;
const int NUM_PLAYERS = 2;
const char FILLER = '*';

/* Typedef alias for gameboard array */
typedef char gameBoard[POS_X][POS_Y];

/* Function Prototypes: Main menu, Display menu, Display intro,
   Display logo, Get player info (name and play tile), Initialize
   gameboard, Get move, Check move (validate the moves), Check
   win, Populate highscores, Display winner */
int mainMenu();
void displayMenu();
void displayInstructions();
void displayLogo();
void getPlayerInfo(string &, char &, string &, char &);
void initGameboard(gameBoard board);
void displayGameboard(gameBoard board);
void getMove(gameBoard board, string, string, int &, int &,
             char, char, int);
bool checkMove(gameBoard board, int, int);
bool checkWin(const gameBoard board, int, int);
void popHighscores(string [], int [], string, string, int);
void displayHighscores(const string [], const int []);
void displayWinner(string, string, bool, int);

int main()
{
   mainMenu();

   pauseSystem();

   return 0;
}

/* **********************************************************
   Definition: displayMenu  

   This function displays the menu options. On first start of
   the game a logo is also displayed when this function is
   called.
   ********************************************************** */

void displayMenu()
{
   /* Variable: First start initialized to true (If the game is run
                for the first time, the logo is displayed once, and
                firstStart gets false */
   static bool firstStart = true;

   if (firstStart == true)
   {
      displayLogo();
   }

   cout << "\n\t\tTIC-TAC-TOE GAME\n\n"
        << "\t\t[1.] INSTRUCTIONS\n"
        << "\t\t[2.] SETUP\n"
        << "\t\t[3.] START GAME\n"
        << "\t\t[4.] HIGHSCORE\n"
        << "\t\t[5.] QUIT\n\n"
        << "\t\tCHOOSE: ";

   firstStart = false;
}

/* **********************************************************
   Definition: mainMenu

   This function provides a basic menu that lets the players
   select between the following items:

      * Introduction - Gives a basic overview of the game
      * Setup - Asks the players for their names and the
                play tiles they wish to play with
      * Start game
      * Highscores
      * Quit
   ********************************************************** */

int mainMenu()
{
   /* board is the alias for the gameboard array */
   gameBoard board = { };

   /* Constants: Introduction, Setup, Start game, Highscore, Quit */
   const int INSTRUCTIONS = 1,
             SETUP = 2,
             START_GAME = 3,
             HIGHSCORES = 4,
             QUIT = 5;

      /* Variables: Menu item, Is win, Index X, Index Y, Tick (counter),
                    Name player one, Name player two, Play tile player one,
                    Play tile player two, Player names, Highscores */
   int menuItem = 0;
   int idxX = 0, idxY = 0;

   bool isWin = false;

   static int tick = 0;

   static string nameP1 = " ",
                 nameP2 = " ";

   static char tileP1 = ' ',
               tileP2 = ' ';

   static string playerNames[NUM_PLAYERS] = { " ", " " };
   static int highScores[NUM_PLAYERS] = { };

   do
   {
      displayMenu();
      cin >> menuItem;

      while (menuItem < 1 || menuItem > 5)
      {
         displayMenu();
      }

      switch (menuItem)
      {
         case 1:
         displayInstructions();
         break;

         case 2:   
         getPlayerInfo(nameP1, tileP1, nameP2, tileP2);
         break;

         case 3:
         {
            /* Checks whether the players have completed the initial
               setup. If so, the gameloop is entered. If they didn't,
               the getPlayerInfo function is called. */
            if (nameP1 != " " || nameP2 != " " ||
                tileP1 != ' ' || tileP2 != ' ')
            {
               /* Initialize the gameboard */
               initGameboard(board);

               /* Reset tick to 0 after it has reached MAX_TURNS, or after
                  each win by either player */
               tick = 0;

               /* The gameloop runs until either a win or a draw has
                  been reached. */
               do
               {
                  tick += 1;

                  displayGameboard(board);
                  getMove(board, nameP1, nameP2, idxX, idxY,
                          tileP1, tileP2, tick);
                  isWin = checkWin(board, idxX, idxY);
               } while (isWin == false && tick != MAX_TURNS);

               if (isWin == true)
               {
                  displayWinner(nameP1, nameP2, isWin, tick);
                  popHighscores(playerNames, highScores, nameP1,
                                nameP2, tick);
                  displayGameboard(board);
               }

               else if (isWin == false)
               {
                  displayWinner(nameP1, nameP2, isWin, tick);
                  displayGameboard(board);
               }
            }
            else
            {
               cout << "\n\t\tPlease complete the setup before "
                    << "you start the game!\n";
               getPlayerInfo(nameP1, tileP1, nameP2, tileP2);
            }
         }
         break;

         case 4:
            displayHighscores(playerNames, highScores);
         break;

         case 5:
            cin.clear();
            cout << "\n\n\t\t{~REMEMBER: THE ONLY WINNING MOVE IS NOT TO PLAY!~}\n\n"
                 << "\t\t\t\tSHUTTING DOWN GAME ...\n\n";
            break;
      }
   } while (menuItem != QUIT);

   return 0;
}


/* **********************************************************
   Definition: displayLogo

   Displays the game logo.
   ********************************************************** */

void displayLogo()
{
   cout << "\n\n\t\t*****" << "\t*****" << "\t*****\n"
        << "\t\t * *"      << "\t * * " << "\t*\n"
        << "\t\t  *"       << "\t  * "  << "\t*\n"
        << "\t\t  *"       << "\t * *"  << "\t*\n"
        << "\t\t  *"       << "\t*****" << "\t*****\n\n"

        << "\t\t\t\t*****" << "\t*****" << "\t*****\n"
        << "\t\t\t\t * *"   << "\t*   *" << "\t*\n"
        << "\t\t\t\t  *"   << "\t* * *" << "\t*\n"
        << "\t\t\t\t  *"   << "\t*   *" << "\t*\n"
        << "\t\t\t\t  *"   << "\t*   *" << "\t*****\n\n"

        << "\t\t\t\t\t\t*****" << "\t*****" << "\t*****\n"
        << "\t\t\t\t\t\t * * " << "\t* * *" << "\t*\n"
        << "\t\t\t\t\t\t  *"   << "\t*   *" << "\t* *\n"
        << "\t\t\t\t\t\t  *"   << "\t* * *" << "\t*\n"
        << "\t\t\t\t\t\t  *"   << "\t*****" << "\t*****\n\n";
}

/* **********************************************************
   Definition: displayInstructions

   This function reads in and displays the instructions from
   "instructions.txt"
   ********************************************************** */

void displayInstructions()
{
   /* Instantiate input file stream object*/
   ifstream tttRules;

   /* Variable to hold the contents of "instructions.txt" */
   string rules = " ";

   /* Open: "Instructions.txt" */
   tttRules.open("instructions.txt");

   /* If the file exists, and was opened successfully, the content is
      read in and displayed */
   if (tttRules)
   {
      cout << "\n\n";
      while (getline(tttRules, rules) && !tttRules.eof())
      {
         cout << " " << rules << "\n";
      }
   }
   else
   {
      cout << "\n\t\tFile Processing Error: 'instructions.txt' could not be opened,\n"
           << "\t\tread-in or processed. Please make sure that the file exists\n"
           << "\t\tand that it is placed in the correct folder. If it exists but is\n"
           << "\t\tis damaged, replace it with the original file provided to you.\n"
           << "\t\tPlease choose quit from the main menu to exit this program.\n\n";
   }

   /* Close: "instructions.txt" */
   tttRules.close();
}

/* **********************************************************
   Definition: getPlayerInfo

   Asks the players for their names and play tiles they wish
   to play with.
   ********************************************************** */

void getPlayerInfo(string &nameP1, char &tileP1, string &nameP2,
                   char &tileP2)
{
   cout << "\n\t\tName Player One: ";
   cin >> nameP1;

   cout << "\t\tPlay Tile " << nameP1 << ": ";
   cin >> tileP1;

   cout << "\n\t\tName Player Two: ";
   cin >> nameP2;

   cout << "\t\tPlay Tile " << nameP2 << ": ";
   cin >> tileP2;

   /* Input validation */
   while (tileP1 == tileP2)
   {
      cout << "\n\t\t" << nameP1 << " already selected this tile!\n\n";
      cout << "\t\tPlay Tile " << nameP2 << ": ";
      cin >> tileP2;
   }
}

/* **********************************************************
   Definition: initGameboard

   This function initializes the board before the game starts
   with FILLER ('*').
   ********************************************************** */

void initGameboard(gameBoard board)
{
   /* Variables: Index x, Index y */
   int idxX = 0,
       idxY = 0;

   /* Populate the gameboard with the Filler character */
   for (int idxX = 0; idxX < 3; idxX++)
   {
      for (int idxY = 0; idxY < 3; idxY++)
      {
         board[idxX][idxY] = FILLER;
      }
   }
}

/* **********************************************************
   Definition: displayGameboard

   This function displays the gameboard populated at first
   start with FILLER ('*'), and after each move, displaying
   the player tiles at the appropriate position.
   ********************************************************** */

void displayGameboard(gameBoard board)
{
   /* Constant: Seperator width */
   const int SEP_SIZE = 20;

   /* Variables: Index x, Index y (loop counters) */
   int idxX = 0, idxY = 0;

   for (idxX = 0; idxX < POS_X; ++idxX)
   { 
      cout << "\n\n\n\n";

      for (int idxY = 0; idxY < POS_Y; ++idxY)
      {   
         cout <<setw(SEP_SIZE) << right << "| "
              << board[idxX][idxY] << " |";
      }
      cout << "\n\n\n\n";
   }
}

/* **********************************************************
   Definition: getMove

   This function gets the player moves, and assigns the tile
   to a board position if a move is valid.
   ********************************************************** */

void getMove(gameBoard board, string nameP1, string nameP2,
             int &idxX, int &idxY, char tileP1, char tileP2,
             int tick)
{
   /* Variable: Is valid initialized to false */
   bool isValid = false;

   /* Tick is used to display whose turn it is */
   tick % 2 == 1 ? cout << "\t\tMOVE: " << nameP1 << "\n" :
                   cout << "\t\tMOVE: " << nameP2 << "\n";

   cout << "\n\t\tEnter Position x: ";
   cin >> idxX;

   cout << "\t\tEnter Position y: ";
   cin >> idxY;

   /* Validate input */
   while (idxX < 0 || idxY < 0 || idxX > 2 || idxY > 2)
   {
      cout << "\n\t\tInvalid Move!\n";

      cout << "\t\tEnter Position x: ";
      cin >> idxX;

      cout << "\t\tEnter Position y: ";
      cin >> idxY;
   }

   while (isValid = checkMove(board, idxX, idxY) == false)
   {
      cout << "\n\t\tThis square already is already taken!\n";

      cout << "\t\tEnter Position x: ";
      cin >> idxX;

      cout << "\t\tEnter Position y: ";
      cin >> idxY;
   }

   /* If input is accepted as being correct, the appropriate play
      tile is assigned to the gameboard position */
   if (isValid = checkMove(board, idxX, idxY) == true)
   {
      tick % 2 == 1 ? board[idxX][idxY] = tileP1 :
                      board[idxX][idxY] = tileP2;
   }
}

/* **********************************************************
   Definition: checkMove

   This function validates the moves.
   ********************************************************** */

bool checkMove(gameBoard board, int idxX, int idxY)
{
   bool isValid = false;

   board[idxX][idxY] == FILLER ? isValid = true : isValid = false;

   return isValid;
}

/* **********************************************************
   Definition: checkWin

   This function checks whether there is a winner. The first
   and second ternary operators check the vertical and the
   horizontal board positions, the second to the last checks
   diagonal [x+0,0][y+2.2] / [y+2.2][x+0.0] direction, and
   the last ternary checks [x+0.2][y+2.0] / [y+2.0][x+0.0]
   for a win. If a winner is found, isWin gets true, and is
   returned.
   ********************************************************** */

bool checkWin(const gameBoard board, int idxX, int idxY)
{
   bool isWin = 0;

   board[0][idxY] == board[1][idxY] && board[0][idxY] == board[2][idxY] ?
      isWin = true : isWin = false;

   if (isWin == true)
   {
      return isWin;
   }

   board[idxX][0] == board[idxX][1] && board[idxX][0] == board[idxX][2] ?
      isWin = true : isWin = false;

   if (isWin == true)
   {
      return isWin;
   }

   board[0][0] != FILLER && board[0][0] == board[1][1] &&
      board[1][1] != FILLER && board[1][1] == board[2][2] ?
      isWin = true : isWin = false;

   if (isWin == true)
   {
      return isWin;
   }

   board[0][2] != FILLER && board[0][2] == board[1][1] &&
      board[1][1] != FILLER && board[1][1] == board[2][0] ?
      isWin = true : isWin = false;
   return isWin;

   if (isWin == true)
   {
      return isWin;
   }

   return 0;
}

/* **********************************************************
   Definition: popHighscores

   This function accepts the following arrays as arguments:

      * nameTable - Stores the player names
      * highScore - Stores the number of wins of each player
   ********************************************************** */

void popHighscores(string nameTable[], int highScore [], string nameP1,
                   string nameP2, int tick)
{
   /* Variables: Score player one, Score player two */
   static int scoreP1 = 0,
              scoreP2 = 0;

   /* Score increments after each win by 1. The player names
      and highscores are then stored in the according arrays */
   tick % 2 == 1 ? scoreP1 += 1 : scoreP2 += 1;
   tick % 2 == 1 ? nameTable[0] = nameP1 : nameTable[1] = nameP2;
   tick % 2 == 1 ? highScore[0] = scoreP1 : highScore[1] = scoreP2;
}

/* **********************************************************
   definition: displayWinner

   This function displays a congratulatory message when a
   game has been won by either player. In case of a draw,
   the players are notified accordingly.
   ********************************************************** */

void displayWinner(string nameP1, string nameP2, bool isWin, int tick)
{
   if (isWin == true)
   {
      tick % 2 == 1 ? cout << "\n\n\t\t\tCONGRATULATIONS, " << nameP1
                           << "! YOU WON THIS ROUND!\n" :
                      cout << "\n\n\t\t\tCONGRATULATIONS, " << nameP2
                           << "! YOU WON THIS ROUND!\n";
   }
   else
   {
      cout << "\n\t\tWE HAVE A DRAW!\n\n";
   }
}

/* **********************************************************
   Definition: displayHighscores

   This function accepts the following arrays as arguments:

      * nameTable[] - Contains the player names
      * highScore[] - Contains the highscores reached by each
                      player

   If called the Highscores are displayed.
   ********************************************************** */

void displayHighscores(const string nameTable[], const int highScore[])
{
   /* Display: Table header and highscores */
   cout << "\n\n\t\tPLAYER NAME: " << "\t\t" << "HIGHSCORE:\n"
        << "\t\t------------" << "\t\t" << "---------\n";

   for (unsigned int numScores = 0; numScores < NUM_PLAYERS; ++numScores)
   {
      cout << "\t\t" << nameTable[numScores] << "\t\t\t"
           << highScore[numScores] << "\n\n";
   }
}

Example Output:

















No comments:

Post a Comment