1

So, I'm working on a simple hangman game in C, and I have the function read_guess, shown below.

void read_guess(char *guesses, char *p_current_guess)
{
    int valid_guess = 0;

    // Repeatedly takes input until guess is valid
    while (valid_guess == 0)
    {
        printf(">>> ");
        fgets(p_current_guess, 2, stdin);

        if (!isalpha(*p_current_guess)) printf("Guesses must be alphabetic. Please try again.\n\n");
        else
        {
            valid_guess = 1;

            // Iterates over array of guesses and checks if letter has already been guessed
            for (int i = 0; guesses[i] != '\0'; i++)
            {
                if (guesses[i] == *p_current_guess)
                {
                    printf("You have already guessed this letter. Please try again.\n\n");
                    valid_guess = 0;
                    break;
                }
            }
        }
    }
}

I've tried all the standard input functions (including getchar), but with all of them, when an input larger than one character is supplied, instead of taking just the first character and moving on (or asking again), the rest of the input is "pushed back", and the next time input is requested, whether it be because the input contained a non-alphabetic character or the next round begins, the rest of the input is automatically processed. This repeats for each character of the input.

How can I avoid this?

joe.
  • 75
  • 2
  • no, standard library functions do not have this functionality. remember that user input is just read from the file – 0___________ Feb 14 '20 at 07:43
  • Users enter input as lines? Just discard everything until after the next newline character was found in the input stream. Optionally trigger an error if more than one character was entered. Then proceed with the next round. – moooeeeep Feb 14 '20 at 08:02
  • Also see [How to clear input buffer in C?](https://stackoverflow.com/questions/7898215/how-to-clear-input-buffer-in-c) – jamesdlin Feb 14 '20 at 09:20

3 Answers3

1

You can discard all input until end-of-line, each time you want to ask for input.

void skip_to_eol(FILE* f, int c)
{
    while (c != EOF && c != '\n')
        c = fgetc(f);
}
...
    char c = getchar(); // instead of fgets
    skip_to_eol(stdin, c);
anatolyg
  • 26,506
  • 9
  • 60
  • 134
1

You are using fgets which is good, but unfortunately not the right way...

fgets reads up to an end of line or at most 1 less the the number of character asked. And of course remaining characters are left for the next read operation...

The idiomatic way would be to ensure reading up to the end of line, whatever the length, or at least up to a much larger length.

  1. Simple but could fail in more than SIZE characters on input:

    #define SIZE 64
    ...
    
    void read_guess(char *guesses, char *p_current_guess)
    {
        char line[SIZE];
        int valid_guess = 0;
    
        // Repeatedly takes input until guess is valid
        while (valid_guess == 0)
        {
            printf(">>> ");
            fgets(line, SiZE, stdin);                // read a line of size at most SIZE-1
            p_current_guess[0] = line[0];            // keep first character
            p_current_guess[1] = '\0';
            ...
    
  2. Robust but slightly more complex

    /**
     * Read a line and only keep the first character
     *
     * Syntax: char * fgetfirst(dest, fd);
     *
     * Parameters:
     *  dest: points to a buffer of size at least 2 that will recieve the
     *         first character followed with a null
     *  fd  : FILE* from which to read
     *
     * Return value: dest if one character was successfully read, else NULL
     */
    char *readfirst(dest, fd) {
    #define SIZE 256              // may be adapted
        char buf[SIZE];
        char *cr = NULL;          // return value initialized to NULL if nothing can be read
        for (;;) {
            if(NULL == fgets(buff, sizeof(buff), fd)) return cr; // read error or end of file
            if (0 == strcspn(buff, "\n")) return cr;             // end of file
            if (cr == NULL) {                                    // first read:
                cr = dest;                        //  prepare to return first char
                dest[0] = buff[0];
                dest[1] = 0;
            }
        }
    }
    

    You can then use it simply in your code:

    void read_guess(char *guesses, char *p_current_guess)
    {
        int valid_guess = 0;
    
        // Repeatedly takes input until guess is valid
        while (valid_guess == 0)
        {
            printf(">>> ");
            fgetfirst(p_current_guess, stdin);
    
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

You can use getch() function on windows to get single character. and this is linux equivalent

What is the equivalent to getch() & getche() in Linux?

idris
  • 488
  • 3
  • 6
  • and [How can I capture a key stroke immediately in linux?](https://stackoverflow.com/questions/12710582/how-can-i-capture-a-key-stroke-immediately-in-linux) – David C. Rankin Feb 14 '20 at 07:57
  • @DavidC.Rankin I didnt tried getch() in linux but in windows getch capture it immediatly no need enter. i assume linux equivalent that i put the link should do same. – idris Feb 14 '20 at 08:02
  • Yes, you put the keyboad in *non-cannonical* mode using `tcsetattr`. I have a short example here [How can i use backspace while using getchar(); in this function?](https://stackoverflow.com/questions/53248523/c-language-how-can-i-use-backspace-while-using-getchar-in-this-function/53249714?r=SearchResults&s=1|28.4423#53249714) – David C. Rankin Feb 14 '20 at 08:05
  • @DavidC.Rankin sorry. i miss understand. Thanks for comment. – idris Feb 14 '20 at 08:18