Link to home
Create AccountLog in
Avatar of shampouya
shampouya

asked on

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

Avatar of ozo
ozo
Flag of United States of America image

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.
if (authorizedPlayerNumber == 1) {
            authorizedPlayerNumber = 2;  //whenever this happens
}
if (authorizedPlayerNumber == 2) {
            authorizedPlayerNumber = 1; // this follows to undo it
}
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.
Avatar of Dung Trinh
Dung Trinh

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
ASKER CERTIFIED SOLUTION
Avatar of ozo
ozo
Flag of United States of America image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
Calling srand again seems pointless, for your purposes.
Unless your purpose is to make it highly likely that authorizedPlayerNumber and secretNumber have opposite parity.
>> 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.
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
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;