Thread: Program Critique (I'm a n00b)

  1. #1
    Registered User
    Join Date
    Sep 2016
    Posts
    4

    Program Critique (I'm a n00b)

    Hola!

    I am new to c programming and am in a college c-programming course. I just completed the chapter in my textbook pertaining to pointers and completed one of the challenge questions at the end of the chapter.

    Request some feedback on this program. It works. It does what it's supposed to do. However, I want to know what you experienced c-programmers would do differently.

    The problem: create a program to allow a user to roll up to six die and store the die in an array. Build a function to perform the dice roll and storage of the dice in the array.

    Here's my source code:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    
    void TossDie(int, int [], int *);
    void CPU_TossDie(int, int [], int *);
    void WhoWon(int, int);
    void DelayGame(int);
    void GetDieNum(int *);
    
    
    int main(void) 
    {
        int iNumDice = 0;
        int dice[6];
        int CPU[6];
        int x;
        int iDelay = 2;
        int iSum = 0;
        int iResult = 0;
        int *iDptr, *iRptr, *iNptr;
        int iMenu_Selection = 0;
        iNptr = &iNumDice;
        iDptr = &iSum;
        iRptr = &iResult;
        
        do 
        {
            system("clear");
            
            GetDieNum(&iNumDice);
            printf("\nPress ENTER to roll %d die!", iNumDice);
            getchar();
            getchar(); //need two getchar to function properly
            TossDie(iNumDice, dice, &iSum); //function to roll user selected # of die and store in dice[]
            printf("\n\nYour score:  %d", iSum);
        
            DelayGame(iDelay); //delay 
        
            printf("\n\nCPU's turn to roll %d die!\n", iNumDice);
        
            DelayGame(iDelay); //delay 
    
    
            CPU_TossDie(iNumDice, CPU, &iResult); //function to roll user selected # of die for CPU's turn and store in CPU[];
            printf("\n\nCPU score:  %d\n", iResult);
        
            DelayGame(iDelay); //delay 
    
    
            WhoWon(iSum, iResult); //function to check and display the winner
    
    
            system("clear");
    
    
            printf("\n:::::::::::::::::MENU::::::::::::::::::");
            printf("\n1 - Play Again");
            printf("\n2 - Quit");
            printf("\nEnter your selection (1-2):  ");
            scanf("%d", &iMenu_Selection);
        } while (iMenu_Selection != 2);
        return 0;
    }
    
    
    void GetDieNum(int *iNumDice)
    {
        do 
        {
            printf("\nSelect the number of die to toss (1-6): ");
            scanf("%d", iNumDice);
            if (*iNumDice > 6)
            {
                printf("\n***Error: number cannot exceed 6.  Try again.***\n\n"); //error message
            }
        } while (*iNumDice > 6);
        return;
    }
    
    
    void DelayGame(int x)
    {
        int iCurrentTime = 0;
        int iElapsedTime = 0;
        
        iCurrentTime = time(NULL);
        do
        {
            iElapsedTime = time(NULL);
        } while ((iElapsedTime - iCurrentTime) < x);
    }
    
    
    void TossDie(int iNumDice, int array[], int *iSum)
    {
        int x, iIndex, iCurrentTime, iElapsedTime;
    
    
        *iSum = 0;
        for (iIndex = 0; iIndex < iNumDice; iIndex++)
        {
            x = (rand() % 6) +1;
            array[iIndex] = x;
            printf("\nDice #%d rolled %d", iIndex + 1, array[iIndex]);
            iCurrentTime = time(NULL);//delay random number generation to seed different numbers from srand()
            do 
            {
                iElapsedTime = time(NULL);
            } while ((iElapsedTime - iCurrentTime) < .5);
            *iSum += array[iIndex];
        }
        return;
    }
    
    
    void CPU_TossDie(int iNumDice, int array[], int *iResult)
    {
        int x, iIndex, iCurrentTime, iElapsedTime;
    
    
        *iResult = 0;
        for (iIndex = 0; iIndex < iNumDice; iIndex++)
        {
            x = (rand() % 6) +1;
            array[iIndex] = x;
            printf("\nDice #%d rolled %d", iIndex + 1, array[iIndex]);
            iCurrentTime = time(NULL);//delay random number generation to seed different numbers from srand()
            do 
            {
                iElapsedTime = time(NULL);
            } while ((iElapsedTime - iCurrentTime) < .5);
            *iResult += array[iIndex];
        }
        return;
    }
    
    
    void WhoWon(int iSum, int iResult)
    {
        if (iSum > iResult)
            printf("\nYou win!  You score %d more than the CPU.\n\n", iSum - iResult);
        else if (iSum < iResult)
            printf("\nYou lose!  CPU scored %d more than you.\n\n", iResult - iSum);
        else if (iSum == iResult)
            printf("\nDraw!  Both you and the CPU scored %d\n\n", iSum);
        printf("\nPress Enter to go to MENU");
        getchar();
        return;
    }
    Gracias!

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,664
    > iCurrentTime = time(NULL);//delay random number generation to seed different numbers from srand()
    This is wholly unnecessary.
    First of all, you're not calling srand at all - but you should call it exactly once at the start of main. Calling rand() in a tight loop is independent of the passage of time. The mistake people make is calling srand(time(NULL)) in a loop and wondering why they get the same rand() result each time.
    Second, even if this were necessary, you should be using your DelayGame() function.


    TossDie and CPU_TossDie are the same code (with the occasional renaming of variables).


    > void TossDie(int, int [], int *);
    Putting in the parameter names helps to document what the function does.


    > iNptr = &iNumDice;
    > iDptr = &iSum;
    > iRptr = &iResult;
    These are unused, and serve no purpose. They should be removed.


    > int dice[6];
    > int CPU[6];
    Create a constant in place of magic numbers like 6
    #define MAX_ROLLS 6
    int dice[MAX_ROLLS];
    int CPU[MAX_ROLLS];

    Replace other uses of 6 (where it refers to the number of die rolls) in your code with MAX_ROLLS
    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.

  3. #3
    Registered User
    Join Date
    Sep 2016
    Posts
    4
    > iCurrentTime = time(NULL);//delay random number generation to seed different numbers from srand()
    This is wholly unnecessary.
    First of all, you're not calling srand at all - but you should call it exactly once at the start of main. Calling rand() in a tight loop is independent of the passage of time. The mistake people make is calling srand(time(NULL)) in a loop and wondering why they get the same rand() result each time.
    Second, even if this were necessary, you should be using your DelayGame() function.
    Bad job on my part in editing those comments after editing that function's code (lesson learnt). I initially called srand(), but as you stated, I realized that something was off because I was getting the same rand() result each time. I removed stand() and kept the time delay code to delay displaying the dice numbers. With that said, I will change it and call stand() in main once to seed rand().

    TossDie and CPU_TossDie are the same code (with the occasional renaming of variables).
    Doh! I should just have TossDie and pass different variables to it -- user variables and CPU variables. Got it.

    > iNptr = &iNumDice;
    > iDptr = &iSum;
    > iRptr = &iResult;
    These are unused, and serve no purpose. They should be removed.
    I thought that I had to create a pointer of the variable before passing the pointer to a function; poor understanding on my part.

    > int dice[6];
    > int CPU[6];
    Create a constant in place of magic numbers like 6
    #define MAX_ROLLS 6
    int dice[MAX_ROLLS];
    int CPU[MAX_ROLLS];

    Replace other uses of 6 (where it refers to the number of die rolls) in your code with MAX_ROLLS
    Ah! Makes perfect sense.

    Thank you so much for taking the time to critique my program, Salem. I actually learned quite a bit from your comments -- very helpful.

  4. #4
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    Some small things:
    - if you're comfortable with semi-Hungarian notation - sure, do your thing. I don't see much need for it, though. If you haven't already - do a search on apologies and criticism of it. Makes for a fun, topical, even if sometimes religion smacking, read.
    - for (iIndex = 0; iIndex < iNumDice; iIndex++) since C99 you can do a C++-esque: for (int i = 0; i < max; i++), where i is local to the loop. That goes to every other place where you defined variables at the start of the function block - that's not necessary anymore in C99-onwards (we have C11 now). You can define local variables wherever you need them.
    - return; at the end of function returning void is redundant. The function will properly return either way. You can safely omit it.
    - function and variable names - they are a tad confusing. As an example - WhoWon() would make me think it returns some identifier of the winning player or something like that. PrintWinnerMessage() might be a tad less confusing. Self-documenting names are fun, because you don't have to read, or add, that much comments (if at all) to know what is what after you get back to your code some time later. Your x variable in TossDie() doesn't self-document itself very well, luckily though, you assign value to it not far from where it's used. But even that - it's completely unneeded, you could just have: array[iIndex] = (rand() % 6) +1; no need for x there. If you used that value twice - now that would be a useful variable. A tad more verbose and useful name for it would be 'currentRoll' or something like that. You rather want more verbose names. There are, however, some shorthand names that by general convention are easily recognized as general iterators like i, j, k,... are commonly used as iterators, i for outer loop, j for first nested, k for one nested in the previous, etc. x, y, (z), can easily be used as iterators where context is right (say, they signify a position of something on a grid). Your iIndex can easily be replaced by i and nobody reasonable will complain about it being not verbose enough.
    - every function returns void. Functions can return values - why not make use of it? CPU_TossDie(iNumDice, CPU, &iResult); could be iResult = CPU_TossDie(iNumDice, CPU); and you wouldn't have to play with pointers all that much in your function. Makes for a bit cleaner code if you ask me.

    Again, some small things, some are just preference. Take it or leave it.

  5. #5
    Registered User
    Join Date
    Sep 2016
    Posts
    4
    Thank you, Xupicor. I never heard of semi-Hungarian notation until I read your reply. My textbook does it, so I did it. I will implement your suggestions. It seems like it will make my code look cleaner. I must say, I am a bit upset that neither my textbook or my professor explained that i, j, k are recognized as general iterators. Again, thank you for the critique!
    Last edited by Mwilkers1; 09-22-2016 at 05:47 PM. Reason: grammar b/c I suck

  6. #6
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Also n and p.

    However, do not stick to them if the context favours other names. A x,y loop to describe a matrix, for instance, is going to be more expressive than i,j. And don't stick to single char identifiers either, if a row,col loop or a lat,lon loop helps better understanding your code.
    Last edited by Mario F.; 09-22-2016 at 07:31 PM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  7. #7
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    I used "semi-Hungarian notation" to mean "kind of Hungarian notation", since there are variations on it (Systems and Apps Hungarian seem to be the most popular, though I've seen people modify the idea). Hungarian notation - Wikipedia, the free encyclopedia
    The important part is to know the rationale behind a notation. Personally, I'm not a fan of Hungarian, but it's not something I'd say you "need" to change. Seems it's still good I mentioned it.

    You'll find there are more topics like this in programming that are based at least largely on personal preference, like... Bracket positions. For example, you can have:
    Code:
    int *fun(double foo[], int *bar) 
    {
      for (int i = 1; i < 2; i++)
      {
        stuff(i);
      }
    }
    //or
    int* fun(double* foo, int* bar) {
        for (int i=1; i<2; i++) {
            stuff(i);
        }
    }
    //etc
    for (;;) {
      do_stuff(); }
    for (;;)
    { do_stuff(); }
    for (;;) { do_stuff(); }
    for (;;) do_stuff();
    for (;;) {
      do_stuff();
    }
    for (;;)
    {
      do_stuff();
    }
    for (;;)
      {
      do_stuff();
      }
    for (;;)
      {
        do_stuff();
      }
    // etc
    There are different style guides and documents that describe these things. Usually you want to (have to) conform to a style guide of the team you are a part of. It might be of benefit to you to look through some style guides and pick what you like from them. The important part is to be consistent. Knowing the rationale behind it - even knowing that it's not really all that strong - is always of benefit too. Also, be sure to check out critiques of style guides too.
    Stroustrup: C++ Style and Technique FAQ
    Google C++ Style Guide
    https://www.linkedin.com/pulse/20140...a-deal-breaker

    And sure, wholeheartedly agree with Mario here - if the context deems it fit, you're probably better off using "col/row" or "lat/lon" or whatever expressive name that comes to mind and makes sense. These I noted are just widely spread (among languages too) and rather generic.

    Don't count on the textbook to tell you everything, especially about things that aren't specifically part of the language, like style.

  8. #8
    Nasal Demon Xupicor's Avatar
    Join Date
    Sep 2010
    Location
    Poland
    Posts
    179
    Ah, and yes, can't edit that in, but - these guides are for C++, so you'll have to take what you can from them, mostly about stylistic stuff, when it comes to C. (mods, please feel free to merge that)

    Some C style guides are out there though.
    http://homepages.inf.ed.ac.uk/dts/pm...sa-c-style.pdf 1994, be aware of that.
    C and C++ Style Guides
    https://www.kernel.org/doc/Documentation/CodingStyle
    GNU Coding Standards

    Remember, some people will outright hate some certain styles. It's still better to know about them than not. Also, I'm saying you should outright read all of these now, because that's a LOT of stuff to go through, but make it a point to do skim over it over some period of time. Won't hurt.

  9. #9
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Also be careful about over complicating your if statements, and when possible end if()/else if() constructs with a plain else.

    Code:
    void WhoWon(int iSum, int iResult)
    {
        if (iSum > iResult)
            printf("\nYou win!  You score %d more than the CPU.\n\n", iSum - iResult);
        else if (iSum < iResult)
            printf("\nYou lose!  CPU scored %d more than you.\n\n", iResult - iSum);
        else if (iSum == iResult) // If you get here then iSum will always be equal iResult, no need for the else if() a simple else would be better.
            printf("\nDraw!  Both you and the CPU scored %d\n\n", iSum);
        printf("\nPress Enter to go to MENU");
        getchar();
        return;  // This is not necessary, the function will automatically return.  
    }
    Also I'm not a big fan of your function naming convention. I myself prefer using the leading uppercase letter names for User Defined Types (structures).

    Jim

  10. #10
    Its hard... But im here swgh's Avatar
    Join Date
    Apr 2005
    Location
    England
    Posts
    1,688
    >I myself prefer using the leading uppercase letter names for User Defined Types (structures).

    For me it's camel-case for function names, Upper-case (as it should) for structure definitions.

    One very small other thing:

    Code:
    System("clear");
    I don't really see the point of that line of code. It may have a purpose I am not
    seeing however.
    Double Helix STL

  11. #11
    Registered User
    Join Date
    Sep 2016
    Posts
    4
    Thanks, again, everyone for the program critique. I am earnestly trying to learn versus simply trying to get a good grade in this class, so all of your input is valuable.

    I don't really see the point of that line of code. It may have a purpose I am not
    swgh: I run my programs from the command line, so I wrote that code into the program to clear the console window so that only my program is visible while running.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Source Checking Program - Critique?
    By Bart de Koning in forum C Programming
    Replies: 9
    Last Post: 12-15-2011, 12:10 PM
  2. Replies: 9
    Last Post: 07-23-2011, 01:23 AM
  3. n00b needs help with small program
    By rightbrainer in forum C Programming
    Replies: 3
    Last Post: 04-15-2009, 05:52 PM
  4. Critique my program
    By Jason Spaceman in forum C Programming
    Replies: 6
    Last Post: 10-26-2004, 05:38 PM
  5. Critique / Help me make this program run faster.
    By Mastadex in forum C++ Programming
    Replies: 10
    Last Post: 06-26-2004, 11:58 AM

Tags for this Thread