0

Trying to complete an assignment for a simple tic tac toe game without using functions or anything that has not been covered in our class. Currently everything in the code is acceptable but I keep getting an infinite loop issue when picking a square and using a character that is not a number. Any help would be appreciated!

#include <iostream>
using namespace std;

int i;
char board[10];
bool gameover;
bool check_choice;
char player;
int choice = 0;
char restart;

int main()
{

do
{


    for (int i = 0; i < 10; i++) board[i] = ' ';

    bool gameover = false;
    bool check_choice = true;
    char player = 'X';

    do
    {
        // Draw game board

        cout << "+---+---+---+ \n";
        cout << "| " << board[1] << " | " << board[2] << " | " << board[3] << " |  \n";
        cout << "+---+---+---+ \n";
        cout << "| " << board[4] << " | " << board[5] << " | " << board[6] << " |  \n";
        cout << "+---+---+---+ \n";
        cout << "| " << board[7] << " | " << board[8] << " | " << board[9] << " |  \n";
        cout << "+---+---+---+ \n";

        // Win Check

        if ((board[1] == 'X' && board[2] == 'X' && board[3] == 'X') ||
            (board[4] == 'X' && board[5] == 'X' && board[6] == 'X') ||
            (board[7] == 'X' && board[8] == 'X' && board[9] == 'X') ||
            (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') ||
            (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') ||
            (board[3] == 'X' && board[6] == 'X' && board[9] == 'X') ||
            (board[1] == 'X' && board[5] == 'X' && board[9] == 'X') ||
            (board[3] == 'X' && board[5] == 'X' && board[7] == 'X'))
        {
            cout << "Game Over - X wins! \n";
            gameover = true;
        }

        else if ((board[1] == 'O' && board[2] == 'O' && board[3] == 'O') ||
            (board[4] == 'O' && board[5] == 'O' && board[6] == 'O') ||
            (board[7] == 'O' && board[8] == 'O' && board[9] == 'O') ||
            (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') ||
            (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') ||
            (board[3] == 'O' && board[6] == 'O' && board[9] == 'O') ||
            (board[1] == 'O' && board[5] == 'O' && board[9] == 'O') ||
            (board[3] == 'O' && board[5] == 'O' && board[7] == 'O'))
        {
            cout << "Game Over - O wins! \n \n";
            gameover = true;
        }
        // Draw Check

        else if ((board[1] != ' ' && board[2] != ' ' && board[3] != ' ') &&
                 (board[4] != ' ' && board[5] != ' ' && board[6] != ' ') &&
                 (board[7] != ' ' && board[8] != ' ' && board[9] != ' '))
        {
            cout << "Game Over - Draw \n \n";
            gameover = true;
        }
        else

            check_choice = true;

        while (check_choice == true)
        {
            cout << "Place " << player << " at: ";
            cin >> choice;

            if (choice > sizeof(board) || choice == 0 || choice == 10)
            {
                cout << "Invalid input! 1-9 only. \n";
                check_choice = true;
            }

            else if (board[choice] == ' ')
            {
                board[choice] = (player == 'X') ? 'X' : 'O';
                player = (player == 'X') ? 'O' : 'X';
                check_choice = false;
            }
            else
            {
                cout << "Invalid input! Position already filled. \n";
                check_choice = true;
            }
        }

    } while (gameover == false);



    cout << "Would you like to play again? (Y / N): ";
    cin >> restart;
    cin.ignore();

} while (restart == 'y' || restart == 'Y');



cout << "Thanks for playing!! \n \n";

system("pause");
return 0;
} 
QF Marine
  • 21
  • 1
  • Where is your error? – MMG Apr 01 '20 at 02:12
  • Arrays start their indexing at `0`, not `1`. Your attempt of accessing `board` at `1` can potentially lead to off-by-one bugs. – PaulMcKenzie Apr 01 '20 at 02:14
  • if lets say / is used for choice, it will post "Invalid input! 1-9 only. " infinitely... – QF Marine Apr 01 '20 at 02:14
  • 1
    *but I keep getting an infinite loop issue when picking a square and using a character that is not a number.* -- Read your data into a string, and check if the string has valid information. Also, you should remove the extraneous global variables, plus it would be a better idea if this were put into a function instead of sticking everything in `main`. – PaulMcKenzie Apr 01 '20 at 02:19
  • @PaulMcKenzie can you elaborate on that? Or maybe post an example on how I would validate the string? Also I am not allowed to use functions in this assignment, as we have not learned those yet – QF Marine Apr 01 '20 at 02:22
  • 1
    If you expect bad input, you should get the input into a string, check out the string to see if it has valid `int` data, and go forward if it is ok. Also some advice -- you could have whittled this down to "how to test if a string is a valid integer" or something like that, instead of posting a whole tic-tac-toe game. Obviously there will be StackOverflow posts on how to test if a string is an integer, you can easily search for such questions. – PaulMcKenzie Apr 01 '20 at 02:32
  • 2
    Helpful [how to convert int to char, char to int after get an input](https://stackoverflow.com/a/54398368/3422102) and for clearing invalid characters with `.ignore()`, see [How to catch invalid user inputs](https://stackoverflow.com/a/54474262/3422102) – David C. Rankin Apr 01 '20 at 02:42

3 Answers3

1

When an error occurs when reading from a stream, an error flag gets set and no more reading is possible until you clear the error flags. That's why you get an infinite loop.

Copied the answer from here, so no credit to me :P

And as @PaulMcKenzie said, you're better of reading it into a string first, and then doing the necessary checks.

melk
  • 530
  • 3
  • 9
0

As melk correctly pointed out, the issue is with the error flag which gets set when you get wrong input preventing you from using the stream before you clear the flag.

Others have suggested using strings and then validating the input which would work quite well. But, if you don't want to make a lot of changes in your code, you can just declare your choice variable to be of type char.

Now, after you take the input, because of the way the char data type works, your choice variable will store the ASCII value of the character entered by the user. To make it store the numeric value itself instead, you can do this neat trick:

choice -= '0';

This exploits the fact that in ASCII, the numeric characters come one after the other starting from '0'. So '1' - '0' would give 1 and '5' - '0' would give 5.

Now the rest of your code now works well, since you won't face any problem checking whether the entered character was a number between 1-9.

Though one more thing you'll need to do is to flush the input buffer when the user enters a wrong choice. This is to prevent the error message from appearing multiple times ( once for each character entered ) if the user enters multiple characters.

So your code should overall look like this ( I have just added two more lines):

#include <iostream>
using namespace std;

int i;
char board[10];
bool gameover;
bool check_choice;
char player;
char choice = 0; // the choice variable is now a character
char restart;

int main()
{

do
{


    for (int i = 0; i < 10; i++) board[i] = ' ';

    bool gameover = false;
    bool check_choice = true;
    char player = 'X';

    do
    {
        // Draw game board

        cout << "+---+---+---+ \n";
        cout << "| " << board[1] << " | " << board[2] << " | " << board[3] << " |  \n";
        cout << "+---+---+---+ \n";
        cout << "| " << board[4] << " | " << board[5] << " | " << board[6] << " |  \n";
        cout << "+---+---+---+ \n";
        cout << "| " << board[7] << " | " << board[8] << " | " << board[9] << " |  \n";
        cout << "+---+---+---+ \n";

        // Win Check

        if ((board[1] == 'X' && board[2] == 'X' && board[3] == 'X') ||
            (board[4] == 'X' && board[5] == 'X' && board[6] == 'X') ||
            (board[7] == 'X' && board[8] == 'X' && board[9] == 'X') ||
            (board[1] == 'X' && board[4] == 'X' && board[7] == 'X') ||
            (board[2] == 'X' && board[5] == 'X' && board[8] == 'X') ||
            (board[3] == 'X' && board[6] == 'X' && board[9] == 'X') ||
            (board[1] == 'X' && board[5] == 'X' && board[9] == 'X') ||
            (board[3] == 'X' && board[5] == 'X' && board[7] == 'X'))
        {
            cout << "Game Over - X wins! \n";
            gameover = true;
        }

        else if ((board[1] == 'O' && board[2] == 'O' && board[3] == 'O') ||
            (board[4] == 'O' && board[5] == 'O' && board[6] == 'O') ||
            (board[7] == 'O' && board[8] == 'O' && board[9] == 'O') ||
            (board[1] == 'O' && board[4] == 'O' && board[7] == 'O') ||
            (board[2] == 'O' && board[5] == 'O' && board[8] == 'O') ||
            (board[3] == 'O' && board[6] == 'O' && board[9] == 'O') ||
            (board[1] == 'O' && board[5] == 'O' && board[9] == 'O') ||
            (board[3] == 'O' && board[5] == 'O' && board[7] == 'O'))
        {
            cout << "Game Over - O wins! \n \n";
            gameover = true;
        }
        // Draw Check

        else if ((board[1] != ' ' && board[2] != ' ' && board[3] != ' ') &&
                 (board[4] != ' ' && board[5] != ' ' && board[6] != ' ') &&
                 (board[7] != ' ' && board[8] != ' ' && board[9] != ' '))
        {
            cout << "Game Over - Draw \n \n";
            gameover = true;
        }
        else

            check_choice = true;

        while (check_choice == true)
        {
            cout << "Place " << player << " at: ";
            cin >> choice;
            choice -= '0'; // added this line to convert character to number

            if (choice > sizeof(board) || choice == 0 || choice == 10)
            {
                cout << "Invalid input! 1-9 only." << std::endl;
                check_choice = true;
                fflush(stdin); // added this line to clear input buffer
            }

            else if (board[choice] == ' ')
            {
                board[choice] = (player == 'X') ? 'X' : 'O';
                player = (player == 'X') ? 'O' : 'X';
                check_choice = false;
            }
            else
            {
                cout << "Invalid input! Position already filled. \n";
                check_choice = true;
            }
        }

    } while (gameover == false);



    cout << "Would you like to play again? (Y / N): ";
    cin >> restart;
    cin.ignore();

} while (restart == 'y' || restart == 'Y');



cout << "Thanks for playing!! \n \n";

system("pause");
return 0;
}

Nice work on the game, otherwise, by the way. Looking great for a console project.

Tanay Karnik
  • 424
  • 6
  • 19
  • You might want to add that reading the choice into a `char` only works with numbers 0-9 (as is the case here), as soon as they are bigger (or smaller) you will need a string. – melk Apr 02 '20 at 07:19
-1

You can use something like

string mystr;
getline (cin,mystr);

to read a line from cin into a string. After this, you can check if it is a number and then obtain it or, if it is not, show an error.

PS: you should not use global variables and you should split your code into functions. Also, your array is one larger than it needs to be (you are allocating space for 10 elements but you just need 9). Be careful tho, since you are mistakenly starting the indices at 1, when arrays start at 0, which means right now you are ignoring completely the first element. If you change it to allocate 9 elements make sure to substract 1 to all your indices have them correct (otherwise accesing 9 will cause errors as you are accesing an element out of the array bounds).