Tic-Tac-Toe бесконечный цикл пользовательского ввода при вводе буквенных символов - PullRequest
1 голос
/ 27 марта 2019

Итак, я делаю игру в крестики-нолики на C, и у меня возникла проблема, когда я прошу пользователя отменить свой ход, нажав 2. Каждый раз, когда наступает его очередь, они могут отменить ход или продолжить выберите там следующий ход, выбрав другую клавишу.

Это работает так, как должно, но у меня есть проблема, когда, если пользователь вводит алфавитный символ, программа просто повторяет цикл, говоря, что это не число, когда проверяет мою функцию GetHumanMove. Кто-нибудь знает, как заставить это прекратить делать это и просто сказать, что это не число один раз без циклического перезапуска программы?

Вот мой код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <windows.h>

//Tic-Tac-Toe game in C
//20/03/2019
//5x5 Board with 4 in a row to win. Also has playable AI that randomly blocks human moves. 
//Also has replay function using a doubly linked list

/*
int board[49] = {
    ;,;, ;, ;, ;, ;, ;,
    ;,1, 2, 3, 4, 5 ,;,
    ;,6, 7, 8, 9, 10,;,
    ;,11,12,13,14,15,;,
    ;,16,17,18,19,20,;,
    ;,21,22,23,24,25,;,
    ;,;, ;, ;, ;, ;, ;,
}
*/

struct node
{
    int side;
    int move;
    int place;
    struct node * prev;
    struct node * next;
};

void append(struct node **, int, int, int);
void display(struct node *);
void delete(struct node **, int);

//Constants of pieces, border and empty places
enum { NOUGHTS, CROSSES, BORDER, EMPTY };
//Constants for human/computer win or draw
enum { HUMANWIN, COMPWIN, DRAW };

const int directions[4] = { 1, 7, 6, 8 };

int moveCount = 0;

//Values of original places to play
const int ConvertTo49[25] = {
    8, 9, 10,11,12,
    15,16,17,18,19,
    22,23,24,25,26,
    29,30,31,32,33,
    36,37,38,39,40
};

void append(struct node ** list, int side, int move, int place)
{
    struct node *temp , *current = *list;

    if(*list == NULL)
    {
        *list = (struct node *) malloc(sizeof(struct node));
        (*list) -> prev = NULL;
        (*list) -> side = side;
        (*list) -> move = move;
        (*list) -> place = place;
        (*list) -> next = NULL;
    }
    else
    {
        while(current -> next != NULL)
            current = current -> next;

        temp = (struct node *) malloc(sizeof(struct node));
        temp -> side = side;
        temp -> move = move;
        temp -> place = place;
        temp -> next = NULL;
        temp -> prev = current;
        current -> next = temp;
    }
}

void delete(struct node ** list, int num)
{
    struct node *temp = *list;

    while(temp != NULL)
    {
        if(temp -> move == num)
        {
            if(temp == *list)
            {
                *list = (*list) -> next;
                (*list) -> prev = NULL;
            }
            else
            {
                if(temp -> next == NULL)
                    temp -> prev -> next = NULL;
                else
                {
                    temp -> prev -> next = temp -> next;
                    temp -> next -> prev = temp -> prev;
                }
                free(temp);
            }
            return ;
        }
        temp = temp -> next;
    }
    printf("Element %d not found in the supplied list \n\n", num);
}

//Loop through a direction until a border square is hit
int GetNumForDir(int startSq, const int dir, const int *board, const int us)
{
    int found = 0;

    while(board[startSq] != BORDER)
    {
        if(board[startSq] != us)
        {
            break;
        }
        found++;
        startSq += dir;
    }
    return found;
}

//Loop through directions and find four in a row
int FindFourInARow(const int *board, const int ourIndex, const int us)
{
    int dirIndex = 0;
    int dir = 0;
    int countFour = 1;

    for(dirIndex = 0; dirIndex < 4; ++dirIndex)
    {
        dir = directions[dirIndex];
        countFour += GetNumForDir(ourIndex + dir, dir, board, us);
        countFour += GetNumForDir(ourIndex + dir * -1, dir * -1, board, us);
        if(countFour == 4)
        {
            break;
        }
        countFour = 1;
    }
    return countFour;
}

//Set up board structure
void InitialiseBoard(int *board)
{
    //index variable
    int index;

    //set whole board to border squares initially
    for(index = 0; index < 49; ++index)
    {
        board[index] = BORDER;
    }

    //Set up empty squares for placing pieces
    for(index = 0; index < 25; ++index)
    {
        board[ConvertTo49[index]] = EMPTY;
    }
}

//Print the board
void PrintBoard(const int *board)
{
    //index variable
    int index;

    //Set pieces and places
    char pceChars[] = "OX| ";

    printf("\n\n\n");

    //Loop through and only draw the actual playable squares, leaving out the border squares
    for(index = 0; index < 25; ++index)
    {
        //Put lines inbetween places
        if(index==1 || index==2 || index==3 || index==4 ||
           index==6 || index==7 || index==8 || index==9 ||
           index==11 || index==12 || index==13 || index==14 ||
           index==16 || index==17 || index==18 || index==19 ||
           index==21 || index==22 || index==23 || index==24)
        {
            printf(" |");
        }

        //Put space at start to space out board from edge of screen
        if(index==0)
        {
            printf("        ");
        }

        //Print new line every 5 places
        if(index!= 0 && index==5 || index==10 || index==15 || index==20)
        {
            printf("\n");
            printf("        -------------------");
            printf("\n");
            printf("        ");
        }

        //Print each playable piece
        printf(" %c", pceChars[board[ConvertTo49[index]]]);     
    }
    printf("\n\n");
}

//Check if board has empty spaces
int HasEmpty(const int *board)
{
    int index;

    for(index = 0; index < 25; ++index)
    {
        if(board[ConvertTo49[index]] == EMPTY) return 1;
    }

    return 0;
}

//Function for making move of current player
void MakeMove(int *board, const int sq, const side)
{   
    //set board place that either player decides
    board[sq] = side;   
}

void UndoMove(int *board, int sq, int side)
{   
    //set board place that either player decides
    board[sq] = side;   
}


//Help the computer find a winning move
int GetWinningMove(int *board, const int side)
{
    int ourMove = -1;
    int winFound = 0;
    int index = 0;

    for(index = 0; index < 25; ++index)
    {
        if(board[ConvertTo49[index]] == EMPTY)
        {
            ourMove = ConvertTo49[index];
            board[ourMove] = side;

            if(FindFourInARow(board, ourMove, side) == 4)
            {
                winFound = 1;
            }
            board[ourMove] = EMPTY;
            if(winFound == 1)
            {
                return ourMove;
            }
            ourMove = -1;
        };
    }
    return ourMove;
}

//Get computer player move
int GetComputerMove(int *board, const int side)
{
    int index;
    int numFree = 0;
    int availableMoves[25];
    int randMove = 0;

    //Set random number to randomly run a function
    int randFunction = 0;
    randFunction = (rand() % 2);

    //Go for the winning move
    randMove = GetWinningMove(board, side);
    if(randMove != -1)
    {
        return randMove;
    }

    //If random function is 1, stop any winning move from the human
    if(randFunction == 1)
    {
        randMove = GetWinningMove(board, side ^ 1);
        if(randMove != -1)
        {
            return randMove;
        }
    }

    randMove = 0;
    //Loop through all squares and put piece in random place
    for(index = 0; index < 25; ++index)
    {
        if(board[ConvertTo49[index]] == EMPTY)
        {
            availableMoves[numFree++] = ConvertTo49[index];
        };
    }

    randMove = (rand() % numFree);
    return availableMoves[randMove];
}


//Get human player move
int GetHumanMove(const int *board)
{
    //Array for user input
    int userInput;
    char term;
    //Start moveOK at 0
    int moveOK = 0;
    int move = -1;

    //Loop through until move being made is valid
    while(moveOK == 0)
    {

        printf("\n");

        //Ask user for input of 1-9
        printf("Please enter a place on the board to make your move, from 1 to 25: ");
        //Make sure that user doesnt enter long string with number on the end that eventaully passes tests in the while loop
        scanf("%d%c", &userInput, &term);

        if(term != '\n')
        {
            move = -1;
            printf("This is not a number\n");
            continue;
          }

        //Check if input is in proper range
        if(userInput < 1 || userInput > 25)
        {
            move = -1;
            printf("Invalid Range\n");
            continue;
        }

        //decrement move to get the array location of the playable places
        userInput--;

        //Check if place selected is already taken
        if(board[ConvertTo49[userInput]] != EMPTY)
        {
            move = -1;
            printf("Square not available\n");
            continue;
        }

        //Set move ok to 1 after passing all the tests
        moveOK = 1;
    }

    //Print move being made and return it
    printf("\nHuman making Move at square: %d\n",(userInput+1));
    return ConvertTo49[userInput];
}

//Run Game function
void RunGame()
{
    struct node *list;
    list = NULL;

        //Display
    printf("\n\n     Noughts And Crosses Game:");
    printf("\n     =========================");

    //game over variable
    int gameOver = 0;   
    //Start on noughts side
    int side = NOUGHTS; 
    //Record last move
    int lastMoveMade = 0;   
    //Create new board
    int board[49];
    //Count of first move
    int initialMove = 0;

    //Initialise the Board
    InitialiseBoard(&board[0]);

    //Print the board to screen
    PrintBoard(&board[0]);

    //While game isn't over
    while(!gameOver)
    {
        initialMove++;
        //Human
        if(side==NOUGHTS)
        {
            //Ask to undo move after first move played
            if(initialMove > 1)
            {
                while(list -> prev != NULL)
                        list = list -> prev;
                int undo;
                printf("\n\nWould you like to undo your move? \nEnter 2 to undo or any other key/s to continue: ");
                scanf("%d", &undo);
                if( undo == 2)
                {
                    while(list -> next != NULL)
                        list = list -> next;

                    char pceChars[] = "OX| ";
                    //printf(" %c", pceChars[board[ConvertTo49[index]]]);

                    int count = 2;
                    while(count != 0)
                    {                       
                        UndoMove(&board[0], list -> place, EMPTY);                      
                        list = list -> prev;
                        delete(&list, list -> next -> move);
                        moveCount--;
                        count--;
                    }
                    PrintBoard(&board[0]);
                }
            }
                //Make new move for human
                lastMoveMade = GetHumanMove(&board[0]);
                MakeMove(&board[0], lastMoveMade, side);
                moveCount++;
                int humanMove;
                //Find the position the computer is making on the 5x5 board
                for(humanMove = 0; humanMove < 25; ++humanMove)
                {
                    if(ConvertTo49[humanMove] == lastMoveMade)
                    {
                        break;
                    }
                }               
                append(&list, 0, moveCount, lastMoveMade);
                side = CROSSES;

        }
        //Computer
        else
        {
            //Make move for computer
            lastMoveMade = GetComputerMove(&board[0], side);            
            MakeMove(&board[0], lastMoveMade, side);
            int compMove;
            //Find the position the computer is making on the 5x5 board
            for(compMove = 0; compMove < 25; ++compMove)
            {
                if(ConvertTo49[compMove] == lastMoveMade)
                {
                    break;
                }
            }
            printf("\nComputer making move at square: %d", compMove+1);
            moveCount++;
            append(&list, 1, moveCount, lastMoveMade);          
            side = NOUGHTS;
            PrintBoard(&board[0]);
        }

        //If three in a row exists, game over
        if(FindFourInARow(board, lastMoveMade, side ^ 1) == 4)
        {                       
            gameOver = 1;
            if(side == NOUGHTS)
            {
                printf("\nGame Over!\n");
                printf("Computer Wins! :(\n");
            }
            else
            {               
                PrintBoard(&board[0]);
                printf("\nGame Over!\n");
                printf("Human Wins! :D\n");
            }
        }

        //If no more moves, game is a draw      
        if(!HasEmpty(board))
        {
            PrintBoard(&board[0]);
            printf("\n\nGame Over!\n");
            gameOver = 1;
            printf("It's a draw! :/\n");
        }
    }

    /*
    *
    *    Match Replay
    *   
    */
    int answer; 
    printf("\n\nWould you like to replay the match? \nEnter 1 to replay or any other key/s to quit: ");
    scanf("%d", &answer);

    if(answer == 1)
    {
        InitialiseBoard(&board[0]);
        while ( list != NULL )
        {

            MakeMove(&board[0], list -> place, list -> side);   
            PrintBoard(&board[0]);
            char who[7];
            if(list -> side == 0)
            {
                strcpy(who, "NOUGHTS");
                //who = "NOUGHTS";
            }
            else
            {
                strcpy(who, "CROSSES");
                //who = "CROSSES";
            }
            int moveMade;
            //Find the position the computer is making on the 5x5 board
            for(moveMade = 0; moveMade < 25; ++moveMade)
            {
                if(ConvertTo49[moveMade] == list -> place)
                {
                    break;
                }
            }
            printf ("\nMove:%2d    Side:%8s    Place: %d\n", list -> move, who, moveMade+1);
            Sleep(2000);
            list = list -> next ;
        }
    }
    else
    {
        printf("\nGood Game! See ya!\n");
        getch();
    }   
}



int main()
{
    //Create random number
    srand(time(NULL));

    //Call Run Game to play
    RunGame();

    return 0;
}

Ответы [ 2 ]

1 голос
/ 27 марта 2019

Просто прочитайте страницу руководства:

   The  format  string consists of a sequence of directives which describe
   how to process the sequence of input characters.  If  processing  of  a
   directive  fails,  no  further  input  is read, and scanf() returns.  A
   "failure" can be either of the following: input failure,  meaning  that
   input  characters  were  unavailable, or matching failure, meaning that
   the input was inappropriate (see below).

Вы не проверяете возвращаемое значение scanf(). Поэтому вы пытаетесь все время сканировать одну и ту же не цифру и цифру.

1 голос
/ 27 марта 2019

Ввод, который не соответствует вашему формату, не будет использован scanf. Остается во входном потоке. Поэтому, когда вы набираете букву, она не соответствует требуемому номеру %d и остается во входном потоке.

scanf("%d%c", &userInput, &term);

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

char *line = NULL;
size_t len = 0;

while ((read = getline(&line, &len, stdin)) != -1) {
    // Do something with line

    int scanned = sscanf(line, "%d%c", &userInput);

    if (scanned == EOF || scanned != 2) {
        printf("Line failed to match\n");
    }
}
...