Search

5.x — Chapter 5 comprehensive quiz

Quick review

If statements allow us to execute a statement based on whether some condition is true. Else statements execute if the associated if statement is false. You can chain together multiple if and else statements.

Switch statements provide a cleaner and faster method for selecting between a number of discrete items. Switch statements pair great with enumerations.

Goto statements allow the program to jump to somewhere else in the code. Don’t use these.

While loops allow the program to loop as long as a given condition is true. The condition is evaluated before the loop executes.

Do while loops are the same as while loops, but the condition is evaluated after the loop execution. They’re great for menus or things that need to execute at least once.

For loops are the most used loop, and are perfect when you need to loop a specific number of times.

Break statements allow us to break out of a switch, while, do while, or for loop. Or a for each loop, which we haven’t covered yet.

Continue statements allow us to move immediately to the next loop iteration. Be careful when using these with while and do while loops, as your loop counter may not get incremented properly.

And finally, random numbers give us a way to make our programs behave different each time they are run. We’ll see an example of this in the quiz below!

Quiz time!

Warning: The quizzes start getting harder from this point forward, but you can do it. Let’s rock these quizzes!

1) In the chapter 2 comprehensive quiz, we wrote a program to simulate a ball falling off of a tower. Because we didn’t have loops yet, the ball could only fall for 5 seconds.

Take the program below and modify it so that the ball falls for as many seconds as needed until it reaches the ground.

In constants.h:

In your main code file:

Show Solution

2a) Implement a game of hi-lo. First, your program should pick a random integer between 1 and 100. The user is given 7 tries to guess the number.

If the user does not guess the correct number, the program should tell them whether they guessed too high or too low. If the user guesses the right number, the program should tell them they won. If they run out of guesses, the program should tell them they lost, and what the correct number is. At the end of the game, the user should be asked if they want to play again. If the user doesn’t enter ‘y’ or ‘n’, ask them again.

Note: You do not need to implement error handling for the user’s guess.

Here’s what your output should look like:

Let's play a game.  I'm thinking of a number.  You have 7 tries to guess what it is.
Guess #1: 64
Your guess is too high.
Guess #2: 32
Your guess is too low.
Guess #3: 54
Your guess is too high.
Guess #4: 51
Correct! You win!
Would you like to play again (y/n)? y
Let's play a game.  I'm thinking of a number.  You have 7 tries to guess what it is.
Guess #1: 64
Your guess is too high.
Guess #2: 32
Your guess is too low.
Guess #3: 54
Your guess is too high.
Guess #4: 51
Your guess is too high.
Guess #5: 36
Your guess is too low.
Guess #6: 45
Your guess is too low.
Guess #7: 48
Your guess is too low.
Sorry, you lose.  The correct number was 49.
Would you like to play again (y/n)? q
Would you like to play again (y/n)? f
Would you like to play again (y/n)? n
Thank you for playing.

Hints:
* Seed your random number generator with time(0).
* Visual Studio users: Due to a flaw in the Visual Studio implementation of rand(), call rand() once after seeding to discard the first result.
* Use the getRandomNumber() function from lesson 5.9 -- Random number generation to pick a random number.
* Write a function that allows the user to play a single game of hi-lo.
* Write a function that asks the user if they want to play again and handles the looping logic for an incorrect input.

Show Solution

2b) Update your previous solution to handle invalid input (e.g. ‘x’) or valid input with extraneous characters (e.g. “43x”) when the user is guessing a number.

Hint: Write a separate function to handle the user inputting their guess (along with the associated error handling).

Show Solution

6.1 -- Arrays (Part I)
Index
5.11 -- Introduction to testing your code

336 comments to 5.x — Chapter 5 comprehensive quiz

  • Adam

    Hi, I'm pretty new to coding and my code looks slightly different from yours but I'm still able to get the same result. I'm not sure if my code is poorly written and I should try to make it look more similar to yours or just leave it. One quick question I have is how do I know if my code is inefficient so I know what parts should be changed? Thanks in advance.

    • Hi Adam!

      Line 7-9: Should be constexpr
      Line 19, 24: Use @std::numeric_limits<std::streamsize>::max() rather than 32767
      Line 33, 66: Use uniform initialization
      Line 34, 63: Explicitly initialize to 0
      Line 54, 55: Either move line 54 into line 55 or use ++guessnum
      Line 36, 55: Magic number (8), storing min/max/tries in constants makes your game easier to configure

      Looks good otherwise.

      > how do I know if my code is inefficient so I know what parts should be changed?
      Even if there was something inefficient in this code, I don't think there's a reliable way of detecting it.
      Once you work on big projects you can measure the execution time and judge whether or not it is justified based on the action it is executing. You can disable certain features and see how the programs execution speed and resource consumption changes.
      Things the generally watch out for are loops that run longer than required, or double comparisons (line 68, 74, you're comparing to 'y' twice even though the value doesn't change in between).

      References
      * Lesson 8.16 - Timing your code

  • Enzo

    Thanks for the tuts! Had a go on the 2b) and here is what i have. Perfectly working but certainly not perfect. Any tip on my way to write code?
    (Sorry if there is any grammatical error, english is not my main language)

    • Hi Enzo!

      * You're using the same naming style for variables and functions, this will get confusing.
      * Line 106: Use uniform initialization.
      * Line 97, 118: You have a constant for that.
      * @getEndingChar: Using a pointer without validating it's not a nullptr can cause trouble. If you are expecting the function to never receive a nullptr use a reference instead.
      * Line 32, 36, 48, 51: Don't repeat yourself. Write a wrapper function and use @std::numeric_limits<std::streamsize>::max().
      * Line 58, 80: You're running the same comparison twice.

  • threlo

    Here's my go at 2b, any suggestions?

    • nascardriver

      Hi Threlo!

      * Prefer constexpr over const.
      * Use @std::rand, @std::srand, std::exit and @std::time rather than their non-namespaced counterparts. The non-namespaced variants aren't guaranteed to be declared in <ctime> and <cstdlib>.
      * Avoid using @std::exit.
      * Initialize variables to 0 if you don't know their values. Chars to 0, '\0' or '\x00'.
      * @tryAgain The condition for the while-loop can never be true, use a while(true)-loop, this will get rid of the compiler warning too.
      * Line 55, 70: Use @std::numeric_limits<std::streamsize>::max() rather than 32767. 32767 doesn't have any special meaning to @std::cin.ignore.
      * Line 85: Use ++prefix unless you need postfix++.
      * Don't compare to false/true, use the expression directly or negate it

      * @guess in @playGame should be declared inside the loop to achieve a smaller scope.
      * @tryAgain should only be called in once place.

      Good job on using uniform initialization, easy-to-modify game settings and input validation!

  • ArmaIG

    I loved the quiz, this is my 2b) code, any improvement advices?

    • nascardriver

      Hi ArmaIG!

      * "streamsize_t" is a bad name, because the type isn't streamsize anymore, it's numeric_limits. Since you're calling @std::cin.ignore multiple times with always the same arguments, write a wrapper function for it.
      * @thinkNumber: You don't need to call @rand here to discard results. Doing this once after calling @srand is enough. Anything more is a waste of resources.
      * Line 11, 19, 74: Uniform initialization
      * Use @std::rand, @std::srand and @std::time instead of their non-namepspaced counterparts. They're the same, but @<ctime> and @<cstdlib> don't guarantee their declaration in the global namespace.
      * Line 10, 11: Those could be constexpr
      * Line 36, 83: A bool is false/true, not 0/1.
      * Line 41: Use ++prefix rather than postfix++ unless you need postfix++.
      * Line 45-53: Once you checked two conditions and they were false the third one must be true. One condition can be dropped.
      * Line 55: Magic numbers are bad numbers.
      * Line 69: A cast might be needed with some compilers, because @srand wants an unsigned int but the return type of @time is implementation specific.
      * Line 79: You missed it.
      * Sorting your includes makes it easier to see if you already included something.

      No major problems though, code works fine, good job!

  • _RryanT

    Here's go at 1)

    • nascardriver

      Hi Ryan!

      Suggestions:
      * Line 7, 19: Use uniform initialization.
      * Line 10: Should be <= and 0.0.
      * Line 13: Use the ++prefix rather than postfix++ unless you need postfix++.
      * You're using the same name style for functions and variables, this makes your code harder to read.

  • Ren

    Here is my go at 2a. Is there anything I should or shouldnt be doing?. Btw love the quizzes at the end of each chapter really makes you apply what you've learned.

    • nascardriver

      Hi Ren!

      Skipping @getRmdNumber, because you copied it.
      Only use empty uniform initialization when the date type is too complex to specify a value. Initialize to 0.
      If the random number is 0 the game won't start.
      Rest looks alright.

  • FelixPhill

    This is my code for the second task. I just realized I had to write a separate function for a single hi-lo game, but since it's just a hint I'll keep it like this for now. However, the first task was a lot harder for me and I had to look at the solution after just not figuring it out.

  • Rohit

    I want to post my code for Question 2a, before I even start with 2b. I tried to use the c++11 <random> header with a mersenne twister and it works, but I'm just trying to understand how it works. I have some comments on it as well as some other things. If someone gets a chance to look at it, that would be much appreciated.

    • nascardriver

      Hi Rohit!

      > A "do while" loop might be better here
      Doesn't matter, but use 'true' instead of '1'.

      Quoting cppreference
      "std::random_device is a uniformly-distributed integer random number generator that produces non-deterministic random numbers."

      We could use @std::random_device as it is to generate random numbers, but "the performance of many implementations of random_device degrades sharply once the entropy pool is exhausted". So we only use the @std::random_device once to seed an @std::mt19937.
      The @std::mt19937 instance can be use to generate random integers.

      This is fine, as long as you don't need a specific range of numbers. That's where the @std::uniform_int_distribution comes to play. When we construct the @std::uniform_int_distribution we tell it the (inclusive) range of the numbers we want to get. We then pass out @std::uniform_int_distribution instance our std::mt19937 instance which the @@std::uniform_int_distribution instance uses to generate a random number and perform a calculation to limit the big number to our range.

      rd() replaces time(0)
      mt() replaces rand()
      uid(x) replaces (a + (b - a) * (x / max_x)) where a and b are minimum and maximum and x is the return value of rand()

      References
      * std::uniform_int_distribution - http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution
      * std::mersenne_twister_engine - http://en.cppreference.com/w/cpp/numeric/random/mersenne_twister_engine
      * std::mt19937 - http://www.cplusplus.com/reference/random/mt19937/
      * std::random_device - http://en.cppreference.com/w/cpp/numeric/random/random_device

      • Rohit

        Awesome sounds good. Thanks! Those are good references too. I'll have to check them out.

        Is the rest of my code good in general? Any bad habits/mistakes you see?

        • nascardriver

          • Rohit

            Your code is so much more modular, extensible, and efficient. I gotta practice thinking with future-proofing in mind. Thanks! And my variable names seem fine to me, but I guess they're likely to confuse other people.

            Couple question though. Where you said use uniform initialization for @replay, I figured since I'm asking the user for the input in the next line anyway, initializing it wouldn't matter. Do you say that for consistency and good practice reasons, or is there more to it than that?
            And I'm thinking hex 00 is the null character, correct? I would intuitively assume initializing a char type with a null is better than a space like I had done at first, but why is that?

            • nascardriver

              > And my variable names seem fine to me, but I guess they're likely to confuse other people.

              > since I'm asking the user for the input in the next line anyway, initializing it wouldn't matter.
              Don't rely on functions being able to handle uninitialized variables or that they overwrite the value. @std::cin does, but for a reader who doesn't know, this might lead to confusion. Initializing all variables makes your program more deterministic and therefore easier to debug.

              > I would intuitively assume initializing a char type with a null is better than a space like I had done at first, but why is that?
              You can initialize it to whatever you want. Traditionally 0 is used, it's the in every numeral system and easy to recognize.

              • Rohit

                Alright sounds good. I appreciate the advice.

                I also gotta pay attention to the warnings I get after compiling. If I had seen the one you mention on line 71 my code might have looked less sloppy.

  • Will

    My take on solution 1. It was quite fun going back to the older exercise and improving upon it.
    Any comments would be welcome.

    • nascardriver

      Hi Will!

      Use uniform initialization. Other than that your code looks fine to me.

      References
      Lesson 2.1 - Fundamental variable definition, initialization, and assignment

    • Rohit

      if you're doing nothing with @height and @variableHeight except calculating the current height and assigning that value to @variableHeight, then i don't see the point of having the variable @height in the first place. couldn't you just have this in the for loop:

      unless of course there's something i'm not seeing

  • Kio

    Maybe the easiest solution for task 1.

  • Joe

    I have a question.  

    I was attempting the ball drop quiz and just could not figure out a sensible way of using do/while or other loops, but i stumbled on something and I want to know how bad an idea is this.

    With the function to print results, I copied and pasted them into the calculateAndPrintResults function, and after the if statements cout I called the calculateAndPrintResults with a ++secondsPassed parameter and it worked.  

    void calculateAndPrintHeight(double initialHeight, int secondsPassed)

    so my question is, why would this be bad?

    • nascardriver

      Hi Joe!

      What you did is called recursion (Lesson 7.11). An iterative solution is preferred unless it would make to code too complicated.
      Recursion is more resource-hungry than loops and harder to understand.

  • David

    Is there a way to query whether or not the std::cin buffer is empty? For example, if a user entered "3asdf", it might be useful to use a conditional block to ask "Did you mean 3?"

    • nascardriver

      Hi David!

      The buffer won't be empty, there will be a linefeed character in the end. You can check for it.
      @std::istream::rdbuf returns the buffer
      @std::streambuf::sgetc returns the next character in the buffer

      Sample output

      • David

        This looks great, thank you! I don't yet understand the line

        but hopefully I will after I finish the tutorials.

        • nascardriver

          I added the two lines
          @std::istream::rdbuf returns the buffer
          @std::streambuf::sgetc returns the next character in the buffer
          to my comment.
          You can look up the documentation of those functions
          here http://www.cplusplus.com/reference/fstream/ifstream/rdbuf/
          and http://www.cplusplus.com/reference/streambuf/streambuf/sgetc/
          but they might be more confusing than helpful for now.

          The arrow -> is part of the upcoming lessons.

  • Peter Baum

    This comment applies to the site in general and the comment mechanism in particular.  These comments seem to have more than one use:

    1. Because Alex is so good at responding to the comments, it has been a great mechanism for improving the lessons.  However, for the students wading through a lot of comments that have already been used to fix problems, it is a waste of time (by-in-large).  It would be nice to separate out this function and/or delete the comments once the lesson has been changed to address the issue.

    2. Some people have used this mechanism for specific help with a program that they have written, an I applaud Alex and nascardriver for all the work that goes into such responses.  However, I almost never look at these because if I am going to look at code, I want to look at code from people with a lot of experience so it is more likely that I will pick up useful things.  So these responses could/should be deleted for the most part to increase the likelihood that the comments will be useful.

    3. There are some comments that I would classify as "if you want to go a little further with this topic" such as the comment about hungarian notation.  Those comments would be especially valuable (to me at least).

    • Alex

      Yes, you are absolutely correct -- the comments section is a muddled between lesson corrections/feedback, help-needed, and lesson extensions (which sometimes get integrated into the lessons themselves). I have gone back and culled out older comments periodically, but it's a lot of work and not at all fun. I'm not sure what the best way to manage this is. I could explore a self-destruct mechanism so that older posts will clean themselves up. Alternatively, I could explore a flag solution to raise the most interesting comments to the top. I'll have to give this some more thought. In the meantime, if you (or anyone else reading this, hi nascardriver) have any additional thoughts or opinions on the matter, please respond to this thread!

      • Peter Baum

        One thought is that if you create different types of "comment" sections, then the people that post will help with this problem.  For example, if they post to a "I need specific help on my program" messaging area, then you could easily program it to auto-delete after a certain length of time has elapsed because you wouldn't have to worry that some of the messages needed to stay around longer.

        Regarding culling not being fun - don't forget that you have people interested in helping out.  Perhaps another message area where you can communicate with your volunteers.  You could ask for specific help and/or they could volunteer for things they were interested in.

        Another idea: One of the problems I found with Khan Academy and their version of Javascript was that they didn't think through how what they did provide would assist in the next learning stages for a student broadly interested in programming.  The problem is much less of a problem with this site because a strong C++ education is going to be broadly useful.  However, in terms of motivation, we are asking a lot of the student to slog through a lot of material just to get to control structures.  (They want to have fun too!)  Then there are issues of the console interface and cout.  Perhaps the course could provide a wrapper that would get people to do some windows programming and then let them go back later to revisit the complex details hidden underneath.

        • Alex

          Thanks for the perspective. I'll consider what you've said moving forward. Some of these options may not be technically viable, or more work to develop than just to do things manually. But you've given me some things to think about.

      • nascardriver

        - Lesson feedback/suggestions
        Visible only to admins, remove after processed, send user a thank you mail.

        - Quiz solution submission
        Visible to everyone after clicking "View other people's solutions" or similar. This avoids spoilers when reading the comments before working on a quiz.

        - Questions
        Visible by default to everyone, sorted by date, I doubt a rating system would work out well for this.

  • Peter Baum

    In the calculateHeight() function, I suggest using a variable name such as "time" or "timeInSeconds" rather than "seconds."  The idea is to name the variable in a way that identifies the function of the variable (to hold a value that represents the measurement of a time interval) rather than confusing it with the units associated with that function.

    • Alex

      Variable names are most useful when they clarify both the intent and units of the variable are. A better name would be something like, "secondsPassed", which tells you both the intent (how many seconds have passed since the start of the simulation) and the unit (seconds).

      • Peter Baum

        I like your suggestion.  

        I also wanted to tell you I very much appreciate your willingness to accept all these suggestions and to continue to improve the site.  I've discovered that the very smartest people recognize that with projects this size, improvement is an endless process.

        • Alex

          Half the reason these lessons are useful to anybody is because of all the great feedback that readers have given over the years. So thank you for all of the feedback you've taken the time to give, you're making these lessons better for everyone who comes after you.

  • Peter Baum

    Readability issue: in the "Quick Review" section you will find the sentence

    "Break statements allow us to break out of a switch, while, do while, or for loop. Or a for each loop, which we haven’t covered yet."

    I stumbled on the "Or a for each loop" part because "for each" hasn't been covered yet and is not clearly identified as a keyword.  Finding some typography to identify keywords would help... font change, italics, bold, even quotes would help.  Also, trying to start a sentence with "Or" is problematic.

  • Matt

    I finally finished this program to the best of my ability.  I added the ability for the user to select some game settings, and added some error checking.  I'm not completely happy with the error checking (for example, "What is the number of guesses you want to have? (Must be between 1 and 15): " 5j works just fine.  I understand why it works, but don't know how to make it fail and loop again.  Regardless, I'm extremely happy to be able to write a program like this considering I knew absolutely nothing about programming not so long ago, a testament to the quality of instruction on this site.

    Regards,
    Matt

    • nascardriver

      Hi Matt!

      > how to make it fail and loop again
      Warning: Pointers (Chapter 6)
      Get the input as a string (std::getline) and convert it to a number using std::strtol (str=string, to=to, last letters=type (strtoll, strtod, strtof, etc.)). std::strtol sets a pointer after the number has ended. You can check if this pointer is the end of your string to see if the entire string is a number.

      http://en.cppreference.com/w/cpp/string/basic_string/getline
      http://en.cppreference.com/w/cpp/string/byte/strtol

    • nascardriver

      I just learned about null statements. You don't need the curly brackets for the while-loop in @main.

  • Matt

    For quiz 2a, I came up with the following solution first.  I still haven't looked at the solutions, but even so I decided to change the functionality but I'm putting this here because I have a question about it:

    Question: This game plays fine, and outputs as asked; however, the playAgain() function doesn't use standard loop syntax (instead it just calls itself and calls the playGame() function.  It also required forward declaration to compile.  Is there actually anything wrong with this?

    So, good or bad I didn't really like it and decided to nest @playGame() inside of a loop with the condition being the result of @playAgain.  Seems cleaner to me and more in line with the spirit of this chapter.  Final code for quiz 2a (before any error handling) follows:

    • nascardriver

      Hi Matt!

      > Is there actually anything wrong with this?
      This is called recursion (A function calls itself) (Lesson 7.11). Recursive functions are handy in some situations, but they are more resource intensive than an equivalent iterative solution.

      > 2a minus error handling

    • Alex

      Beyond being more resource intensive, recursive solutions tend to be harder to understand/follow. Thus, they should really only be used when the recursive solution is simpler than the equivalent iterative solution. In this case, a loop would be easier to understand.

  • Matt

    For some reason I really struggled to decide exactly where the loop should be located.  It wasn't until I went back to my old habit of writing down an outline that it occurred to me that the loop really belonged in the main function.  After I got the whole thing written (originally just using a while loop) I had a hard time figuring out how to handle the case where the user input a tower height of exactly 0.0.  Using just a while loop, an initial height of 0.0 resulted in either nothing printing [using while (ballHeight < 0.0)] or time 0 and time 1 printing [using while (ballHeight <= 0.0)].

    Trying to solve this issue, I thought of the do while loop early in the scenario, but I misunderstood the do while lesson so entirely that I failed to implement it multiple times even referencing the do while chapter 5.6.  Here's how I had the erroneous do while loop written originally:

    After re-re-rereading 5.6 it finally clicked and my program was complete.

    Here's my take on the first quiz:

    In constants.h:

    In main.cpp:

    • nascardriver

      Hi Matt!

      You got it right, nothing more I can say. Everything I'd change is in Alex' part of the code so I'm not going to list it.

  • radu f

    Hello,
    first of all, congrats for your work!
    Concerning the second problem, after I've seen the differences between your solution and mine, even if - according to my tests - my code is running fine, I came to the conclusion that it might be a good idea to post my code here. Maybe you'll have time to look at it and give me suggestions for improvement or point me some faults I didn't see.
    I'll post below the code for a modified version (significantly longer code), where the user can customize some things in the game (range for the random number and the number of tries). However, removing all these personal adds, you can easily guess how the code for the original program looks like.
    Before posting the code, I also have 3 questions:
    - how can I treat as an invalid input an entire string like "300f"? (using some kind of rule like "if a part of the input is invalid, then all input is invalid")
    - is there any reason to worry about newPlayFunction (see the below code) not having a return value outside of if sequences? afterall, there's no way out from the loop except the break(s)
    - with the knowledge from your tutorials so far, is it possible to store all the previous numbers proposed by the user and compare them with the last one? (I managed to do it only for the last previous number - variable g_previous_number).
    Thanks for the tutorials, sorry for writing so much (code and post).

    • nascardriver

      Hi Radu!

      > it might be a good idea to post my code here
      I'm always happy to look through code, don't hesitate to share your solutions.

      > the user can customize some things in the game
      Good idea, things like this will keep you motivated.

      > how can I treat as an invalid input an entire string like "300f"?
      There are various ways of achieving this, I don't think there's a function that handles everything for you.
      Here are some
      - Regex
      - std::getline into a string, loop though string to see if every character is a digit (For integers, more checks for floating point) and convert to number using a strto* function or std::stringstream
      - std::getline and strtoll or strtold to convert to number, check endptr
      The second option is probably the easiest for now.

      > is there any reason to worry about newPlayFunction (see the below code) not having a return value outside of if sequences?
      See comments in code, one would not usually write the function like you did. General rule: Don't ignore warnings

      > with the knowledge from your tutorials so far, is it possible to store all the previous numbers proposed by the user and compare them with the last one?
      Not yet, chapter 6 will teach you how to do this.

      Good thing you posted your code, I made quite some changes

      Feel free to ask any questions about the changes I made.

      • radu f

        Thank you, nascardriver!
        Too many superficial readings from me, especially regarding booleans.
        I think I'll read again the tutorials and practice some more.
        I don't want to go further with bad habits.

  • I'm feeling a little stupid here and need some help with the following code:

    • nascardriver

      Hi Chandler!

      Let's take a look at the following:

      The if-statement requires you to give it a boolean expression, in this case it's (ch == 'y').
      (ch == 'y') is true if the value of @ch is 'y' and false otherwise. We can validate this by printing the result.

      Output

      The == operator returns a bool. We can use this behavior to return a bool from a function.

      References:
      Lesson 2.6 (Boolean values and an introduction to if statements)
      Lesson 3.4 (Sizeof, comma, and conditional operators), the conditional operators section

      • Thanks for the reply, nascardriver. I get bools okay. My issue was with the TRUE/FALSE return of that function.
          

        You told me:

        That was the confusing part to me right there. The unseen setting of ch to FALSE. Hmm...or is that just an evaluation?

        LOL! I see it now. That's an IF statement without the IF 😀

        It wld have made more sense to me like this:

        But I get it now.

        Thanks for your time, nascardriver. And another thanks to you, Alex, for your excellent tutorials.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]