当输入字母字符时,Tic-Tac-Toe用户输入无休止地循环

问题描述 投票:1回答:2

所以我在C中制作一个tic tac toe游戏,我遇到了一个问题,我要求用户通过按下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;
}
c arrays console-application doubly-linked-list
2个回答
1
投票

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");
    }
}

1
投票

只需阅读手册页:

   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()的返回值。因此,您尝试始终扫描相同的非数字作为数字。

© www.soinside.com 2019 - 2024. All rights reserved.