2

I was trying to make a Rock, Paper, Scissors Game in C.

  1. Rock beats Scissors.
  2. Scissors beat Paper.
  3. Paper beats Rock.

My code is:

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

/**
 * @brief Checks whether choice is in arr or not
 * 
 * @param arr 
 * @param choice 
 * @return int 
 */
int In(char (*arr)[10], char *choice){
    int len = strlen(choice);
    int ctr = 0;
    while(*choice!='\0'){
        if(*choice = *(*arr)){
           ctr++; 
        }
        choice++;
        arr++;
    }
    if(ctr==len){
        return 1;
    }
    return 0;
}
/**
 * @brief Ask for playing forward
 * 
 * @return int 
 */
int play_fwd(){
    int fwd;
    fflush(stdin);
    printf("Would You Like to play further?(0 for No/ 1 for Yes): ");
    scanf("%d",&fwd);
    return fwd;
}

int main(){
    char choice[10],choices[3][10],comp_choice[10];
    int cont_fwd = 1;
    strcpy(choices[0],"Rock");
    strcpy(choices[1],"Paper");
    strcpy(choices[2],"Scissor");
    
    printf("!!!Welcome!!!\n");

    srand(time(NULL));
    int index = rand()%3;
    strcpy(comp_choice,choices[index]);

    do{
        fflush(stdin);
        printf("Enter Your Choice: ");
        gets(choice);
        if(In(choices,choice)==0){
            printf("Wrong Input");
            continue;
        }
        
        if(strcmp(choice,"Rock")&&strcmp(comp_choice,"Paper")){
            printf("Lose\n");
            cont_fwd = play_fwd();
        }
        else if(strcmp(choice,"Rock")&&strcmp(comp_choice,"Scissor")){
            printf("Win\n");
            cont_fwd = play_fwd();
        }
        else if(strcmp(choice,"Scissor")&&strcmp(comp_choice,"Paper")){
            printf("Win\n");
            cont_fwd = play_fwd();
        }
        else if(strcmp(choice,"Scissor")&&strcmp(comp_choice,"Rock")){
            printf("Lose\n");
            cont_fwd = play_fwd();
        }
        else if(strcmp(choice,"Paper")&&strcmp(comp_choice,"Rock")){
            printf("Win\n");
            cont_fwd = play_fwd();
        }
        else if(strcmp(choice,"Paper")&&strcmp(comp_choice,"Scissor")){
            printf("Lose\n");
            cont_fwd = play_fwd();
        }
        else{
            printf("Draw\n");
            cont_fwd = play_fwd();
        }
    }while(cont_fwd==1);
    return 0;
}

I am not getting my desired outcome.

IN Function, I guess, isn't working. Since It doesn't print "Wrong Output" as output even if I deliberately put wrong input for choice.

My Way of Thinking for the IN Function:

  1. I will take input of array of string,choices and string array, choice.
  2. Then I will compare two string character by character.
  3. Count of variable, ctr will increase if character are same.
  4. Then I will check whether ctr is equal to length of choice.
  5. If Yes, Then It will return 1 or else,0.

My Way of Thinking for Game:

  1. Create 2 String Arrays,comp_choice,choice and array of string array,choices(which will contains the choices available to computer and users.)
  2. After that I will randomly generate an index value ranging from 0 to 2 which will help in randomly letting computer choose option.
  3. Then I will check whether User had chosen right option or not by checking whether, choice is in choices or not.
  4. If not, then it will ask again.
  5. If yes, then It will check for condition of game and print the status of game.
  6. It will also ask for whether user wants to play further or not.

I am not able to understand why my IN function isn't working? Any Idea??

Edit: Thanks to @Barmar. My Code had started working. I changed the definition of IN Function in my code.

int In(char (*arr)[10], char *choice,int len){
    for(int i=0;i<len;i++){
        if(strcmp(*(arr+i),choice)==0){
            return 1;
        }
    }
    return 0;
}

After this, It started working.

  • 4
    Have you stepped through your code with a debugger? And you may want to look at your use of `strcmp`, since it doesn't `return` what I think you're assuming. – Stephen Newell Jul 25 '22 at 20:19
  • 1
    Stop using `gets()` immediately. It's a dangerous function because you can't specify the buffer size limit. It has been removed from the language. – Barmar Jul 25 '22 at 20:22
  • 1
    Why don't you using `strcmp()` inside `In()`? – Barmar Jul 25 '22 at 20:24
  • `In()` needs another argument that tells it how long `arr` is. – Barmar Jul 25 '22 at 20:25
  • @StephenNewell I didn't used debugger to debug the code. Instead I did Rubber Duck Debugging which I was told to use for short programs. – RACHIT MITTAL Jul 25 '22 at 20:53
  • @Barmar What should I use instead of gets() then? – RACHIT MITTAL Jul 25 '22 at 20:54
  • @Barmar It didn't occurred to my mind to use strcmp(). Can I, instead of specifying length, use pointer arithmetic? – RACHIT MITTAL Jul 25 '22 at 20:56
  • How would you use pointer arithmetic to tell when you've gotten to the end of the array? Unlike strings, there's no special terminator value at the end of an array. You could add one, though. – Barmar Jul 25 '22 at 21:00
  • @Barmar Thanks. Got it. Adding terminator, I guess, wouldn't be as efficient as specifying length as argument. – RACHIT MITTAL Jul 25 '22 at 21:07
  • Rubber duck debugging is great to sanity-check high-level logic. An interactive debugger that lest you step through the program line by line and examine memory is useful when trying to figure out why code isn't doing what you expect. – Stephen Newell Jul 25 '22 at 21:20
  • Another problem is `fflush(stdin)`, which though popular, is undefined. That you're using it suggests you're having the common problem due to mixing `scanf` with other input functions. You should use `scanf` for either everything, or nothing: mixtures are quite unnecessarily frustrating. So either use `scanf("%s")` to read the user's choice, or else don't try to use `scanf` in `play_fwd`. – Steve Summit Jul 25 '22 at 21:57
  • did you do any research? It should give you what you should use in place of `gets` right away [Why is the gets function so dangerous that it should not be used?](https://stackoverflow.com/q/1694036/995714) – phuclv Jul 26 '22 at 04:11
  • @RACHIT MITTAL - Whoever told you to "use Rubber Duck Debugging ... for short programs" was WRONG. You should be able to *THINK* about the problem analytically ("Rubber Duck") *AND* you should *ALSO* frequently step through the code to make sure it's doing what you think it's doing ("empirically"). Both skills/practices are equally important; both are essential. – paulsm4 Jul 26 '22 at 14:41

1 Answers1

3
  1. You have to strip the newline from choice. Easiest option is probably:
- gets(choice);
+ scanf("%9s", choice);

Don't use gets() as it's not safe.

  1. Your In() function uses = instead of == so it changes the value of choice. Here is a more straight forward implementation:
int In(int n, char (*arr)[10], char *choice){       
          for(int i = 0; i < n; i++) {
                  if(!strcmp(arr[i], choice))         
                          return 1;
          }                 
          return 0;                                
}                                                     

Make sure you update caller to pass in number of options.

  1. strcmp() returns 0 when two strings are the same so you want to negate all those tests. Also did a minor refactoring by moving the common cont_fwd = play_fwd(); line to after the conditions:
        if(
            !strcmp(choice,"Rock") &&
            !strcmp(comp_choice,"Paper")
        ) {
            printf("Lose\n");
        } else if(
            !strcmp(choice,"Rock") &&
            !strcmp(comp_choice,"Scissor")
        ) {
            printf("Win\n");
        } else if(
            !strcmp(choice,"Scissor") &&
            !strcmp(comp_choice,"Paper")
        ) {
            printf("Win\n");
        } else if(
            !strcmp(choice,"Scissor") &&
            !strcmp(comp_choice,"Rock")
        ) {
            printf("Lose\n");
        } else if(
            !strcmp(choice,"Paper") &&
            !strcmp(comp_choice,"Rock")
        ) {
            printf("Win\n");
        } else if(
            !strcmp(choice,"Paper") &&
            !strcmp(comp_choice,"Scissor")
        ) {
            printf("Lose\n");
        } else {
            printf("Draw\n");
        }
        cont_fwd = play_fwd();
  1. I removed all the fflush() calls as the behavior is undefined.

  2. The program now seems to work for me, however, you may want to update comp_choice within the loop so each play uses a new value.

The next step would be to encode the game logic in a table along these lines:

choice vs comp_choice Rock Paper Scissor
Rock Draw Lose Win
Paper Win Draw Lose
Scissor Lose Win Draw

If you convert your choice to a symbol (say by converting the text to an enum symbol then it would just an 2d array lookup instead of all those if` statements.

I made this change so you can see how compact it becomes:

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

enum symbol {
    INVALID = -1,
    ROCK,
    PAPER,
    SCISSOR,
    SYMBOLS // count of valid symbols
};

enum symbol str_to_sym(const char *s) {
    if(!strcmp(s, "Rock")) return ROCK;
    if(!strcmp(s, "Paper")) return PAPER;
    if(!strcmp(s, "Scissor")) return SCISSOR;
    return INVALID;
}

const char *rules[3][3] = {
    (const char *[]) { "Draw", "Lose", "Win" },
    (const char *[]) { "Win", "Draw", "Lose" },
    (const char *[]) { "Lose", "Win", "Draw" }
};

int play_fwd() {
    int fwd;
    printf("Would You Like to play further?(0 for No/ 1 for Yes): ");
    scanf("%d", &fwd);
    return fwd;
}

int main() {
    printf("!!!Welcome!!!\n");
    srand(time(NULL));
    for(;;) {
        enum symbol comp_choice = rand() % SYMBOLS;
        printf("Enter Your Choice (Rock, Paper or Scissor): ");
        char str[10];
        scanf("%9s", str);
        enum symbol choice = str_to_sym(str);
        if(choice == INVALID) {
            printf("Wrong Input");
            continue;
        }
        printf("%s\n", rules[choice][comp_choice]);
        if(!play_fwd())
            break;
    }
    return 0;
}
Allan Wind
  • 23,068
  • 5
  • 28
  • 38
  • 1
    @RACHIT MITTAL: one of the most important skills you can learn is to familiarize yourself with your debugger. Know how to set breakpoints, single step through portions of the code, look at variables. It's a very good habit to occasionally walk through your code with some test data - even when "nothing's wrong". You'll learn a lot from doing this - honest! – paulsm4 Jul 26 '22 at 03:57
  • 1
    I approve of the suggestion to use an `enum`; they are exactly what this game state needs, very efficient, and good for debugging, and compact. You will need a simple parser to convert from input lines to `enum`, after that, the logic is much simpler. – Neil Jul 26 '22 at 04:07