Thread: Can't stop while loop

  1. #1
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174

    Can't stop while loop

    I'm trying to create a connect four game, but I've ran into a problem while trying to read valid user input.

    Code:
    // 4 in a row game
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define NUM_ROWS 6
    #define NUM_COLS 7
    
    #define NO_PLAYER 0
    #define PLAYER_1 1
    #define PLAYER_2 2
    
    #define TRUE 1
    #define FALSE 0
    
    void printBoard(int board[NUM_COLS][NUM_ROWS]);
    void prompt(int player);
    int input(int board[NUM_COLS][NUM_ROWS], int player);
    
    int main(int argc, char **argv) {
        int board[NUM_COLS][NUM_ROWS] = {{NO_PLAYER}}; //(a,b) = (row,col)
        int round = 1;
        int player;
        
        printBoard(board);
        
        player = (round-1)%2 + 1;
        prompt(player);
        while (input(board, player) == FALSE) {
            printf("(!) Invalid input\n\n");
            prompt(player);
        }
    
        return EXIT_SUCCESS;
    }
    
    
    void printBoard(int board[NUM_COLS][NUM_ROWS]) {
        int i,j;
        
        printf("\n");
        for (i=0; i<NUM_ROWS; i++) {
            printf("\t\t");
            for (j=0; j<NUM_COLS; j++) {
                if (board[i][j] == NO_PLAYER)
                    printf(".");
                else if (board[i][j] == PLAYER_1)
                    printf("X");
                else if (board[i][j] == PLAYER_2)
                    printf("O");
                else
                    printf("E");
                printf("  ");       
            }    
            printf("\n");
        }
        printf("\n\t\t");
        for (i=0; i<NUM_COLS; i++) {
            printf("%d  ", i+1);
        }
        printf("\n\n");
    
        return;
    }
    
    void prompt(int player) {
        printf("Player %d, enter position 1-%d: ", player, NUM_COLS);
        return;
    }
    
    int input(int board[NUM_COLS][NUM_ROWS], int player) {
        int position;
        int numScan;
        int i;
        
        numScan = scanf("%d", &position);
        if (numScan != 1 || position < 1 || position > NUM_COLS
            || board[position][0] != NO_PLAYER) 
            return FALSE;
        
        for (i=NUM_ROWS-1; i>=0; i--) {
            if (board[position][i] == NO_PLAYER)
                board[position][i] == player;
        }
        
        return TRUE;
    }
    If the user enters an invalid number, then it works fine, but if a character is entered, the while loop in main never breaks. How can I fix this?

  2. #2
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    When a character is entered, "scanf()" fails and leaves that character in the input buffer. Then it loops, tries to read the same input, fails, etc. That is the cause of your infinite loop in that situation.

    One option would be to flush the input buffer if "scanf()" fails, so you start the next iteration of the loop with a clean slate.

    Also, check line 83 - you have a mistake there.

    Code:
    main.c||In function 'input':|
    main.c|83|warning: statement with no effect|
    ||=== Build finished: 0 errors, 1 warnings ===|

  3. #3
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Quote Originally Posted by Matticus View Post
    When a character is entered, "scanf()" fails and leaves that character in the input buffer. Then it loops, tries to read the same input, fails, etc. That is the cause of your infinite loop in that situation.

    One option would be to flush the input buffer if "scanf()" fails, so you start the next iteration of the loop with a clean slate.
    I see. Sadly, I don't know how to do that.

    Quote Originally Posted by Matticus View Post
    Also, check line 83 - you have a mistake there.

    Code:
    main.c||In function 'input':|
    main.c|83|warning: statement with no effect|
    ||=== Build finished: 0 errors, 1 warnings ===|
    Thanks, I noticed it too, and I would've spotted it even earlier if I used the -Wall flag

  4. #4
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    This might suit your needs:

    Code:
        numScan = scanf("%d", &position);
        if(numScan != 1)                // if "scanf" failed,
            while(getchar() != '\n')    //   eat up all characters in the
                ;                       //   input buffer up to the newline
    Review that carefully and make sure you understand how it works. If you're not certain, just ask for clarification.

    This is not the most elegant approach, and will not cover all circumstances (e.g. if the user just presses enter without typing anything), but should do you well for now.

    In the future, you might consider making a separate function just for reading a number from stdin. This can use the "fgets()" and "sscanf()" combination to read the input without having to worry about the issues usually encountered with "scanf()" alone.

  5. #5
    Registered User hk_mp5kpdw's Avatar
    Join Date
    Jan 2002
    Location
    Northern Virginia/Washington DC Metropolitan Area
    Posts
    3,817
    Looks like you need to increment round in the loop and also reset player so that it switches back and forth.
    "Owners of dogs will have noticed that, if you provide them with food and water and shelter and affection, they will think you are god. Whereas owners of cats are compelled to realize that, if you provide them with food and water and shelter and affection, they draw the conclusion that they are gods."
    -Christopher Hitchens

  6. #6
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Quote Originally Posted by Matticus View Post
    This might suit your needs:

    Code:
        numScan = scanf("%d", &position);
        if(numScan != 1)                // if "scanf" failed,
            while(getchar() != '\n')    //   eat up all characters in the
                ;                       //   input buffer up to the newline
    Review that carefully and make sure you understand how it works. If you're not certain, just ask for clarification.

    This is not the most elegant approach, and will not cover all circumstances (e.g. if the user just presses enter without typing anything), but should do you well for now.
    I think I see what it's doing. It's going to be consuming each character in the input buffer including the new line character, but breaks from the while loop when that happens so it stops waiting for more chars by that point.


    Quote Originally Posted by Matticus View Post
    In the future, you might consider making a separate function just for reading a number from stdin. This can use the "fgets()" and "sscanf()" combination to read the input without having to worry about the issues usually encountered with "scanf()" alone.
    Could I get some help with this please? I'm willing to try and do it, but I'm rusty with each of those functions.


    Quote Originally Posted by hk_mp5kpdw View Post
    Looks like you need to increment round in the loop and also reset player so that it switches back and forth.
    I wasn't quite done at that point. I now have

    Code:
    // 4 in a row game
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define NUM_ROWS 6
    #define NUM_COLS 7
    
    #define NO_PLAYER 0
    #define PLAYER_1 1
    #define PLAYER_2 2
    
    #define TRUE 1
    #define FALSE 0
    
    void printBoard(int board[NUM_ROWS][NUM_COLS]);
    void prompt(int player);
    int input(int board[NUM_ROWS][NUM_COLS], int player, int *play);
    int gameOver(int board[NUM_ROWS][NUM_COLS], int play);
    int win(int board[NUM_ROWS][NUM_COLS], int play);
    
    int main(int argc, char **argv) {
        int board[NUM_ROWS][NUM_COLS] = {{NO_PLAYER}}; //(a,b) = (row,col)
        int round = 1;
        int player;
        int play = 0;
        int *playP = &play;
        
        printBoard(board);
        
        while (gameOver(board, play) == FALSE) {
            player = (round-1)%2 + 1;
            prompt(player);
            while (input(board, player, playP) == FALSE) {
                printf("(!) Invalid input\n\n");
                prompt(player);
            }
            printBoard(board);
            round++;
        }
    
        return EXIT_SUCCESS;
    }
    
    
    void printBoard(int board[NUM_ROWS][NUM_COLS]) {
        int i,j;
        
        printf("\n");
        for (i=0; i<NUM_ROWS; i++) {
            printf("\t\t");
            for (j=0; j<NUM_COLS; j++) {
                if (board[i][j] == NO_PLAYER)
                    printf(".");
                else if (board[i][j] == PLAYER_1)
                    printf("X");
                else if (board[i][j] == PLAYER_2)
                    printf("O");
                else
                    printf("E");
                printf("  ");       
            }    
            printf("\n");
        }
        printf("\n\t\t");
        for (i=0; i<NUM_COLS; i++) {
            printf("%d  ", i+1);
        }
        printf("\n\n");
    
        return;
    }
    
    void prompt(int player) {
        printf("\t\tPlayer %d: ", player);
        return;
    }
    
    int input(int board[NUM_ROWS][NUM_COLS], int player, int *play) {
        int position;
        int numScan;
        int i;
        
        numScan = scanf("%d", &position);
        if (numScan != 1 || position < 1 || position > NUM_COLS
            || board[0][position] != NO_PLAYER) 
            return FALSE;
            
        *play = position;
        
        for (i=NUM_ROWS-1; i>=0; i--) {
            if (board[i][position-1] == NO_PLAYER) {
                board[i][position-1] = player;
                break;
            }
        }
        
        return TRUE;
    }
    
    int gameOver(int board[NUM_ROWS][NUM_COLS], int play) {
        int i;
        
        for (i=0; i<NUM_COLS; i++)
            if (board[0][i] == NO_PLAYER) break;
        if (i == NUM_COLS && board[0][i-1] != NO_PLAYER) return TRUE;
        
        if (win(board, play) == TRUE) return TRUE;
        
        return FALSE;
    }
    
    int win(int board[NUM_ROWS][NUM_COLS], int play) {
    
        return FALSE;
    }

  7. #7
    Registered User
    Join Date
    Mar 2012
    Posts
    33
    You should look up documentation on fgets and sscanf, but you could do something like this.

    Code:
    int getInt(){    
        const int STR_SIZE = 20;
        int integer;
        char stringInt[STR_SIZE];
        fgets(stringInt, STR_SIZE, stdin);
        sscanf(stringInt, "%d", &integer);
        return integer;
    }

  8. #8
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    Quote Originally Posted by atac View Post
    You should look up documentation on fgets and sscanf, but you could do something like this.

    Code:
    int getInt(){    
        const int STR_SIZE = 20;
        int integer;
        char stringInt[STR_SIZE];
        fgets(stringInt, STR_SIZE, stdin);
        sscanf(stringInt, "%d", &integer);
        return integer;
    }
    if scanf fails you will return value of uninitialized variable integer
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  9. #9
    Registered User
    Join Date
    Mar 2012
    Posts
    33
    Quote Originally Posted by vart View Post
    if scanf fails you will return value of uninitialized variable integer
    I don't think he's gonna have that problem for whats he's doing.

  10. #10
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,787
    Not sure if it's a bad thing. It's quite common throughout history. A great example is this:

    https://www.youtube.com/watch?v=36tsp2rTACo

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Can't stop endless loop?...
    By tvred in forum C++ Programming
    Replies: 4
    Last Post: 03-24-2010, 10:27 PM
  2. why the first loop doesnt stop..
    By transgalactic2 in forum C Programming
    Replies: 1
    Last Post: 06-20-2009, 07:07 AM
  3. how to stop the loop
    By amits in forum C++ Programming
    Replies: 5
    Last Post: 05-19-2004, 10:46 AM
  4. when a while loop will stop ?
    By blue_gene in forum C Programming
    Replies: 13
    Last Post: 04-20-2004, 03:45 PM
  5. loop stop
    By pode in forum C++ Programming
    Replies: 8
    Last Post: 12-08-2001, 08:36 PM