• C

Why does my while loop in C run indefinitely?

I have two while loops below, one that asks the user for their player number, and the other that asks the user to guess a number. Both loops keep running indefinitely if the user enters something unexpected. Why isn't the scanf stopping the program to give the chance to the user to enter in a different value?

And why does it always say it's player 1's turn instead of player 2's turn?

//
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

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

    int authorizedPlayerNumber = 0, secretNumber = 0, playerSelfID= 0;
    int p1PassesLeft=3, p2PassesLeft=3;
    int secretNumberGuess = 0;
    char passResponse[4] = {0};
    
    srand(time(NULL));
    authorizedPlayerNumber = 1 + rand() % 2; /* Random number is generated */
    srand(time(NULL));
    secretNumber = 0 + rand() % 10; /* Random number is generated */
    while (secretNumber!=secretNumberGuess)
    {
        printf("\nIt's player's %d turn", authorizedPlayerNumber);
        printf("\nPlayer Number? ");
        scanf("%d", &playerSelfID);
        
        while (playerSelfID != 1 && playerSelfID != 2) {
            printf("\n%d is not a valid player number. \nPlayer number? ", playerSelfID);
            scanf("%d", &playerSelfID);
            //break;
        }
        while (authorizedPlayerNumber != playerSelfID)
        {
            printf("\nWait your turn.");
            printf("\nIt's player's %d turn", authorizedPlayerNumber);
            printf("\nPlayer number? \n");
            scanf("%d", &playerSelfID);
        }
        

        
        printf("Enter Your Guess, 0 - 10 or Pass: ");
        
        scanf("%s", passResponse);
        
        if (strcmp(passResponse, "pass") == 0){
            if (authorizedPlayerNumber == 1){
                p1PassesLeft--;
                printf("Player 1 has %d more 'Pass' left!\n", p1PassesLeft);
            }
            if(authorizedPlayerNumber == 2){
                p2PassesLeft--;
                printf("Player 2 has %d more 'Pass' left!\n", p2PassesLeft);
            }
        }
        else if (atoi(passResponse) <= 10 && atoi(passResponse) >= 0 ){ //add validation to exit out if non-number or non-pass is selected
            secretNumberGuess = atoi(passResponse); //converts string to int
            if(secretNumberGuess < secretNumber)
                printf("Your guess was to low.\n ");
            else if(secretNumberGuess > secretNumber)
                printf("Your guess was to high.\n ");
            else
                printf("Good job Player %d!! You got it!\n", authorizedPlayerNumber);
        }
        else{
            printf("You did not enter a valid number. You lose your turn.\n ");
        }
        if (authorizedPlayerNumber == 1) {
            authorizedPlayerNumber = 2;
        }
        if (authorizedPlayerNumber == 2) {
            authorizedPlayerNumber = 1;
        }
    }
    return 0;
}

Open in new window

shampouyaAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ozoCommented:
man scanf
...
Scanning stops when an input character does not match such a format character.
     Scanning also stops when an input conversion cannot be made
...
RETURN VALUES
     These functions return the number of input items assigned.  This can be fewer than provided for, or
     even zero, in the event of a matching failure.  Zero indicates that, although there was input avail-
     able, no conversions were assigned; typically this is due to an invalid input character, such as an
     alphabetic character for a `%d' conversion.  The value EOF is returned if an input failure occurs
     before any conversion such as an end-of-file occurs.  If an error or end-of-file occurs after conver-
     sion has begun, the number of conversions which were successfully completed is returned.
0
ozoCommented:
if (authorizedPlayerNumber == 1) {
            authorizedPlayerNumber = 2;  //whenever this happens
}
if (authorizedPlayerNumber == 2) {
            authorizedPlayerNumber = 1; // this follows to undo it
}
0
jmcgOwnerCommented:
Additional comments:

Calling srand again seems pointless, for your purposes.

Calling scanf without checking it's return value can lead you into trouble. I've always thought that calling scanf on user input with anything other than a %s was likely to be asking for trouble. Once you've captured the user input in a string, you can try using sscanf on it to see if it matches your supposition about what it contains. But you always have to be prepared for it being different from what you expected.
0
KuppingerCole Reviews AlgoSec in Executive Report

Leading analyst firm, KuppingerCole reviews AlgoSec's Security Policy Management Solution, and the security challenges faced by companies today in their Executive View report.

Dung TrinhMarching Band Field CrewCommented:
In C scanf will scan the value and put it on the stack only. It will get whatever input, which it can take and push to stack. You will have invalid data, so when it read it out, it can crash the program or create the loop. To be more specific, it is unpredicted behavior. You cannot really know how the program will behave
0
ozoCommented:
scanf can cause undefined behavior when given invalid pointers, which is not the case here, or in the case of integer over flow, which I don't think is the case here.
If playerSelfID was uninitialized, scanf with non-matching data would leave it uninitialized, which could lead to undefined behavior unrelated to scanf, but playerSelfID is initialized, so that doesn't apply either.
In this case, an an infinite loop after any input that does not look like a number when the previous number was not 1, is the  predicted and correct behavior.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ozoCommented:
Calling srand again seems pointless, for your purposes.
Unless your purpose is to make it highly likely that authorizedPlayerNumber and secretNumber have opposite parity.
0
phoffricCommented:
>> In C scanf will scan the value and put it on the stack only.
As a general statement, this is not always true. C scanf
Reads data from stdin and stores them according to the parameter format into the locations pointed by the additional arguments.
http://www.cplusplus.com/reference/cstdio/scanf/?kw=scanf

If the additional arguments are not on the stack, then the scanned value will not be put on the stack.
0
sarabandeCommented:
I've always thought that calling scanf on user input with anything other than a %s was likely to be asking for trouble.
jmcg is right. you better would use gets which reads any input until the user typed <enter> and strtol to convert the input to number.

define the following variables instead of 'char passResponse[4];'

   
char input[256] = { '\0' };
   char * psend = 0;

Open in new window


then you would read input and convert to integer like:

        while(1) 
        {  
            if (gets(input) == 0) 
                return -1;   // user typed ctrl+z
            if ((playerSelfID = strtol(input, &psend, 10)) < 1|| playerSelfID > 2 ||  *psend != '\0')
            {
                printf("\n<%s> is not a valid player number. \nPlayer number? ", input);
                continue;
            }
            ...
            break;
        }

Open in new window


in the same loop before break you could check whether the player id is correct:

           
if (authorizedPlayerNumber != playerSelfID)
            {
                printf("\nWait your turn.");
                ...
                continue;
            }

Open in new window


that way you spare the second while loop.

use input buffer also for reading either 'pass' or the guessed number.

       
printf("Enter Your Guess, 0 - 9 or Pass: ");
        if (gets(input) == 0) 
           break;   
        if (stricmp(input, "pass") == 0)
        {
            if (authorizedPlayerNumber == 1){
            ...
            } else if ((secretNumberGuess = strtol(input, &psend, 10)) <= 10 
                           && secretNumberGuess >= 0 
                           && *psend == '\0'){

Open in new window


finally you may use the following to toggle between player 1 and player 2.

authorizedPlayerNumber = (authorizedPlayerNumber%2)+1;

Open in new window


Sara
0
ozoCommented:
calling scanf on user input with anything other than a %s was likely to be asking for trouble.
char input[256] = { '\0' };
scanf("%s",input);
is also asking for trouble
 scanf("%255s",input);
would be safer.

authorizedPlayerNumber = (authorizedPlayerNumber%2)+1;
Since authorizedPlayerNumber is guaranteed to be 1 or 2 coming in,  it suffices to do
authorizedPlayerNumber = 1+2-authorizedPlayerNumber;
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.