By benwad


2009-11-03 20:04:19 8 Comments

I'm trying to develop a simple text-based hangman game, and the main game loop starts with a prompt to enter a guess at each letter, then goes on to check if the letter is in the word and takes a life off if it isn't. However, when I run the game the prompt comes up twice each time, and the program doesn't wait for the user's input. It also takes off a life (one life if it was the right input, two if it wasn't), so whatever it's taking in isn't the same as the previous input. Here's my game loop, simplified a bit:

while (!finished)
{
    printf("Guess the word '%s'\n",covered);

    scanf("%c", &currentGuess);

    i=0;
    while (i<=wordLength)
    {
        if (i == wordLength)
        {
            --numLives;
            printf("Number of lives: %i\n", numLives);
            break;
        } else if (currentGuess == secretWord[i]) {
            covered[i] = secretWord[i];
            secretWord[i] = '*';
            break;
        }
        ++i;
    }

    j=0;
    while (j<=wordLength)
    {
        if (j == (wordLength)) {
            finished = 1;
            printf("Congratulations! You guessed the word!\n");
            break;
        } else {
            if (covered[j] == '-') {
                break;
            }
        }
        ++j;

        if (numLives == 0) {
            finished = 1;
        }

    }
}

I assume the problem is scanf thinking it's taken something in when it hasn't, but I have no idea why. Does anyone have any idea? I'm using gcc 4.0.1 on Mac OS X 10.5.

10 comments

@Mohit 2010-10-03 04:33:28

When you enter the character, you have to enter a whitespace character to move on. This whitespace character is present in the input buffer, stdin file, and is read by the scanf() function. This problem can be solved by consuming this extra character. This can be done by usnig a getchar() function.

scanf("%c",&currentGuess);  
getchar();   // To consume the whitespace character.

I would rather suggest you to avoid using scanf() and instead use getchar(). The scanf()requires a lot of memory space. getchar() is a light function. So you can also use-

char currentGuess;  
currentGuess=getchar();  
getchar();  // To consume the whitespace character.

@Nick 2011-09-15 01:13:37

scanf(" %c", &fooBar);

Notice the space before the %c. This is important, because it matches all preceding whitespace.

@bta 2009-11-03 21:35:21

When you read keyboard input with scanf(), the input is read after enter is pressed but the newline generated by the enter key is not consumed by the call to scanf(). That means the next time you read from standard input there will be a newline waiting for you (which will make the next scanf() call return instantly with no data).

To avoid this, you can modify your code to something like:

scanf("%c%*c", &currentGuess);

The %*c matches a single character, but the asterisk indicates that the character will not be stored anywhere. This has the effect of consuming the newline character generated by the enter key so that the next time you call scanf() you are starting with an empty input buffer.

Caveat: If the user presses two keys and then presses enter, scanf() will return the first keystroke, eat the second, and leave the newline for the next input call. Quirks like this are one reason why scanf() and friends are avoided by many programmers.

@Craig Ringer 2012-08-15 03:25:29

An anonymous edit tried to add the text: "it should be %*c%c, not %c%*c, for my code at least" to this post, and change the scanf to match. I've rejected the edit as I'm not convinced it's right, but want to raise it as a comment for consideration.

@bta 2012-08-16 03:47:53

@Craig Ringer- "%c%*c" will eat the newline attached to the current input character. You would use "%*c%c" after the fact, to eat a newline from a previous input. Both could technically be correct, but it's better practice to clean up after yourself instead of leaving stray characters in the buffer for someone else to have to deal with.

@ilgaar 2013-12-31 22:29:22

Actually it is important where you put "%*c" consider this piece of code, where "%*c%i" fixes the infinite loop problem, but "%i*c" won't: getagin: printf("Please enter a number:\n"); isnumber = scanf("%*c%i", &number); // "%*c%i" will eat the newline attached to the current input character. and fix the problem. But not "%i%*c" if(isnumber) { printf("You enterd a number and it was %i\n", number); } else { printf("You did not eneter a number.\n"); goto getagin; }

@chux - Reinstate Monica 2018-10-17 14:06:14

If code wants to consume a trailing '\n' and not something else, then use "%*1[\n]" instead of "%*c".

@Cacahuete Frito 2019-05-07 21:33:33

@chux Although technically more precise, because there is the legitimate case where you would want to not skip spaces, the generic " %c" will be easier.

@T.E.D. 2009-11-03 20:21:50

Jim and Jonathan have it right.

To get your scanf line to do what you want (consume the newline character w/o putting it in the buffer) I'd change it to

scanf("%c\n", &currentGuess);

(note the \n)

The error handling on this is atrocious though. At the least you should check the return value from scanf against 1, and ignore the input (with a warning) if it doesn't return that.

@T.E.D. 2017-09-18 13:29:07

@AnttiHaapala - In this case, that's not undesirable. All he's trying to do is take single-character commands in without generating 2 inputs. Since whitespace is not a command in this scheme, he doesn't want it. Which is probably why he accepted the more complex answer, which also skips leading whitespace.

@Antti Haapala 2017-09-18 13:31:47

Your answer is completely different. It would wait until another non-whitespace character is input before it processes the first character. Thus it is not useful in solving the problem.

@T.E.D. 2017-09-18 14:07:39

@AnttiHaapala - Just tried it in an online compiler. It does indeed wait for a second input. That second input isn't lost, but thereafter the program is always one command behind in its processing. Not totally unworkable, but definitely sub-optimal. Another reason to like the Leffler answer better.

@sdtom 2009-11-03 20:20:46

I see a couple of things in your code:

  1. scanf returns the number of items it read. You will probably want to handle the cases where it returns 0 or EOF.
  2. My guess would be that the user is hitting letter + Enter and you're getting the newline as the second character. An easy way to check would be to add a debugging printf statement to show what character was entered.
  3. Your code will only match the first occurrence of a match letter, i.e. if the word was "test" and the user entered 't', your code would only match the first 't', not both. You need to adjust your first loop to handle this.

@Roboprog 2009-11-03 20:18:18

I'll guess: your code is treating a newline as one of the guesses when you enter data. I've always avoided the *scanf() family due to uncontrollable error handling. Try using fgets() instead, then pulling out the first char/byte.

@Roboprog 2009-11-03 20:20:48

OK, everybody jumped on the trailing newline byte/char within a minute. Thus, the suggestion to read a line of input, then check what you have, cleaning it up and detecting errors as needed. Perhaps in a "get_answer()" function?

@pmg 2009-11-03 20:17:40

A couple points I noticed:

  • scanf("%c") will read 1 character and keep the ENTER in the input buffer for next time through the loop
  • you're incrementing i even when the character read from the user doesn't match the character in secretWord
  • when does covered[j] ever get to be '-'?

@jheddings 2009-11-03 20:16:17

Break the problem up into smaller parts:

int main(void) {
    char val;
    while (1) {
        printf("enter val: ");
        scanf("%c", &val);
        printf("got: %d\n", val);
    }
}

The output here is:

enter val: g
got: 103
enter val: got: 10

Why would scanf give you another '10' in there?

Since we printed the ASCII number for our value, '10' in ASCII is "enter" so scanf must also grab the "enter" key as a character.

Sure enough, looking at your scanf string, you are asking for a single character each time through your loop. Control characters are also considered characters, and will be picked up. For example, you can press "esc" then "enter" in the above loop and get:

enter val: ^[
got: 27
enter val: got: 10

@Roboprog 2009-11-04 01:59:37

Good answer: distill the problem down to its essence, show and explain (while racing all the other posters, no less!). This answer deserves more than my single mod point.

@jheddings 2009-11-04 02:48:35

Thanks for the vote! I wanted to be sure of my answer before I posted, and it was already answered by the time I confirmed it. I thought it might be useful to just jot down my method instead.

@Jonathan Leffler 2009-11-03 20:13:18

Newlines.

The first time through the loop, scanf() reads the character. Then it reads the newline. Then it reads the next character; repeat.

How to fix?

I seldom use scanf(), but if you use a format string "%.1s", it should skip white space (including newlines) and then read a non-white space character. However, it will be expecting a character array rather than a single character:

char ibuff[2];

while ((scanf("%.1s", ibuff) == 1)
{
    ...
}

@T.E.D. 2009-11-03 20:24:59

I think I like this solution better than mine, as it would allow the user to simply type in the rest of the word (we are playing Hangman, remember?) once they know what it is.

@caf 2009-11-03 20:53:22

scanf(" %c", &currentguess) should also do the trick, without needing to change the type of the variable. Whitespace in the scanf format string matches "any amount of whitespace, including none, in the input".

@Jim Garrison 2009-11-03 20:12:28

Just a guess, but you are inputting a single character with scanf, but the user must type the guess plus a newline, which is being consumed as a separate guess character.

Related Questions

Sponsored Content

16 Answered Questions

[SOLVED] Why is scanf() causing infinite loop in this code?

  • 2009-11-11 15:39:29
  • user208785
  • 55324 View
  • 48 Score
  • 16 Answer
  • Tags:   c gcc scanf

10 Answered Questions

1 Answered Questions

One of my scanf loops is asking for two inputs before executing. Whitespace problem?

  • 2019-04-06 21:33:05
  • Noah Sinishtaj
  • 36 View
  • 0 Score
  • 1 Answer
  • Tags:   c scanf

2 Answered Questions

3 Answered Questions

[SOLVED] In C - scanf() in a for loop is causing printf() to run multiple times

  • 2017-01-19 06:06:52
  • marc-maguire
  • 1530 View
  • 0 Score
  • 3 Answer
  • Tags:   c scanf

1 Answered Questions

[SOLVED] Last character in input buffer (C)

  • 2014-12-05 15:12:41
  • Gal Fl
  • 1470 View
  • 0 Score
  • 1 Answer
  • Tags:   c input buffer

2 Answered Questions

[SOLVED] Why is my while loop acting this way?

  • 2014-09-12 20:31:35
  • Belphegor
  • 92 View
  • 0 Score
  • 2 Answer
  • Tags:   c

Sponsored Content