Использование fgets и sscanf в C испортило программу после цикла

Я программирую игру крестики-нолики на C.

Кажется, все идет хорошо, но мне нравится реализовывать методы обработки ошибок для таких программ, если пользователь вводит неверные данные.

Я прошу пользователя ввести число от 1 до 9, чтобы заполнить слот на графике крестики-нолики. Когда я использовал вместе fgets и sscanf, в первой игре все работало хорошо. Затем, когда пользователь выбирал «Y» или «y», чтобы продолжить играть в новую игру, казалось, что ни одно из значений переменных не обновлялось, и это в основном вызывало хаос в программе.

Какие-нибудь советы?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
int printmatch(int array[3][3]);
int check(int array[3][3]);
char y;
char Y;
int complacer = 0;
int complacer2 = 0;

int main()
{
  do
  {

    int fill = 0;
    int j = 0;
    int slot = 0;
    int array[3][3];
    array[0][0] = 0;
    array[0][1] = 0;
    array[0][2] = 0;
    array[1][0] = 0;
    array[2][0] = 0;
    array[1][1] = 0;
    array[2][1] = 0;
    array[1][2] = 0;
    array[2][2] = 0;
    srand(time(NULL ));
    printmatch(array);
    char line[20];

    do
    {
      do
      {
        tryagain: printf("\nEnter Position 1-9(from left to right):");

        //I was using fgets and sscanf as an error handling technique incase user inputs incompatible data type, but after the 1st game is over, it seems this code messes up the functionality of the program
        //fgets(line,sizeof(line),stdin);
        //sscanf(line,"%d",&slot);

        //when I just use scanf for data input, all is fine, but limited error handling
        scanf("%d", &slot);
        if (slot > 9 || slot < 1)
        {
          printf("Incorrect data input! Try again. \n");
        }

      } while (!(slot > 0 && slot < 10));

      switch (slot)
      {
      case 1:
        if (array[0][0] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[0][0] = 1;
        check(array);
        break;
      case 2:
        if (array[1][0] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[1][0] = 1;
        check(array);
        break;
      case 3:
        if (array[2][0] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[2][0] = 1;
        check(array);
        break;
      case 4:
        if (array[0][1] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[0][1] = 1;
        check(array);
        break;
      case 5:
        if (array[1][1] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[1][1] = 1;
        check(array);
        break;
      case 6:
        if (array[2][1] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[2][1] = 1;
        check(array);
        break;
      case 7:
        if (array[0][2] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[0][2] = 1;
        check(array);
        break;
      case 8:
        if (array[1][2] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[1][2] = 1;
        check(array);
        break;
      case 9:
        if (array[2][2] == -1)
        {
          printf("\nWoops, try again!");
          goto tryagain;
        }
        array[2][2] = 1;
        check(array);
        break;

      }

      if (array[0][0] != 0 && array[0][1] != 0 && array[0][2] != 0
          && array[1][0] != 0 && array[2][0] != 0 && array[1][1] != 0
          && array[2][1] != 0 && array[1][2] != 0 && array[2][2] != 0)
      {
        check(array);

        if (check(array) == 1)
        {
          printf("The user wins!\n");
        }

        else if (check(array) == -1)
        {
          printf("The computer wins.\n");
        }

        else
        {
          printmatch(array);
          printf("It's a draw!\n");
        }

        goto done;
      }
      ++fill;
      label:

      complacer = rand() % 3;
      complacer2 = rand() % 3;

      if (array[complacer][complacer2] == 0)
      {
        array[complacer][complacer2] = -1;
        check(array);
      }

      else
        goto label;

      ++fill;

      printmatch(array);
      int fullcheck = check(array);
      if (fullcheck == 1)
      {
        printf("The user wins!");
        break;
      }

      if (fullcheck == -1)
      {
        printf("The computer wins.");
        break;
      }

      if (fill > 9)
        break;
    } while (fill < 10);
    done: printf("\nDo you want to continue? Y/N\n");
    scanf("%c %c", &y, &Y);
  } while ((Y == 'Y' || Y == 'y'));
  getchar();
  return 0;

}
int printmatch(int array[3][3])
{

  int i;
  int j;
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 3; j++)
    {
      printf("%d\t", array[j][i]);
    }

    printf("\n");

  }
}

int check(int array[3][3])
{
  int settle;

  if (array[0][0] == 1 && array[1][1] == 1 && array[2][2] == 1)
  {
    settle = 1;

  }

  else if (array[0][0] == -1 && array[1][1] == -1 && array[2][2] == -1)
  {

    settle = -1;

  }

  if (array[0][0] == 1 && array[0][1] == 1 && array[0][2] == 1)
  {
    settle = 1;

  }
  else if (array[0][0] == -1 && array[0][1] == -1 && array[0][2] == -1)
  {

    settle = -1;

  }

  if (array[0][2] == 1 && array[1][2] == 1 && array[2][2] == 1)
  {
    settle = 1;

  }
  else if (array[0][2] == -1 && array[1][2] == -1 && array[2][2] == -1)
  {

    settle = -1;

  }

  if (array[0][1] == 1 && array[1][1] == 1 && array[2][1] == 1)
  {
    settle = 1;

  }
  else if (array[0][1] == -1 && array[1][1] == -1 && array[2][1] == -1)
  {

    settle = -1;

  }

  if (array[1][0] == 1 && array[1][1] == 1 && array[1][2] == 1)
  {
    settle = 1;

  }
  else if (array[1][0] == -1 && array[1][1] == -1 && array[1][2] == -1)
  {
    settle = -1;

  }

  if (array[0][0] == 1 && array[1][0] == 1 && array[2][0] == 1)
  {
    settle = 1;

  }
  else if (array[1][0] == -1 && array[1][1] == -1 && array[1][2] == -1)
  {
    settle = -1;

  }

  if (array[2][0] == 1 && array[1][1] == 1 && array[0][2] == 1)
  {
    settle = 1;

  }
  else if (array[1][0] == -1 && array[1][1] == -1 && array[1][2] == -1)
  {
    settle = -1;

  }
  if (array[2][0] == 1 && array[2][1] == 1 && array[2][2] == 1)
  {
    settle = 1;

  }

  else if (array[2][0] == -1 && array[2][1] == -1 && array[2][2] == 1)
  {
    settle = -1;

  }
  return settle;
}

person user2640115    schedule 01.08.2013    source источник
comment
Кроме того, вложенные-если-в-вложенных-переключателях ужасны. Используйте циклы, целочисленное деление и арифметику по модулю и т.д.   -  person    schedule 01.08.2013
comment
Я заметил, что вы используете goto для обработки ошибок. Я настоятельно рекомендую против этого. Есть много причин, и на самом деле есть много колонок, статей, стяжек и разглагольствований против этого (и иногда статьи в пользу), но в основном все сводится к следующему: это добавляет уровень путаницы и сложности, и это разрушает обычные потоки управления, которые любой, кто смотрит на ваш код, ожидает найти. (Для goto существуют допустимые варианты использования, и я работал с производственным кодом, в котором он используется, но пока вы не станете настоящим экспертом и не будете знать, что делаете, вам не следует даже считай.)   -  person This isn't my real name    schedule 02.08.2013


Ответы (1)


Пользовательский ввод — это зло. Когда вам нужен номер, кто-то набирает «А». Вы хотите «y» или «n», и вы получаете ввод из десятков букв. Престижность за попытку улучшить ваш код с помощью защитной обработки ошибок.

При использовании sscanf() или scanf() и т. д. обязательно проверяйте результаты.

int result;  
result = sscanf(buffer, "%this %that ...", &var1);  
if (result != ExpectedResult) // handle error

Смешивание fgets() с getchar() и scanf() может запутать проблему. Не рекомендуется использовать fgets() с этими 2. fgets() ориентирован на строку, scanf() часто останавливается перед использованием новой строки.

Забавно набирать только «Y» или «y», чтобы продолжить, но использование комбинации «letter & enter_key» не так уж плохо, и поначалу легче разобраться.

Пара fgets() sscanf(), возможно, вызвала заминку, но, в конце концов, с ней легче разобраться. Рекомендую вернуться к этому стилю и проверить результат sscanf().

Пример

scanf("%d", &slot);

Здесь вы не знаете, набрал ли пользователь какое-либо число до нажатия «Ввод». Даже если пользователь набрал число, ваша функция scanf() использует начальные пробелы и цифры, но оставляет «Enter» для функции ввода следующей. Вместо:

int ScanCount;
const char *prompt = "\nEnter Position 1-9(from left to right):";
do {
  fputs(prompt, stdout);
  prompt = "Incorrect data input! Try again. \n"; // For the maybe next time around
  if (fgets(line, sizeof(line), stdin) == NULL) {
    // Standard input is closed or some grievous I/O error, let's go home.
    return 0;
  }
  ScanCount = sscanf(line,"%d",&slot);
} while ((ScanCount != 1) || (slot < 1) || slot > 9));

Кстати, вот небольшая хитрость, чтобы проверить, был ли введен дополнительный текст после числа, например "9z". Замените ScanCount = ... slot > 9)); на

char c;
ScanCount = sscanf(line,"%d%[^\n]",&slot, &c);
} while ((ScanCount != 1) || (slot < 1) || slot > 9));  // still ScanCount != 1
person chux - Reinstate Monica    schedule 01.08.2013
comment
Я новичок в программировании на C, и мне еще предстоит понять, что на самом деле происходит за кулисами. Ваше объяснение было довольно легко понять, спасибо. Единственная строка, которую я не понимаю, это if(fgets(line... и т.д.). Что именно это делает? - person user2640115; 01.08.2013
comment
Это педантично, но получение данных из stdin может завершиться (консоль пользователя закрыта) или завершиться неудачей (сломан канал). Если это произойдет, fgets() вернет NULL, и if() сможет с этим справиться. Поскольку вы новичок, это вряд ли может вас беспокоить. Предложите изучить причины, по которым fgets() может возвращать NULL позже в процессе написания кода. Счастливого пути и добро пожаловать в SO. - person chux - Reinstate Monica; 01.08.2013