Код игры Yahtzee сообщает мне, что у меня неверный бросок, несмотря на отсутствие ввода, и я делаю первый бросок дважды

Мой код предназначен для игры в Yahtzee, и все работает нормально, пока я не введу имена игроков, он не выполнит первый бросок и не сообщит мне, что мой бросок недействителен, хотя я еще не выбрал, какие броски кубиков хочу сохранить. . Что для меня странно, так это то, что когда он возвращается к игроку 2, он работает совершенно нормально. Если вы запустите код, вы увидите, что сначала вы получаете две единицы, и в этом-то и заключается проблема.

Вот код

// Include C Libraries
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

// Macros
#define ONE 1
#define TWO 2
#define NAME 20
#define ZERO 0
#define SIX 6
#define ROLLS 3
#define THREE 3
#define FOUR 4
#define FIVE 5
#define DICE 5
#define FALSE 0
#define TRUE 1

// Define enumeration scores
enum scores {one, two, three, four, five, six, three_kind, four_kind, full_house, sm_str, lg_str, yahtzee, chance};

// Function Prototypes
void welcomeScreen();
void playGame();
void displayEmptyCard();
void displayDice();
void displayRandomDice();
int rollDie();
void initDice();
void rollDice();
int selectDice();
void displayRoll();
int isValid();
void trimTrailing();

// Main Function
int main()
{
    // Calls welcomeScreen function
    welcomeScreen();

    //random # generator
    srand((unsigned)time(NULL));
  
    // Calls playGame function
    playGame();

    // Program executed successfully
    return 0;
}

// welcomeScreen function displays YAHTZEE logo/rules
void welcomeScreen()
{
    // Prints Title
    printf("\tY       Y     A         H       H   TTTTTTTTT  ZZZZZZZZ   EEEEEEEE   EEEEEEEE\n");
    printf("\t Y     Y     A A        H       H       T            Z    E          E       \n");
    printf("\t  Y   Y     A   A       H       H       T           Z     E          E       \n");
    printf("\t   Y Y     A     A      HHHHHHHHH       T          Z      EEEE       EEEE    \n");
    printf("\t    Y     AAAAAAAAA     H       H       T         Z       E          E       \n");
    printf("\t    Y    A         A    H       H       T        Z        E          E       \n");
    printf("\t    Y   A           A   H       H       T      ZZZZZZZZ   EEEEEEEE   EEEEEEEE\n");
    printf("\n");

    // Prints Game Rules Title
    printf("YAHTZEE GAME RULES\n");
    printf("\n");

    // Prints Game Rules
    printf("1. Five six-sided dice are rolled.\n");
    printf("2. Players roll all five dice.\n");
    printf("3. Players can roll selected dice three times per turn.\n");
    printf("4. Players must score one of the 13 categories per turn.\n");
    printf("5. Players alternate turns.\n");
    printf("6. Game ends when all players score 13 categories.\n");
    printf("7. Player with the highest score wins!\n");
    printf("\n");
}

// Provides The Actual Game Play
void playGame()
{
  // Declaring Characters
  char playerOne[NAME];
  char playerTwo[NAME];
  int currentPlayer = ONE;
  int loop = ZERO;
  int dice[DICE]; 
  int keep[DICE]; 

  // Player 1 Name Input
  printf("Player One, enter your name: ");
  scanf("%s", playerOne);
  
  // Player 2 Name Input
  printf("Player Two, enter your name: ");
  scanf("%s", playerTwo);
  printf("\n");
  
  // Lets Play Display
  printf("%s and %s, let's play Yahtzee!\n", 
  playerOne, playerTwo);
  printf("\n");

  while(loop < 2)
  {
    if (currentPlayer == ONE)
    {
      // Tells The Player Whos Turn It Is
      printf("%s it's your turn.\n", playerOne);
      printf("\n");

      // Displays Empty Card/Dice
      displayEmptyCard();
      printf("\n");
      initDice(dice);
      initDice(keep);
      fflush(stdin);
      rollDice(dice, keep);
      printf("\n");
      currentPlayer = TWO;
    }
    else if (currentPlayer == TWO)
    {
      // Tells The Player Whos Turn It Is
      printf("%s it's your turn.\n", playerTwo);
      printf("\n");

      // Displays Empty Card/Dice
      displayEmptyCard();
      printf("\n");
      initDice(dice);
      initDice(keep);
      fflush(stdin);
      rollDice(dice, keep);
    }
    //Increment loop control variable
    loop++;
  }
}

// Function to Display The Empty Card
void displayEmptyCard()
{
  printf("|---------------------------------------|\n");
  printf("|  UPPER SECTION   |    LOWER SECTION   |\n");
  printf("|---------------------------------------|\n");
  printf("|---------------------------------------|\n");
  printf("| Aces   |         | 3 Kind  |          |\n");
  printf("| Twos   |         | 4 Kind  |          |\n");
  printf("| Threes |         | Full Hs |          |\n");
  printf("| Fours  |         | Sm Str  |          |\n");
  printf("| Fives  |         | LG Str  |          |\n");
  printf("| Sixes  |         | Yahtzee |          |\n");
  printf("| Total  |         | Chance  |          |\n");
  printf("| Bonus  |         | Total   |          |\n");
  printf("|---------------------------------------|\n");
}

//Function to Display Random Dice
void displayRoll(int dice[DICE])
{
  printf("\n");
  printf("+---------+ +---------+ +---------+ +---------+ +---------+\n");
  for (int i = 0; i < 5; ++i){
    if (i == 0){
      printf("|    %d    |", dice[i]);
    }
    else{
      printf(" |    %d    |", dice[i]);
    }
  } 
  printf("\n");
  printf("+---------+ +---------+ +---------+ +---------+ +---------+\n");
  printf("    (1)         (2)         (3)         (4)         (5)    \n");
  printf("\n");
}

//Function for Rolling Dice
int rollDie()
{
  int dieValue = rand() % 6 + 1;
  return dieValue;
}

//Function to Initialize Dice
void initDice(int dice[DICE]){
    for (int i = 0; i < DICE; ++i){
      dice[i] = 0;
  }
}

//Function to Roll Dice
void rollDice(int dice[DICE], int keep[DICE]){
  int roll = 0;
  while (roll != 3){
    printf("Roll %d", roll + 1);
    
    for (int i = 0; i < DICE; ++i){
      if (keep[i] == 0){
        dice[i] = rollDie();
        }
      }
    
    displayRoll(dice);
    if (selectDice(dice, keep) == 0){
      printf("The dice you have selected are invalid. Please try again.\n");
      printf("\n");
    }
    else{
      ++roll;
    }
  }
}

//Function to Select Dice
int selectDice(int dice[DICE], int keep[DICE]){
  char input[NAME];
  char data[NAME];
  char * value;
  int valid = 0;

  printf("Enter the dice you would like to keep, enter values 1 through 5 with spaces between numbers: \n");
  
  fgets(input, NAME, stdin);
  trimTrailing(input);
  strcpy(data, input);
  valid = isValid(data);
  
  printf("\n");
  
  if (valid == 0){
    return 0;
  }

  initDice(keep);
  value = strtok(input, " ");
  
  while (value != NULL){
    int number = atoi(value);
    if (number >= 1 && number <= 5){
      keep[number - 1] = 1;
      valid = TRUE;
    }
    value = strtok(NULL, " ");
  }
  return valid;
}

//Function to determine if Valid
int isValid(char data[NAME]){
  char * value;
  int valid = FALSE;
  value = strtok(data, " ");
  
  while (value != NULL){
    //printf("%d", valid);
    int number = atoi(value);
    if (number >= 1 && number <= 5){
      valid = TRUE;
    }
    else{
      valid = FALSE;
    }
    break;
  }
  value = strtok(NULL, " ");
  return valid;
}

//Function to Trim Trailing
void trimTrailing(char * str){
  
// Set default index to invalid number
  int index = -1;
  
// loop control variable
  int i = 0;
  
// Find last index of non-white space character
  while(str[i] != '\0'){
    if (str[i] != ' ' && str[i] != '\t' && str[i] != '\n'){
      index = i;
    }
    i++;
  }
  
// Mark next character to last non-white space character as NULL
  str[index + 1] = '\0';
}

Я пытался отслеживать значения, которые я установил, чтобы определить, когда следует сообщать пользователю о недопустимом вводе, но я просто не могу это определить.

Вы также не обрабатываете EOF, что приводит к тому, что ваша программа переходит в бесконечный цикл. Мой совет: прочитайте строку данных, а затем извлеките из нее все, что сможете, используя sscanf() или функции более низкого уровня. Если вы не можете ничего извлечь, выбросьте эту строку и возьмите другую. Это более устойчиво к недопустимому вводу, и вы отделяете ввод-вывод, который может привести к сбою или возврату EOF при анализе.

Allan Wind 01.07.2024 05:25
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

scanf("%s", playerTwo); оставляет конечную новую строку во входном буфере, который fgets(input, NAME, stdin) считывает selectDice() и правильно отклоняет его как недопустимый ввод.

Либо удалите новую строку с помощью getchar() (или scanf("%c", &(char) {0})):

    scanf("%s", playerTwo);
    int ch = getchar();
    if (ch == EOF)
         exit(0);

Всегда проверяйте возвращаемое значение всех функций ввода-вывода, таких как scanf(), иначе вы можете работать с неинициализированными данными или попасть в бесконечный цикл EOF.

Или в selectDice() используйте scanf(), чтобы прочитать цифры. Простым вариантом было бы использовать:

   initDice(keep);
   int rv = scanf("%d %d %d %d %d", keep, keep+1, keep+2, keep+3, keep+4);
   if (rv == EOF)
       exit(0);

Или используйте формат %[^\n], чтобы пропускать начальные пробелы и читать все до новой строки. Всегда используйте максимальную ширину поля при чтении строк с scanf(). Макрос str() генерирует максимальную ширину поля и обратите внимание, что вам нужно выделить дополнительный байт для ввода:

#define str(s) str2(s)
#define str2(s) #s

// ...

   char input[NAME+1];
   int rv = scanf(" %" str(NAME) "[^\n]", input);

Более надежный подход — разделить ввод-вывод и анализ и читать построчно (fgets() или getline()), а затем использовать sscanf() или другие функции более низкого уровня для анализа входных данных. Если вы не можете что-то разобрать, выбросьте эту строку и прочитайте другую строку.

Другие вопросы по теме