4

This code is for game of craps.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

int roll_dice(void);
bool play_game(void);

int main()
{
   int i, ch,win = 0,lose = 0;
   bool flag;
   srand((unsigned)time(NULL));

   do
   {
       flag = play_game();
       if(flag)
       {
           printf("You win!");
           win++;
       }
       else
       {
           printf("You lose!");
           lose++;
       }

       printf("\n\nPlay again(Y/N)? ");
       scanf("%c", &ch);
       ch = getchar();
       printf("\n");
   }while(ch == 'Y' || ch == 'y');

   printf("\nWins: %d   Losses: %d",win,lose);
   return 0;
}

int roll_dice(void)
{
    return rand()%6 + rand()%6 + 2;
}

bool play_game(void)
{
   int sum = roll_dice();

   printf("You rolled: %d\n", sum);
   if(sum == 7 || sum == 11)
       return 1;
   else if(sum == 2 || sum == 3 || sum == 12)
       return 0;    
   else
   {
       int point = sum;
       printf("Your point is: %d\n", point);
       do
       {
           sum = roll_dice();
           printf("You rolled: %d\n", sum);
           if(sum == 7)
               return 0;            
       }while(point != sum);
       return 1;
   }                    
}

I have problem only with code snippet

 printf("\n\nPlay again(Y/N)? ");
 scanf("%c", &ch);
 ch = getchar();
 printf("\n");

I have used, because it terminates after one iteration whatever user input Y or N. I thought I am doing wrong by placing ch = getchar() to eat up \n, I removed it and placed a space before conversion specifier and replaced it by " %c" which also did't work.When I replaced the conversion specifier by %d it works fine.
Is anything going wrong with this?
I visited this post and it is saying same thing I did.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 2
    Game of that throne.. – Alex1985 Jul 05 '13 at 21:33
  • 3
    "Reading character with `scanf()`" - No, no, ***never!*** For that, one can use `fgetc(stdin)` instead. In fact, if you ever consider using `scanf()`, then just don't. –  Jul 05 '13 at 21:33
  • @Renan; yes, I did't post question because it has nothing to do with it. – haccks Jul 05 '13 at 21:33
  • BTW if you're getting a value into a character (`%c`) then it shouldn't add a `\n` there. `\n` is another character by itself and you're fetching only one. But then again, my C++ is more rusty than the Titanic. – Geeky Guy Jul 05 '13 at 21:34
  • @H2CO3 I think your comment is a worthy answer by itself. – Geeky Guy Jul 05 '13 at 21:34
  • @Renan Pretty much, yes :) –  Jul 05 '13 at 21:35
  • @H2CO3; see the link I have given with this question. – haccks Jul 05 '13 at 21:36
  • @haccks That answer suggests that you **throw away** the return value of the second `getchar()`. That's not the same as assigning it to your variable. –  Jul 05 '13 at 21:37
  • @H2CO3 would you mind to be more verbose about the reasons why I should never use scanf? I was reading the scanf man page and I found no warnings about never using it. Perhaps you would like to include the reasons, below, along with your answer. I'm looking foward to learn a little bit more about scanf. Thanks – yeyo Jul 05 '13 at 22:14
  • @Kira `scanf()` is wildly abused. The reason is that especially beginner programmers notice its apparent symmetry with the widely used and popular `printf()`. However, `scanf()` has different subtleties concerning primarily format strings, which in general don't behave according to one's obvious/naive expectations. The "magic" format strings (which sometimes appear to be regular expressions but they aren't, eww!) are also harder to read. Furthermore, `scanf()` is often used for reading formatted input, but since it's easy to get it wrong, it's used the wrong way and it breaks the code. –  Jul 05 '13 at 22:19
  • @Kira It's also unsafe when it comes to reading strings (the naive `scanf("%s", buffer)` approach either stops at the first whitespace or potentially causes a buffer overflow). So my general advice is this: read as much **raw** data as you can/logical (for example, using `fgets()` if reading from the keyboard or `fread()` if reading from a file), then parse the result using dedicated functions of the C standard library. Parse numbers using `strtol()` and `strtod()`, detect delimiters using `strtok_r()`, find substrings with `strstr()`, etc. Here I must mention that `scanf()`, because of –  Jul 05 '13 at 22:21
  • @Kira the enormous, universal format engine built into it, is **slow.** For example, a short code snippet written manually using `strchr()` and/or `strtok()` will almost certainly be faster that one using `scanf()` (this holds to `puts()` versus `printf()` too, for the record). So unless it's **absolutely inevitable** to you `scanf()`, it's really **just not worth it.** –  Jul 05 '13 at 22:23

4 Answers4

4

The posted code has undefined behaviour because ch is of type int and the format specifier %c must match a char.

When I replaced the conversion specifier %d it works fine.

When you switch to %d the scanf() fails, because Y or y is not an int, so no input is consumed (apart from leading whitespace which discards the new line character on subsequent iterations of the loop) and the subsequent ch = getchar() actually reads the user entered character, and the code works by fluke. Always check the return value of scanf(), which returns the number of assignments made.

hmjd
  • 120,187
  • 20
  • 207
  • 252
  • Tell me one thing, I read that `char` can be defined as `int`, is it wrong?? – haccks Jul 05 '13 at 22:12
  • And also, I am agree that on using `%d` it will not read a `char` and `ch = getchar()` reads the entered character but, why it is not reading `\n`(left behind) on next iteration? – haccks Jul 05 '13 at 22:21
  • 1
    No. It fails to read Y. So getchar reads the Y, leaving new line unconsumed. Next time round the loop the new line is then skipped, but the next Y fails and getchar reads it leaving the new line ... – hmjd Jul 05 '13 at 22:35
  • You mean to say new line is skipped by `scanf`? – haccks Jul 05 '13 at 22:39
  • Thanks for the answer. You explained well. – haccks Jul 06 '13 at 00:18
3
scanf("%c", &ch);
ch = getchar();

And that's how you lost the previous char stored in ch. How about

ch = fgetc(stdin);
while (fgetc(stdin) != '\n')
    ;

instead?

  • I know there are many possible ways, but why it is not working in that way I tried. You can see many posts regarding this and(also [here](http://stackoverflow.com/questions/14419954/reading-a-single-character-in-c)) which is saying that it should work. – haccks Jul 05 '13 at 21:39
  • @haccks My example was actually flawed and not what I inteneded, see fixed update. –  Jul 05 '13 at 21:43
3

You convert the character with scanf(), and then overwrite it with getchar() immediately afterwards. I wouldn't expect it to work, unless you type "yy" before typing ENTER, but then your second confirmation would fail.

BTW, use the space in " %c".

Paulo1205
  • 918
  • 4
  • 9
2
printf("Play again? ");
scanf(" %c", &char);

this code works for me. The project is from K.N.King's "C programming : A modern approach" book. I met with this problem before and had the same problem. On page 224 there is a guess.c example project which includes exactly the same command "ask" ("play again"). And author used scanf(" %c", &command); (he used command instead of ch) and it did work. I remember I used it during the "game of craps" project but it did not work. Probably I missed something. Overall, the expression above 100% does work.

Orkhan Hasanli
  • 39
  • 1
  • 1
  • 5