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 range-based for 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!

Question #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

Question #2

a) 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.

* Use the Mersenne Twister algorithm from chapter 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.
* Avoid magic numbers by defining constants.
* In an if-statement, if you already checked 2 numbers for equality and compared them with either the < or > operator, but all checks failed, the relation between the numbers must be the one you didn’t check. For example, if guess is neither equal to nor less than number, it must be greater than number.

Show Solution

b) 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)
5.11 -- Introduction to testing your code

794 comments to 5.x — Chapter 5 comprehensive quiz

  • Balldrop revisited:

  • Winston Lai

    Hi, I was trying to setup a solution for failed extraction when the user is entering the input. However, when I run the code, the program stops after user entered the invalid text. Can you please help me?

  • jörg

    Hi again, my solution for quiz 2/2b looks quite different but works pretty good aswell I think. I also used the Marsenne Twister because it was recommended in 5.9. Is is okay like that?

    • * Line 9, 10, 12, 42, 65: Initialize your variables with uniform initialization
      * Line 27, 33: Use @std::numeric_limits<std::streamsize>::max() rather than 32767
      * Line 44: One of those variables can be removed
      * Line 60, 74: Avoid recursion (a function calls itself) unless an iterative solution would be way harder. Use a while-loop.
      * Line 71, 76: Double comparison of @decision to 'y'/'n'

      Lesson 2.1 - Fundamental variable definition, initialization, and assignment
      std::basic_istream::ignore -

  • jörg

    Hello, is this solution for the first quiz okay aswell? And please ignore the other header files, I just keep them in my practising project so I don't forget the names :D

    • Hi Jörg!

      Line 11, 21, 22, 40: Initialize your variables with uniform initialization
      Line 19, 21: Use double numbers when calculating with doubles
      Line 4, 33: Don't use OS-specific functions when there are cross-platform alternatives. Use @std::this_thread::sleep_for

      std::this_thread::sleep_for -

  • Andrii

    Hi. If it possible I want to know if my code is good.

    • Hi Andrii!

      * Line 9, 29, 50, 51, 52, 53, 54, 56: Initialize your variables with uniform initialization
      * Line 15: Use std::numeric_limits<std::streamsize>::max() instead of 32767
      * Line 35, 41: Recursion is bad, use loops instead
      * Magic numbers: 1, 8, 9, 100. Use constants
      * Line 58: (true && x) == (x)
      * @game: Declare @y inside the loop
      * Don't use @system, your code will not behave well systems other than Windows. If there is absolutely no way around it, use @std::system.

      Have a look at some of the other submissions in the comments to see how people solved this quiz without recursion.

  • Reynaldo

    July 24, 2018 at 6:17 pm · Reply
    Hey there! Thanks for having very insightful tutorials for c++, I'd love to know how I can improve the code I wrote for 2b)

    • Hi Reynaldo!

      * "100-1+1" Ok
      * Magic numbers 1, 7, 100
      * Line 7: Use uniform initialization
      * Line 13: Initialize to 0, '\0' of '\x00'
      * Line 18, 72: Double comparison of @playAgain to 'y'
      * Line 38-40: You're calling @std::cin.clear, even if there is no error
      * Line 56: This should not be static, move it outside of the loop. static variables hurt multi-threading compatibility, only use them when the variable is unique to your program. Imagine you were running this program on a server and multiple people play at the same time. You'd only have one @playerGuess for all of them.
      * Inconsistent formatting. Use the auto-format feature of your editor.
      * Spaces before '\n' are memory waste
      * Inconsistent use of "using std::cout"

      * Using uniform initialization for the most part
      * Correct use of @std::cin.ignore
      * Good code separation

  • firebow

    i don't kwow if i'm doing it right:

    • Hi firebow!

      Enable compiler warnings and read them.

      * Line 45, 46 are _not_ encapsulated in curly brackets, use the formatting feature of your editor.
      * Initialize your variables with uniform initialization.
      * 30000 doesn't mean anything to @std::cin.ignore, pass @std::numeric_limits<std::streamsize>::max()
      * Magic numbers, declare constants
      * Use ++prefix unless you need postfix++
      * @main: Missing return statement

      Apart from input validation the game is working, good job!
      Try updating your code with the suggestions above to make sure you understood everything.

      • firebow

        thanks for the help i fixed it :
        PS:the 30000 was just because i didn't want to search how to get the max:

        • @maxIgnore should be constexpr and @std::streamsize

          There are still magic numbers (1, 7, 100) and you're not initializing all variables with uniform initialization (Lines 11, 18, 38).

  • Tubbs

    Hey, so I don't really know if I'm on the right track here. I got stumped on A LOT of things during this test (applying the problem into code in different places)and was wondering if this was normal or just a lack of experience writing the code. Sometimes I can't even understand the question and I don't even know why because everyone else seems to have no difficulty with it. I can read code just fine and can understand why everything's there but when I start writing it I can't figure out where to start or how to apply the problem into my code. Do you have any tips for me or anything to help me? Also, this was my code after finally finishing it with a lot of your code. I really love this and want to continue doing it.

  • Arumikaze

    Hello! I have a question about checking for invalid inputs. When I type in "42x", the program just accepts it as "42" after removing the extra inputs. Even typing in "42a238hkd" works as "42". How can I check for precise inputs? Extraction doesn't fail when doing "42x". If I can also get feedback on my code, that would be great. Thank you so much!

    • Hi Arumikaze!

      > checking for invalid inputs
      You can check if the input stream is empty, i.e. the next character is a line feed, after the extraction has been performed

      > feedback on my code
      * Line 8-10: Use uniform initialization
      * Magic numbers, declare constants
      * Line 61: Initialize chars to 0, '\0' or '\x00' when you don't have the desired value at time of initialization
      * Line 68, 69: Double comparison of @playagaininput to 'y'
      * Line 74-79: Can be replace with a do-while-loop, that way you don't need an extra variable.

  • cx

    Hello! This is my solution to the second quiz. If someone could check out my code and give me tips on what to improve or what to look out for next time, I'd appreciate it a lot! (psst nascardriver :D)

    • Hi cx!

      * Line 8, 9, 11, 20, 60, 82: Initialize your variables with uniform initialization
      * Line 8-11: These variables should be static
      * Line 25: Use @std::numeric_limits<std::streamsize>::max() rather than 32767
      * Line 39, 52, 54: You're comparing @guess to @randomNumber three times without either of those changing in between
      * Line 40: If you don't want to change @attempt, use (attempt + 1)
      * Line 39-50: You're executing ++attempt no matter which condition is met, move it outside of the if-block
      * Magic numbers: 1, 7, 100, use constants. The message says "0-100", the code says "1-100", this wouldn't have happened with constants.
      * A space before a line break is a waste of memory. A small one, but it's unnecessary.
      * You're using the same name style for variables and functions, this can be confusing

      The structure of your code looks good though.

      • cx

        Thank you for checking my code! I would have never thought that there would be so many mistakes. I'll keep these in mind so I can write better code down the line.

  • 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).

      * 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!

      * 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()

      * std::uniform_int_distribution -
      * std::mersenne_twister_engine -
      * std::mt19937 -
      * std::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.

      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
          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.