【问题标题】:Stuck on Paper-Scissors-Rock game卡在剪刀石头布游戏上
【发布时间】:2020-06-04 03:17:24
【问题描述】:

虽然循环每次迭代两次,CPU 只在为剪刀选择“S”时得分。另一个问题是,我如何让它变得更高级,我在哪里添加该功能?我必须使用字符 'r'、's' 和 'p' 而不是 1、2、3 来接受用户输入。不知道如何继续解决这个问题

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

int main () {

   char player1;
   int player2;
   int userScore = 0, cpuScore = 0;
   player2 = rand ( ) % 3 + 1;
   srand ((int) time (NULL));

int count = 0;
while(count <= 10) {
  printf("\nEnter p for Paper, r for rock, or S for scissor: ");
  scanf("%c", &player1);
   switch(player1) {
      case 'P' :
       if(player1 == 'P' && player2 == 1) {
         printf("Draw!");
         break;
       } else if(player1 == 'P' && player2 == 2) {
         userScore++;
         printf("User won this one!");
         break;
       } else {
         cpuScore++;
         printf("CPU won this one!");
         break;
       }
      case 'R': 
         if(player1 == 'R' && player2 == 2) {
         printf("Draw!");
         break;
       } else if(player1 == 'R' && player2 == 3) {
         userScore++;
         printf("User won this one!");
         break;
       } else {
         cpuScore++;
         printf("CPU won this one!");
         break;
       }
      case 'S':
        if(player1 == 'S' && player2 == 3) {
         printf("Draw!");
         break;
       } else if(player1 == 'S' && player2 == 1) {
         userScore++;
         break;
         printf("User won this one!");
       } else {
         cpuScore++;
         printf("CPU won this one!");
         break;
       }
      default :
         printf("\nInvalid Input");
         break;
   }
   printf("\nUser Score: %d", userScore);
   printf("\nCPU Score: %d", cpuScore);
   count++;
}
   if(userScore == cpuScore) {
     printf("\nDraw game!");
   } else if(userScore > cpuScore) {
     printf("\nYou win!");
   } else {
     printf("\nCPU wins!");
   }
   return 0;
} ``` 

【问题讨论】:

  • 请应用一致的缩进。
  • 您需要致电srand() 之前 rand() 否则您每次都会得到相同的player2。
  • @xing :因为只有一个输入请求并且没有游戏循环,所以在%c 前面加一个空格没有任何用处。如果游戏被迭代,它只能解决scanf() 问题的一个子集。例如,用户可能会输入"paper" 或"pp",这会破坏您的方法。
  • @xing 是的,我的错误 - 代码中糟糕的缩进并不明显。我的观点是前导空格只消耗空格,而不是其他垃圾。请参阅我的答案以获得解决方案。

标签: c if-statement while-loop switch-statement


【解决方案1】:

我建议你修复它,让它变得不那么复杂。剪刀石头布的获胜者可以通过算术来确定。

同类比较更简单,并且使用整数值允许更简单的算术解决方案。同样在算术上,使用 0,1,2 而不是 1,2,3 更简单。所以先将用户输入转化为0,1,2:

#define INVALID_SELECTION sizeof(rps)
static const char rps[] = {'r', 'p', 's'} ;
int human = INVALID_SELECTION ;

while( human == INVALID_SELECTION )
{
    printf("\nEnter R for Rock, P for Paper, or S for Scissors: ");

    char ch = 0 ;
    scanf("%c", &ch ) ;
    while( ch != '\n' && getchar() != '\n' ) ;

    for( human = 0; 
         human < INVALID_SELECTION && tolower(ch) != rps[human] ; 
         human++ )
    {
        // do nothing
    }
}

然后应该这样确定计算机播放:

srand( (int)time(NULL) ) ;
int computer = rand() % 3 ;

虽然请注意您只需要调用一次srand(),因此如果您将游戏置于循环中重复播放,srand() 调用应该出现在重复循环之前。

然后你就可以这样举报了:

static const char* play_lookup[] = { "Rock", "Paper", "Scissors" } ;
printf( "Human played %s\n", play_lookup[human] ) ;
printf( "Computer played %s\n", play_lookup[computer] ) ;

那么human 和computer 在算术上是直接可比的,这样:

int battle = human - computer ;
if( battle < 0 ) battle += 3 ;
switch( battle )
{
    case 0 : printf( "Draw!\n" ) ; break ;
    case 1 : printf( "Human wins!\n" ) ; break ;
    case 2 : printf( "Computer wins!\n" ) ; break ;
}

或(归功于@HAL9000):

int battle = ((human - computer) + 3) % 3 ;
switch( battle )
...

把它们放在一起:

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

int main()
{
    // Randomize
    srand( (int)time(NULL) ) ;
    
    // Repeat play indefinitely
    for(;;)
    {
        #define INVALID_SELECTION sizeof(rps)
        static const char rps[] = {'r', 'p', 's'} ;
        int human = INVALID_SELECTION ;
        
        // While human input is not one of R,P,S,r,p or s...
        while( human == INVALID_SELECTION )
        {
            printf("\nEnter R for Rock, P for Paper, or S for Scissors: ");
        
            char ch = 0 ;
            scanf("%c", &ch ) ;
            while( ch != '\n' && getchar() != '\n' ) ;
        
            // Transform input to one of 0,1,2 (for R,P,S respectively)
            for( human = 0; 
                 human < INVALID_SELECTION && tolower(ch) != rps[human] ; 
                 human++ )
            {
                // do nothing
            }
        }

        // Get computer's play    
        int computer = rand() % 3 ;
    
        // Report human and computer plays in full text
        static const char* play_lookup[] = { "Rock", "Paper", "Scissors" } ;
        printf( "Human played %s\n", play_lookup[human] ) ;
        printf( "Computer played %s\n", play_lookup[computer] ) ;
    
        // Calculate and report result
        int battle = ((human - computer) + 3) % 3 ;
        switch( battle )
        {
            case 0 : printf( "Draw!\n" ) ; break ;
            case 1 : printf( "Human wins!\n" ) ; break ;
            case 2 : printf( "Computer wins!\n" ) ; break ;
        }
    }
    
    return 0;
}

示例输出:

Enter R for Rock, P for Paper, or S for Scissors: r
Human played Rock
Computer played Rock
Draw!

Enter R for Rock, P for Paper, or S for Scissors: r
Human played Rock
Computer played Scissors
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: r
Human played Rock
Computer played Rock
Draw!

Enter R for Rock, P for Paper, or S for Scissors: r
Human played Rock
Computer played Scissors
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: r
Human played Rock
Computer played Paper
Computer wins!

Enter R for Rock, P for Paper, or S for Scissors: p
Human played Paper
Computer played Paper
Draw!

Enter R for Rock, P for Paper, or S for Scissors: p
Human played Paper
Computer played Scissors
Computer wins!

Enter R for Rock, P for Paper, or S for Scissors: p
Human played Paper
Computer played Rock
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: s
Human played Scissors
Computer played Scissors
Draw!

Enter R for Rock, P for Paper, or S for Scissors: s
Human played Scissors
Computer played Paper
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: s
Human played Scissors
Computer played Paper
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: s
Human played Scissors
Computer played Scissors
Draw!

Enter R for Rock, P for Paper, or S for Scissors: s
Human played Scissors
Computer played Rock
Computer wins!

Enter R for Rock, P for Paper, or S for Scissors: R
Human played Rock
Computer played Scissors
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: P
Human played Paper
Computer played Paper
Draw!

Enter R for Rock, P for Paper, or S for Scissors: S
Human played Scissors
Computer played Paper
Human wins!

Enter R for Rock, P for Paper, or S for Scissors: xx

Enter R for Rock, P for Paper, or S for Scissors: yy

Enter R for Rock, P for Paper, or S for Scissors: zz

Enter R for Rock, P for Paper, or S for Scissors: 

【讨论】:

  • 明显的 compare-each-entry 需要O(n^2) 空格;您的算术可比解决方案是O(n),而且更优雅。 +1
  • @Neil 谢谢,虽然我的目标不是降低 计算 的复杂性。相当简洁、可读性和可维护性。
  • 只是为了成为“那个人”,但你可以使用battle +=3; battle %= 3 ; 而不是if( battle &lt; 0 ) battle += 3 ;。那些狭窄的角落少了一个分支。
  • @HAL9000 确保有“等价物”。我在嵌入式系统中工作,通常避免使用 mod 或 div 比避免使用分支更可取,所以我的解决方案是在必要时根据习惯而生。但是,我确实更喜欢您的解决方案。
  • @HAL9000 - 添加信用。玩onlinegdb.com/HJe6RAoX8
【解决方案2】:

试试这个:

#include <stdio.h>
#include <stdlib.h> 
int main () {
   char player1;
   int player2;
   int userScore = 0, cpuScore = 0;
   srand ((int) time (NULL));
   int count = 0;
   while(count != 10) {
       printf("Enter p for Paper, r for rock, or S for scissor:\n");
       fgets(&player1, 80, stdin);
       player2 = rand ( ) % 4;
       if(player1 == 'P' && player2 == 1) {
         printf("Draw!\n");
       }if(player1 == 'P' && player2 == 2) {
        userScore++;
        printf("User won this one!\n");
       }if(player1 == 'P' && player2 == 3){
         cpuScore++;
         printf("CPU won this one!\n");
       }
       if(player1 == 'R' && player2 == 2) {
         printf("Draw!\n");
       }if(player1 == 'R' && player2 == 3) {
         userScore++;
         printf("User won this one!\n");
       }if(player1 == 'R' && player2 == 1){
         cpuScore++;
         printf("CPU won this one!\n");
       }
       if(player1 == 'S' && player2 == 3) {
         printf("Draw!\n");
       }if(player1 == 'S' && player2 == 1) {
         userScore++;
         printf("User won this one!\n");
       }if(player1 == 'S' && player2 == 2){
         cpuScore++;
         printf("CPU won this one!\n");
       }
       if(player1!='S' || player1!='R' || player1!='P'){
         printf("Invalid Input\n");
       }
    printf("User Score: %d\n", userScore);
    printf("CPU Score: %d\n", cpuScore);
    count++;
}
   if(userScore == cpuScore) {
     printf("Draw game!\n");
   } else if(userScore > cpuScore) {
     printf("You win!\n");
   } else {
     printf("CPU wins!\n");
   }
   return 0;
}

我还应该补充一点,这个程序很敏感。例如,输入 'r' 将不起作用,但输入 'R' 会。您可以自行决定更改此设置。

【讨论】:

  • 结果很好,但它继续打印“无效输入”,你知道我怎样才能让它接受字符'r'和'R'。在哪里添加 toupper 功能?
  • 好吧,要让它接受 'R' 和 'r' 你只需要这样写: if((player1 == 'R' || player1=='r') && player2 == 2){
  • 你必须对所有条件都这样做。
  • 如果您想使用 toupper 函数,那么您将获取 player1 输入的值,并在给出后立即将其发送到函数。所以它会在 fgets(&player1, 80, stdin); 之后发送
【解决方案3】:

问题是你在循环之外设置player2,而不是更新它;它会一直保持不变。而不是通过toupper 将其转换为中间值,这可能是直接将其转换为enum 的典型情况。这不是 switch 和 if,而是将您的问题空间缩小到 3 个值,这很容易放入查找表中。

#include <stdlib.h>
#include <stdio.h> /* `printf` requires this. */
#include <time.h>

/* https://en.wikipedia.org/wiki/X_Macro just because I don't want to type. */
#define CHOICE(X)  X(ROCK), X(PAPER), X(SCISSORS)
#define OUTCOME(X) X(LOSS), X(TIE), X(WIN)
#define NAME(A) A
#define STR(A) #A
enum Choice { CHOICE(NAME) };
static const char *const choice_names[] = { CHOICE(STR) };
enum Outcome { OUTCOME(NAME) };
static const char *const outcome_names[] = { OUTCOME(STR) };
static enum Outcome choice_outcomes[][3] = {
    { TIE, LOSS,  WIN },
    { WIN,  TIE, LOSS },
    { LOSS, WIN,  TIE }
};

int main(void) { /* <- `void` should be used to prototype. */
    char input;
    enum Choice player1, player2;
    int score[] = { 0, 0, 0 }; /* <- Replace `cpuScore` and `userScore`. */
    int count = 0;

    srand ((int) time (NULL)); /* <- `srand` should precede `rand`. */

    while(count <= 10) {
        printf("\nEnter p for Paper, r for rock, or S for scissor: ");
        /* Ignores whitespace, (_viz_ enter); breaks on error, (_eg_ EOF.) */
        if(scanf(" %c", &input) != 1) break;
        /* Convert it to `player1` immediately. */
        switch(input) {
        case 'r': case 'R':
            player1 = ROCK; break;
        case 'p': case 'P':
            player1 = PAPER; break;
        case 's': case 'S':
            player1 = SCISSORS; break;
        default:
            printf("Unexpected input, '%c'.\n", input); continue;
        }
        player2 = rand() % 3;
        printf("User choses %s _vs_ Cpu choses %s: %s.\n",
            choice_names[player1], choice_names[player2],
            outcome_names[choice_outcomes[player1][player2]]);
        score[choice_outcomes[player1][player2]]++;
        printf("User Score: %d\n", score[WIN]);
        printf("CPU Score: %d\n", score[LOSS]);
        printf("Ties: %d\n", score[TIE]);
        count++;
    }
    /* One of the places was the `scanf` break, so check, (unlikely.) */
    if(ferror(stdin)) { perror("stdin"); return 1; }

    /* Print the winner. */
    if(score[WIN] == score[LOSS]) {
        printf("Draw game!\n");
    } else if(score[WIN] > score[LOSS]) {
        printf("You win!\n");
    } else {
        printf("CPU wins!\n");
    }
    return 0;
}

我还将userScore 和cpuScore 放入由enum Outcome 索引的数组中。

【讨论】:

    猜你喜欢
    • 2016-01-16
    • 2022-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多