Thread: About the function rand()

  1. #16
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    whiteflags,

    Thanks. I changed the code. It looks like this now. (I tried to update my previous post but the editing is disabled). I will later solve the limits problem because as you said the attempts, number of players and players' ID's are all integers.

    Code:
    // get players
        int nplayers = -1;
    
    
        printf("Enter number players (or 0 to exit) ");
    
    
        while (!(nplayers > -1)) {
            int tmp;
            if (tmp = scanf("%d", &nplayers) != 1) {
                int c;
                while ((c = getchar()) != '\n' && c != EOF);
                printf("Enter a valid number: ");
                continue;
            }
            if (nplayers == 0) {
                getchar();
                printf("Do you want to exit ? (Y/N) ");
                char answer;
                scanf("%c", &answer);
                if (answer == 'Y' || answer == 'y') {
                    break;
                } else {
                    nplayers = -1;
                    printf("Enter number players: ");
                }
            } else if (nplayers < 0) {
                printf("Try again: ");
            }
    
    
        }
    
    
    
    
        putchar('\n');
    
    
        if (nplayers == 0) {
            printf("No playing today ..");
            return 0;
        }

  2. #17
    Registered User
    Join Date
    Apr 2007
    Posts
    141
    The basic idea with the linear congruential number generators that are the core of rand() is to create a sequence of numbers with a very large period. That is the sequence doesn't repeat itself until billions of times or more depending on the size of the internal state. To understand why this is true would require knowledge of finite fields, a topic not normally taught to undergraduate CS majors, though I could be wrong.

    One way to think of it is via clock arithmetic. Let's say you start with a 7, which is relatively prime to 12. If you keep adding 7 to itself modulo 12, how many unique numbers do you get until it repeats? (12 since 7 is relatively prime to 12). The linear congruential generator is doing the same thing but with powers in a finite field. Each new number is the old number times a primitive factor, modulo a primitive polynomial.

    The xor generator I provided earlier is actually based on this concept. The shifts are just a cheap way of doing multiplies. Since the topic is somewhat deep, you could focus your research on how well your randomizer performs via some standard randomness tests. If it passes those tests, it doesn't matter how it works, unless you are in to cryptography.

    I think I've actually used this test to beef up the quality of my parallel random number generators, via some trial and error processing. I ended up randomizing both the index used for the seed array as well as running an xor type randomizer across a random set of state variables. All chosen by thousands of independent processors in a bank of GPUs.

    Pseudorandom Number Sequence Test Program

  3. #18
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    SevenThunders ,
    Thanks for your detailed answer. I have selected the subject of my project (any workable game) for the end of our course. We started two weeks ago, so I am really a beginner in computer and programming. For now, I only use topics of C which have been handled during the lessons. I start to understand the code and its structure a little better but we are not allowed to copy and modify any code. I am glad we were free to choose our projects. There's another condition: the program should be edited continuously as we go one. So, if learn a new aspect, we should use it.

    Here follows an update. If you could check it to criticize it, to comment on it, or to make some suggestions. Just like @whiteflags already did.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    
    int getnumber(void); // to read a number from line
    void setplayers (int plist [][2], int SIZE); // create players
    void printplayers(int plist[][2], int SIZE); // print players
    void play(int plist[][2], int SIZE); // start the game
    void printstats(int plist[][2], int SIZE); // print results
    
    
    int main (void) {
        srand(time(NULL));
    
    
        // get players
        printf(" How many players? ");
        int nplayers = getnumber();
    
    
        // get ID's of players
        int players[nplayers][2];
        setplayers (players, nplayers);
    
    
        //print list players
        putchar('\n');
        printplayers(players, nplayers);
    
    
        // let's play
        play(players, nplayers);
    
    
        // print the results
        printf(" Player ID \t Score\n----------------------\n");
        printstats(players, nplayers);
    
    
    
    
        return 0;
    }
    
    
    /**
    * getnumber(): int
    * retry if not a real number
    * retry if input <= 0
    */
    int getnumber () {
    
    
        while (1) {
            char input[11]; // 11 for max int
            fgets(input,11,stdin);
    
    
            int n;
            char c;
            int valid = sscanf(input, " %d %c", &n, &c);
            if (valid == 1) {
                if (n <= 0) {
                    printf(" Try again: ");
                } else {
                    return n;
                }
            } else
                printf(" Try again: ");
        }
    }
    
    
    /**
    * setplayers(list of players): void
    * assign an ID to players, so they can be identified within the game
    * check if ID is unique
    * Beep & retry if ID already exists
    */
    void setplayers(int players[][2], int n) {
        int nplayers = n;
        int i;
    
    
        for (i = 0; i < nplayers; i++) {
            printf(" Enter Player %d ID: ", i+1);
    
    
            int id = getnumber();
    
    
            if (i == 0) {
                players[i][0] = id;
            } else {
                int j;
                int exist;
                for (j = 0; j < i; j++) {
                    if (id == players[j][0]) {
                        exist = 1;
                        printf(" %c%c [ID %d already exists]\n",175,7, id);
                        i--;
                        break;
                    } else {
                        exist = 0;
                    }
                }
    
    
                if (exist == 0) {
                    players[i][0] = id;
                }
            }
        }
    
    
    }
    
    
    /*
    * printplayers (list of players): void
    * print player's ID's in the list
    */
    void printplayers(int players[][2], int n) {
        int nplayers = n;
        int i;
        for (i = 0; i < nplayers; i++) {
            printf(" Player %d [%d]\n", i+1, players[i][0]);
        }
    }
    
    
    /*
    * play (list of players)
    * to guess a number between 100 and 1
    */
    void play (int players[][2], int n) {
        int nplayers = n;
        int i;
        for (i = 0; i < nplayers; i++) {
            /**
            * 2.0 instead of 100 to make the tests easier
            */
            int tmp_rand = (int) (rand() * 2.0 / (RAND_MAX + 1.0)) + 1;
    
    
            int attempts = 0;
    
    
            printf("\n           Player %d\n\n "
                   "Guess the number between 1 and 100 :\n",
                   players[i][0]);
    
    
            while (1) {
                int shot = getnumber();
    
    
                ++attempts;
    
    
                if (shot == tmp_rand) {
                    printf("\n Cool, player %d! You guessed the number"
                           " in %d shots!\n\n", players[i][0], attempts);
                    players[i][1] = attempts;
                    break;
                } else if (shot < tmp_rand) {
                    puts(" Too low!");
                } else {
                    puts(" Too high!");
                }
            }
        }
    }
    
    
    /**
    * printstats(list players with their id's and scores): void
    * print players and their scores
    * score: how many attempts to make find the number between 100 and 1
    */
    
    
    void printstats(int players[][2], int n) {
        int nplayers = n;
        int i;
        for (i = 0; i < nplayers; i++) {
            printf(" %9d \t %d\n", players[i][0], players[i][1]);
        }
    }

  4. #19
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    This:
    Code:
    int valid = sscanf(input, " %d %c", &n, &c);
    if (valid == 1) {
        if (n <= 0) {
            printf(" Try again: ");
        } else {
            return n;
        }
    } else
        printf(" Try again: ");
    can be simplified to:
    Code:
    if (sscanf(input, " %d %c", &n, &c) == 1 && n > 0) {
        return n;
    } else {
        printf(" Try again: ");
    }
    Also, instead of writing at the start of several functions:
    Code:
    int nplayers = n;
    why not rename the parameter n to nplayers?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #20
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    laserlight,

    Thank you very much. I will correct that. I am also going to use three files now: (game.h) to declare functions to use in (main.c) and in (game.c) where the functions are programmed. The program will keep running as long as there's no 1 winner.

    I have now a problem.

    As you probably have remarked I am using an array of players [x][2]. Where [x] stands for amount players, and second array 2 to store player's ID and score. I am going to expand it with the names of players, so that the names should be given by user while the ID's are generated automatically.

    The problem now is that I have to edit the code everywhere . I am trying to figure out how to program it so that the code will be more efficient.

  6. #21
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    Perhaps it's time to use a structure instead of simple arrays?
    Code:
    #define NAME_SIZE 25
    struct playerInfo 
    {
        int id_;
        int score_;
        char name_[NAME_SIZE];
    };
    There are benefits to this approach. For one I think the code is clearer. Instead of assigning random values to an array element with an unhelpful name, you can write something like player[i].score_, player[i].id_, and immediately associate a player with a name with player[i].name_.
    The problem now is that I have to edit the code everywhere
    That should be alleviated somewhat when you put the player's info in one place like I am suggesting.

    Further reading: Structures in C - Tutorial - Cprogramming.com

  7. #22
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    whiteflags,
    Thank you but we didn't handle those structs yet, and that will be in chapter about pointers and data structures.
    I have a working program now. I added another array instead of editing the first one. I will improve it before posting the newest update. I still have some issues with stdin and how to fix the buffers. There's a general solution, I think, but it uses functions I don't understand like realloc, malloc and free. Some of them do work alongside pointers.
    Last edited by Carnotter; 04-05-2015 at 07:12 PM.

  8. #23
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    Topics we have learned until now:

    - Editor, compiler (in both windows and Linux) and tools like man (with sections) on how to use. Different IDE's. What is source file and object file. Other references, how to search, ask questions and read answers.
    - Function printf() how to use it, make file and compile. White spaces and what things compiler cares about.
    - Make own void function using printf():
    Code:
    void printmyname() {
      printf("Hello, I am ..");
    }
    - Variables: declare, initialize int, double, char and string (char str[100]).
    - While, do while and for loops to print some text repeatedly.
    - How access strings with their index: str[0] for example.
    - Organize code by using blocks and nested blocks.
    - Functions scanf(), puts(), putchar() and getchar().
    - Own functions with return value: look again at main() function.
    - Global and local variables, scope and visibility.
    - Random numbers.
    - Library math.h, sin() ans cos() how to use escape code ! doesn't work for me on windows!
    - Organize code 2: what, why and how of #include, #define. How to make own library.

    And some other theoretical issues what algorithm, program, instruction, processor, programming language, C .. are.

    Project:

    Make a program, prove you understood things you learned, you can apply, or at least reproduce.

    My choice's topic: to use srand() and rand() and make a game. Program should be "extensible" as we go on by applying the new topics we learn.

    My program (just game at the moment). Designed for multiple users to play guess the number game. The winner is the one with minimum of guesses, of there are many, keep playing until there's just one winner.

    Files: main.c game.h game.c

    Request: please criticize and challenge.

    Thank you in advance.
    Last edited by Carnotter; 04-06-2015 at 10:20 PM.

  9. #24
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    Project's files:
    game.h main.c game.c


    1. game.h

    Code:
    /********** FUNCTION ARGUMENTS: **********************************************
     * 
     * int SIZE: how many players
     * int playerlist[][2]: 2D list of players, each player with ID and score
     * char pnames[][100]:  2D list of names, each name can be stored in 100 chars
     *
     **********/
    
    
    /**
     * to read an int in its range
     * retry if no real number is given
     * !! function will return max of int if overflow !! program exit.
     */
    int getnumber(void);
    
    
    /**
     * auto ID generator
     * username register
     */
    void setplayers(int playerlist[][2], char pnames[][100], int SIZE);
    
    
    /*
     * to print ranking list of registered players
     */
    void getranking(int playerlist[][2],char pnames[][100], int SIZE);
    
    
    /*
     * to guess a number between x and 1
     * print average attempts
     * call function printavrgscore() 
     * call function printstats()
     */
    void play(int playerlist[][2], char pnames[][100], int SIZE);
    
    
    /**
     * to print average attempts
     */
    float printavrgscore(int playerlist[][2], int SIZE);
    
    
    /*
     * to sort player list by attempts
     *
     */
    void sortplayers(int players[][2], char pnames[][100], int nplayers);
    
    
    /**
     * to process the game
     * check if one winner or more players 
     * with equal minimum of attempts
     */
    void process_game(int playerlist[][2], char pnames[][100], int SIZE);
    2. main.c

    Code:
    #include <stdio.h>
    #include <string.h>
    
    
    #include "game.h"
    
    
    int WINNER;
    
    
    int main (void) {    
        srand(time(NULL));
    
    
        // get (nplayers) number of players
        printf(" How many players? ");
        int nplayers = getnumber();
        
        WINNER = nplayers;
    
    
        /*
         * declare arrays and initialize to default values
         * register the players (ID and username)
         */ 
        int players[nplayers][2];
        char pnames[nplayers][100];
        
        int i;
        for (i = 0; i < nplayers; i++) {
            players[i][0] = 0;
            players[i][1] = 0;
            strcpy(pnames[i], " ");
        }
        
        setplayers (players, pnames, nplayers);
    
    
        //list players
        putchar('\n');
        getranking(players, pnames, nplayers);
    
    
        // let's play
        if (nplayers == 1) {
            play(players, pnames, 1);
        } else {
            while (WINNER != 1) {
                play(players, pnames, WINNER);
            }
        }
    
    
        return 0;
    }
    3. game.c

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    
    #include "game.h"
    
    
    #define MY_MAX_RANDOM 2.0
    #define TRUE 1
    
    
    
    
    int WINNER;
    
    
    
    
    int getnumber() {
        while (TRUE) {
            char input[11];
            fgets(input,11,stdin);
    
    
            int number;
            char c;
            if ( (sscanf(input, " %d %c", &number, &c) == 1) && (number > 0) ) {
                return number;
            } else {
                if (strlen(input) == (10)) {
                    int ch;
                    while (ch = getchar() != '\n' && ch != EOF);
                }
                printf(" Try again: ");
            }
    
    
        }
    }
    
    
    
    
    void setplayers(int players[][2], char pnames[][100], int nplayers) {
        int i, NAME_OK;
        // auto assign the next ID
        for (i = 0; i < nplayers; i++) {
            int id = i+1;
            players[i][0] = id;
        }
    
    
        //make sure player is registered
        NAME_OK = 0;
        while (!NAME_OK) {
            for (i = 0; i < nplayers; i++) {
                printf(" Username for player %d: ", players[i][0]);
                // read names and remove (new line) char
                if (fgets(pnames[i], sizeof (pnames[i]), stdin) != NULL) {
                    size_t length = strlen(pnames[i]);
                    if (length > 2 && pnames[i][length-1] == '\n') {
                        pnames[i][--length] = '\0';
                        NAME_OK = 1;
                    } else {
                        i--;
                    }
                }
            }
        }
    }
    
    
    
    
    void getranking(int players[][2], char pnames[][100], int nplayers) {
        printf(" Player ID \t Attempts \t Player\n");
        int i;
        for (i = 0; i < nplayers; i++) {
            printf(" %04d \t\t %d \t\t %s\n", players[i][0], players[i][1], pnames[i]);
        }
    }
    
    
    
    
    void play(int players[][2], char pnames[][100], int nplayers) {
        int i;
        for (i = 0; i < nplayers; i++) {
            //temporary random number
            int tmp_rand = (int) (rand() * MY_MAX_RANDOM / (RAND_MAX + 1.0)) + 1;
    
    
            int attempts = 0;
    
    
            printf("\n\n\n           Player: %s\n\n "
                   "Guess the number between 1 and %d :\n",
                   pnames[i], (int)MY_MAX_RANDOM);
    
    
            while (TRUE) {
                //get the guess
                int shot = getnumber();
                //preemptive increase of attempts
                ++attempts;
                //check if well guessed and break the loop if it's the case
                if (shot == tmp_rand) {
                    printf("\n Cool, player %s! You guessed the number"
                           " in %d shots!\n\n", pnames[i], attempts);
                    players[i][1] = attempts;
                    break;
                } else if (shot < tmp_rand) {
                    puts(" Too low!");
                } else {
                    puts(" Too high!");
                }
            }
        }
    
    
        //print average attempts
        float avrga = printavrgscore(players, nplayers);
        printf(" Average attempts: %.2f\n\n", avrga);
    
    
        // sort players by attempts
        sortplayers(players, pnames, nplayers);
    
    
        //print the ranking
        getranking(players, pnames, nplayers);
    
    
        // process the game
        process_game(players, pnames, nplayers);
    }
    
    
    
    
    float printavrgscore(int players[][2], int nplayers) {
        float tmp = 0;
        int i;
        for (i = 0; i < nplayers; i++) {
            tmp+=players[i][1];
        }
    
    
        return (tmp/nplayers);
    }
    
    
    
    
    void sortplayers(int players[][2], char pnames[][100], int nplayers) {
        int tmp[nplayers][2];
        char ntmp[nplayers][100];
    
    
        //sort
        int i;
        for (i = 0; i < nplayers - 1; i++) {
            int j;
            for(j = 0; j < nplayers - 1; j++) {
                if (players[j][1] > players[j+1][1]) {
                    //switch ID
                    tmp[j][0] = players[j][0];
                    players[j][0] = players[j+1][0];
                    players[j+1][0] = tmp[j][0];
    
    
                    //switch attempts 
                    tmp[j][1] = players[j][1];
                    players[j][1] = players[j+1][1];
                    players[j+1][1] = tmp[j][1];
    
    
                    //switch names
                    strcpy(ntmp[j], pnames[j]);
                    strcpy(pnames[j], pnames[j+1]);
                    strcpy(pnames[j+1], ntmp[j]);
                }
            }
        }
    }
    
    
    
    
    void process_game(int players[][2], char pnames[][100], int nplayers) {
        /*
         * mia: minimum of attempts
         * the list is already sorted, so
         * players[0][1] scored mia
         */
        int mia = players[0][1];
    
    
        /*
         * assume it's the final
         * 0: just one player with mia
         */
        int draw = 0;
    
    
        /*
         * check if other players got also a mia
         * if so, increase the draw
         */
        int i;
        for (i = 0; i < nplayers - 1; i++) {
            if (players[i][1] == mia) {
                if (players[i][1] == players[i+1][1]) {
                    draw++;
                }
            }
        }
    
    
        /*
         * if one player with mia,
         * beep and print this winner
         */
        if (draw == 0) {
            printf("\n ");
            for (i=0; i<20; i++) {
                printf("-");
                Sleep(100);
            }
            printf("->%c%c",7,7);
            if (mia == 1) {
                printf(" Winner: %s scored in just one shot!!",
                       pnames[0], mia);
            } else {
                printf(" Winner: %s scored in %d shots",
                       pnames[0], mia);
            }
    
    
            // set winner to 1
            WINNER = 1;
    
    
        } else {
            //play-off
            WINNER = draw + 1;
        }
    }
    Last edited by Carnotter; 04-06-2015 at 10:30 PM.

  10. #25
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,665
    A couple of immediate thoughts.
    1. Get rid of that messy global variable int WINNER;

    2. char input[11];
    > fgets(input,11,stdin);
    fgets returns NULL at EOF, but you don't check for this.
    Also, the usual way to call it would be fgets(input,sizeof(input),stdin);

    3. if (strlen(input) == (10))
    So if I type in garbage input which isn't the full line, then what?

    4. printf(" Winner: %s scored in just one shot!!",
    > pnames[0], mia);
    Excess parameters to printf.

    5. Your swap function in sortplayers() only needs a single string and a single int. You don't need to keep an entire history of all your swaps.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  11. #26
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    Hmm, here are some nitpicks because the code looks pretty durned good. I'll only comment on a single instance of a "problem" rather than every instance.

    Code:
    #include "game.h"
    Your header contains no inclusion guards. Not really a huge problem in this case, but it's a good idea to get into the habit:
    Code:
    // game.h
    #ifndef GAME_H
    #define GAME_H
    
    // Stuff
    
    #endif
    Code:
    int WINNER;
    I don't mind the global variable even though you could do away with it, but using names that are in all caps is not conventional. All cap names are typically reserved for macros in most style guidelines.

    Code:
    int players[nplayers][2];
    char pnames[nplayers][100];
    This is my single biggest problem with the code. It's not technically wrong, but rather dependent on the standard you're coding under. Variable length arrays are only supported in C99 and later, so if you're not compiling as C99 or C11, this is non-portable. But it gets worse:

    C99: VLAs have not been widely adopted in C99 compilers, so support might be spotty.
    C11: VLAs are optional. If __STDC_NO_VLA is defined you must check the macro and use a more traditional method as appropriate in portable code. This makes VLAs significantly less interesting.

    I'm not a fan of VLAs in general as they're pretty risky for the intended use case. Your code has an unchecked size coming from user input, so there's a risk of exceeding the stack size in your array definition. The problem with this is it silently invokes undefined behavior with disturbingly high probability. For toy programs it's fine, but in any production quality code that needs robustness guarantees, I'd strongly recommend forgetting that VLAs exist.

    Code:
    srand(time(NULL));
    This is a type mismatch because time_t is not strictly compatible with unsigned int. At the very least you'd want a cast, which to the best of my knowledge works across the board even though it's still not technically correct. A better general approach in my opinion is to still use the current time, but hash it into a suitable unsigned int:
    Code:
    unsigned time_seed()
    {
        time_t now = time(0);
        unsigned char *p = (unsigned char *)&now;
        unsigned seed = 0;
    
        for (size_t i = 0; i < sizeof now; i++)
        {
            seed = seed * (UCHAR_MAX + 2U) + p[i];
        }
    
        return seed;
    }
    
    ...
    
    srand(time_seed());
    There are still issues with predictability in the seed due to the nature of the current time, but I haven't really found anything portable that's superior.

    Code:
    int tmp_rand = (int) (rand() * MY_MAX_RANDOM / (RAND_MAX + 1.0)) + 1;
    Meh. This is a cutesy non-solution to rand's problems. Sure, it uses the high order bits for better randomness in a broken implementation of the linear congruential generator, but in terms of distribution it's really no better than 1 + rand() % MY_MAX_RANDOM. Further, introducing floating point just adds another level of suckage. I'd suggest defaulting to a filtering loop rather than trying to get clever about it:
    Code:
    int random_int(int min, int max)
    {
        if (min > max)
        {
            int temp = min;
            min = max;
            max = temp;
        }
    
        int r;
    
        do
        {
            r = rand();
        } while (r < min || r > max);
    
        return r;
    }
    
    ...
    
    int tmp_rand = random_int(1, 2);
    Code:
    printf("->%c%c",7,7);
    You're assuming ASCII in several places where it's unnecessary. C has escape characters for these things, such as '\a' for an audio/visual cue.
    My best code is written with the delete key.

  12. #27
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    Salem and Prelude,
    Thank you very much for your great cooperation, critic, comments and suggestions.

    First is first, I shall destroy that ugly and messy WINNER. Ugly because I understand the convention that we use capital letters for macros, and messy because it's not really needed.

    After that I will fix other troubles and shortcomings. There are other things which can be solved by using pointers but we didn't learn yet. I will apply some changes after learning this aspect in our class.

    I have a question. Why does the program exit when I type in a big number of players? It doesn't accept 200.000 for example. I guess this has something to do with memory management and/or as Prelude explained: support for dynamic arrays ?

    Kind regards,
    Last edited by Carnotter; 04-07-2015 at 12:04 PM.

  13. #28
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    Quote Originally Posted by Salem View Post
    A couple of immediate thoughts.
    1. Get rid of that messy global variable int WINNER;
    The global variable WINNER is removed. The changes:
    1) in main.c
    Code:
    // let's play
    play(players, pnames, nplayers);
    2) in game.h
    Code:
    // game.h --  inclusion guards
    
    
    #ifndef GAME_H
    #define GAME_H
    
    // ..
    
    /*
     * to guess a number between x and 1
     * call function printavrgscore()
     * call function printstats()
     * call function process_game():int and play while no single winner
     */
    void play(int playerlist[][2], char pnames[][100], int SIZE);
    
    /**
     * to process the game
     * check if one winner or more players with equal minimum of attempts
     * return number of players with equal attempts xor 1
     */
    int process_game(int playerlist[][2], char pnames[][100], int SIZE);
    
    // ...
    
    #endif
    3) in game.c
    Code:
    void play(int players[][2], char pnames[][100], int nplayers) {
        int round; //round to play
        round = nplayers;
        do {
            int i;
            for (i = 0; i < round; i++) {
                //temporary random number
                int tmp_rand = (int) (rand() * MY_MAX_RANDOM / (RAND_MAX + 1.0)) + 1;
    
    
                int attempts = 0;
    
    
                printf("\n\n\n           Player: %s\n\n "
                       "Guess the number between 1 and %d :\n",
                       pnames[i], (int)MY_MAX_RANDOM);
    
    
                while (TRUE) {
                    //get the guess
                    int shot = getnumber();
                    //preemptive increase of attempts
                    ++attempts;
                    //check if well guessed and break the loop if it's the case
                    if (shot == tmp_rand) {
                        printf("\n Cool, player %s! You guessed the number"
                               " in %d shots!\n\n", pnames[i], attempts);
                        players[i][1] = attempts;
                        break;
                    } else if (shot < tmp_rand) {
                        puts(" Too low!");
                    } else {
                        puts(" Too high!");
                    }
                }
            }
    
    
            //print average attempts
            float avrga = printavrgscore(players, round);
            printf(" Average attempts: %.2f\n\n", avrga);
    
    
            // sort players by attempts
            sortplayers(players, pnames, round);
    
    
            //print the ranking
            getranking(players, pnames, round);
            
            round = process_game(players, pnames, round);
        } while (round != 1);
    }
    
    
    int process_game(int players[][2], char pnames[][100], int nplayers) {
        /*
         * mia: minimum of attempts
         * the list is already sorted, so
         * players[0][1] scored mia
         */
        int mia = players[0][1];
    
    
        /*
         * assume it's the final
         * 0: just one player with mia
         */
        int draw = 0;
    
    
        /*
         * check if other players got also a mia
         * if so, increase the draw
         */
        int i;
        for (i = 0; i < nplayers - 1; i++) {
            if (players[i][1] == mia) {
                if (players[i][1] == players[i+1][1]) {
                    draw++;
                }
            }
        }
    
    
        /*
         * if one player with mia,
         * beep and print this winner
         */
        if (draw == 0) {
            printf("\n ");
            for (i=0; i<20; i++) {
                printf("-");
                Sleep(100);
            }
            printf("->%c%c",7,7);
            if (mia == 1) {
                printf(" Winner: %s scored in just one shot!!", pnames[0]);
            } else {
                printf(" Winner: %s scored in %d shots",
                       pnames[0], mia);
            }
    
    
            // set winner to 1
            return 1;
    
    
        } else {
            //play-off
            return draw + 1;
        }
    }
    -----

    Quote Originally Posted by Salem View Post
    2. char input[11];
    > fgets(input,11,stdin);
    fgets returns NULL at EOF, but you don't check for this.
    Also, the usual way to call it would be fgets(input,sizeof(input),stdin);

    3. if (strlen(input) == (10))
    So if I type in garbage input which isn't the full line, then what?
    Busy reading about how fgets() works -in some more details- and what "garbage input" means/implies.

    Quote Originally Posted by Salem View Post
    A couple of immediate thoughts.
    4. printf(" Winner: %s scored in just one shot!!",
    > pnames[0], mia);
    Excess parameters to printf.

    5. Your swap function in sortplayers() only needs a single string and a single int. You don't need to keep an entire history of all your swaps.
    4. Done! It should be:
    Code:
    printf(" Winner: %s scored in just one shot!!", pnames[0]);
    5. Done:
    Code:
    int j;
    for(j = 0; j < nplayers - 1; j++) {
        //temp variables
        int tmp[1][2];
        char ntmp[1][100];
    
    
        if (players[j][1] > players[j+1][1]) {
            //switch ID
            tmp[0][0] = players[j][0];
            players[j][0] = players[j+1][0];
            players[j+1][0] = tmp[0][0];
    
    
            //switch attempts
            tmp[0][1] = players[j][1];
            players[j][1] = players[j+1][1];
            players[j+1][1] = tmp[0][1];
    
    
            //switch names
            strcpy(ntmp[0], pnames[j]);
            strcpy(pnames[j], pnames[j+1]);
            strcpy(pnames[j+1], ntmp[0]);
        }
    }
    Last edited by Carnotter; 04-07-2015 at 01:58 PM.

  14. #29
    Registered User
    Join Date
    Mar 2015
    Location
    BE
    Posts
    66
    Dear Salem,

    Can you check this with me?

    You asked two questions 2 and 3.
    I don't check NULL returned by fgets() at EOF. Why should I? Max of an integer is represented by 10 characters (2 ^31 -1), so the last char is left for null-byte. This is the reason why I assigned 11 to input in my code. What am I missing here ?

    And the third question, if you would take a look at line 26:

    Is that what you meant by garbage input? This will always remain until a wanted value is inserted because it's a loop, you insert a number or you close the program.

    Code:
    // in game.h
    
    int getnumber(void);
    void clsstream(size_t length, size_t input);
    
    //in game.c
    
    int getnumber(void) {
        while (TRUE) {
            char input[11];
            fgets(input,sizeof input,stdin);
    
    
            size_t inptleng = strlen(input);
            size_t fullline = 10;
    
    
            int number;
            char c;
            if ( (sscanf(input, " %d %c", &number, &c) == 1) && (number > 0) ) {
                clsstream(inptleng, fullline);
                return number;
            } else {
                clsstream(inptleng, fullline);
                printf("\n\n\nThis is garbage: ----> %s\n\n", input);
                printf(" Try again: ");
            }
    
    
        }
    }
    Thank you.
    Last edited by Carnotter; 04-08-2015 at 02:41 PM.

  15. #30
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,665
    > I don't check NULL returned by fgets() at EOF. Why should I?
    Because if the user presses ctrl-d (or ctrl-z on windows), fgets() returns NULL and your input buffer contains whatever.

    You then go on to process this unknown data - leading to who knows what chaos.


    > if ( (sscanf(input, " %d %c", &number, &c) == 1)
    If the sscanf succeeds, then it will return 2.
    There is no real point in the %c if you're using sscanf with fgets.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 11-14-2011, 08:08 AM
  2. Is there any function better than rand()
    By ubern00ber in forum C++ Programming
    Replies: 21
    Last Post: 03-09-2006, 08:53 PM
  3. Rand() function...
    By Ash1981 in forum C Programming
    Replies: 7
    Last Post: 01-26-2006, 09:04 AM
  4. rand() function?
    By s_ny33 in forum C++ Programming
    Replies: 2
    Last Post: 03-08-2005, 07:58 PM
  5. Rand() function
    By Da-Nuka in forum C++ Programming
    Replies: 10
    Last Post: 12-26-2004, 08:12 AM