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:
* If your compiler is C++11 capable, use the Mersenne Twister algorithm from chapter 5.9 -- Random number generation to pick a random number.
* If your compiler is not C++11 capable, you can use rand() (also presented in 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.

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

624 comments to 5.x — Chapter 5 comprehensive quiz

  • Parsa

    What is the difference between using if multiple times rather than using if once and then else if?

    And here is my hilo solution-

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability.
      - Don't use `goto`. Loops are easier to read.
      - Inconsistent formatting. Use your editor's auto-formatting feature.
      - Magic number/string: 7.
      - Seed random number generators only once.
      - Don't use `std::exit`.
      - Use ++prefix unless you need postfix++.
      - Line 63 always evaluates to true.
      - Line 28+, 45+: Duplicate code.
      - Line 41: `guess != randomNumber` is always true.

      • Parsa

        -I don't understand what I am supposed to do with the magic number 7.

        -Line 41 and 63: don't understand how it always evaluates to true.

        Thank you by the way.

        • > I don't understand what I am supposed to do with the magic number 7
          Add a `constexpr` variable and use that wherever you used 7.

          > don't understand how it always evaluates to true
          Line 41
          The check only runs if line 24 was `false`. If line 24 was false, then you already know that `guess != randomNumber`.

          Line 63
          You know `!(guess == randomNumber)` because of line 24.
          You know `!(guess < randomNumber)` because of line 59.
          The only relation that the two variables could have is `guess > randomNumber`.

  • Parsa

    When would it be better to use operator= versus brace initialization? Since they seem to be able to do different things.

  • `operator=` is used for assignments. I suppose you mean copy initialization.

    Always use brace initialization unless you can't (Because of `std::initializer_list`. Covered later).

  • alfonso

    My hi-lo solution:

    main.cpp

    functions.cpp

    functions.h

    globals.h

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability.
      - Don't pass 32767 to `std::cin.ignore`. Pass `std::numeric_limits<std::streamsize>::max()`.
      - `tooHigh`, `tooLow`, `itMatches`: That's what enums are there for.
      - functions.cpp:58+ and `guess`: You didn't gain anything from adding `guess`.
      - Use `switch` for limited sets of values.
      - functions.cpp:36,37: `std::swap`.
      - functions.cpp:46 Good use of the constant!
      - `userNumber` and `match` should be declared inside of the loop.
      - functions.cpp:84,85: Why do you assume an error when the input wasn't 'y' or 'n', but when the input is 'y' or 'n', you're sure there is no trailing input?
      - `seed` isn't a seed, it's a mersenne twister. The seed is the part you're initializing `seed` with.
      - You're using the same name style for variables and functions, this can lead to confusion.

      • alfonso

        > - Use `switch` for limited sets of values.

        I wanted to use keywords like break and continue inside the for loop, but depending on switch cases. So then break would apply to switch statement. Using if statement instead, I do not need to have care about conflicts using brake inside a switch inside a loop.

        > `userNumber` and `match` should be declared inside of the loop.

        I deleted match, and guess() function and tooHigh, tooLow global constants. Why I declared variables outside the loop: for performance reasons. Making the variable outside means making the variable once and then only assigning values in the loop for it. I braked the rule 'smallest scope as possible'.

        Thank you for your reply.

        Btw, I can win this game every time. :) If you choose right, at 6th move, you'll have two 'moves' and only two possible answers.

        • > switch
          functions.cpp:79+ Doesn't use `break` or `continue`.

          > Making the variable outside means making the variable once and then only assigning values in the loop for it
          This can be a valid argument for custom types, but not for fundamental types. The space of the variable is re-used, there is not creation or destruction of fundamental types. You're increasing the scope without gaining anything from it.

          > If you choose right, at 6th move, you'll have two 'moves' and only two possible answers.
          Good observation! This reminds me of a problem with an egg and a skyscraper. If you get into algorithms, you're bound to run into it.

  • Parsa

    This code won't compile ( I know my function and variable names aren't great and aren't consistent)

    • Always post error messages along with their line numbers.

      Brace initialization enforces fairly strict conversion rules. You can't initialize a `double` from an `int`. Add a `static_cast<double>`. Change the `2` in your `distanceFallen` calculations to a `2.0`. `2` is an integer, `2.0` is a `double`.

  • samtheham

    Hi Alex/Nascardriver! Thanks again for all the fantastic work on this site. If you could help me look over my code (Q2b) and let me know what improvements can be made, it would be greatly appreciated. My approach for the loops was a bit different from your solution, relying mostly on infinite loops and breaks rather than for statements. I do this mostly because I personally find it easier to follow the logic, but if there's good reason to use for statements instead, I'd appreciate the pointer. (Oh and I use #pragma once because I'm really lazy to type header guards for exercises, sorry) Code as follows:

    main.cpp:

    input.h:

    input.cpp:

    random.h:

    random.cpp:

    playagain.h:

    playagain.cpp:

    • - Limit your lines to 80 characters in length for better readability.
      - Initialize your variables with brace initializers.
      - Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().
      - Use `switch` for a limited set of values.
      - Use `constexpr` for compile-time constants.
      - input.cpp:18 isn't guarded by the `else`. Use your editor's auto-formatting feature, braces for `if`s, and enable warnings.
      - Magic string: 7.
      - "main.cpp" has a bunch of unnecessary includes.
      - main.cpp:29 always compares `true`.

      > good reason to use for statements
      Use what shows your intentions best. Doing so improves readability and allows the compiler to apply appropriate optimizations. `for` doesn't "leak" its iteration and (if you don't use `break`) it's easy to see when it will stop. If you use an infinite loop instead, the read has to read through the entire loop's body to see how long the loop will run.

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    This is my quiz no. 2. What do you think?

    I wanna ask about something inside checkHiLoNumber(). In the else statement I write

    We know what is the case of else statement by looking other statements above else statement (if and elseif): userGuessNumber == matchNumber. I wanna know your opinion. Should I give the else statement an independent context like

        
    so, we know explicitly every checking statement. I'm glad to hear yours! Thanks a lot!

    • Uhh, it started very good, then came `main`.

      - Line 40 has no effect. You don't need to `return` from `void`s and there's no code after this line.
      - Line 81, 81, 97: Magic strings/number: 1, 7, 100.
      - Line 72 should be moved into the loop. Then you don't need line 97.
      - Line 91 always compares true. You're not ending the game when the player won.

      > Should I give the else statement an independent context like
      No, that'd be wasted performance. There are types where (a < b) (b < a) (a == b) can all be false at the same time, `int` isn't one of them. If your conditional code is getting too long, you should add a comment to the `else`, saying that this is the case where (userGuessNumber == matchNumber), but don't check for it.

      • Samira Ferdi

        Thanks for the good feedback, Nascardriver!
        So, this is my change:

        1. I change all int type to short because I think short is the best fit data type (the number is very small - between 1 and 100). I'm thinking to make all my variable unsigned (except may be all variable that used in loop). What do you think about this?

        2. I delete checkHiLoNumber(). I move checking guessing thing in main function for performance reason

        3. main()

        • 1. Use `int` unless you have special needs. If you have special needs, use `std::int_fast*_t` or `std::int_least*_t`. Don't use `unsigned`, see lesson 4.5.

          2. You're not gaining any performance from putting everything into one function. All you did was make the code less readable and reusable.

          • Samira Ferdi

            Thank you, Nascardriver for your correction!

            This is my change!

            • Now you're checking every input twice (Once in the if and once in the switch). If you don't want to print from inside `checkUserGuessNumber`, moving the if back into `playGame` (Like you did before) is better.
              I didn't think `switch` was covered yet. Line 91+ should use a `switch`.
              The rest looks good now!

              • Samira Ferdi

                Thank you, Nascardriver! Yep, your right! I actually checking number twice!

                This is my change!

                1. I delete enum, checkUserGuessNumber() ( I move checking number things to playGame() ).

                2. Yes, you right again, in line 91+ ( playAgain() ), I should use switch instead of if-else.

  • learning

    Hey, this is my solution for 2a & 2b. Is there anything which I can do better? Thanks in advance!

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability on small displays.
      - Line 61, 62: Duplicate comparison.
      - Line 62: `return (cSelection == 'y');`.
      - Line 78+: Should be a do-while loop.
      - If your program prints anything, the last thing it prints should be a line feed.

  • Avijit Pandey

    Here's my approach to the solution for the hi-lo game. I wanted to ask for feedback, like what mistakes I've made that I'm not noticing, or bad practices that I'm still used to, or if I'm doing something that can be done in 2 lines, in 10 lines.

    • Hello!

      - Line 18, 20: Duplicate comparison.
      - Avoid abbreviations. Use descriptive variable names.
      - `noOfTries` should be `constexpr`, because it's compile-time constant.
      - Line 25: Magic string: 7
      - Line 39 always compares true.
      - Line 42 is unreachable.
      - `getRandomNumber` is unused and should be removed (Or re-implemented by using the mersenne twister).
      - Line 51, 52: Initialize with brace initializers.

      • Avijit Pandey

        Thanks for the feedback, much appreciated!
        I'm new to programming so I didn't completely understand these two points, can you explain in a little more detail please?
        -Line 39 always compares true.
        -Line 18, 20: Duplicate comparison.

        • > always compares true
          You already know that `userGuess` is not less than or equal to `answer` because of the two preceding checks. The only relation between the numbers that's left is that `userGuess` is larger than `answer`.

          > Duplicate comparison
          You're comparing `choice` to 'y' twice without modifying `choice` in-between. The two comparisons will yield the same result. That's a waste of processing power (Negligible for `char`, but it can be a lot for other types).
          You would use a `while (true)` loop and `switch` inside the loop. When it's `y` or `n`, you return from inside the loop.

  • Jan

    Hello,

    could you give me some feedback on my code (2a).
    I'm not sure if I took the right approach here by using the mersenne algorithm with the static keyword in an own function instead of the main function.

    (main.cpp)

    • - Don't pass 32767 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max().
      - Don't use `std::system`, it won't work on other operating systems.
      - Line 31: Magic string: 7.
      - `maxGuesses` should be `constexpr`.
      - Line 50 always compares true.
      - Line 38: You know how often the loop will run. Use a `for`-loop.
      - Initialize your variables with brace initializers.

      > I'm not sure if I took the right approach here
      It's fine.

  • potterman28wxcv

    Hello, I wrote the answer to 2a) in a different style than the one you used. I would be curious to hear your thoughts about it. Thanks a lot for these very in-depth tutorials!

    • Hello,

      - Initialize your variables with brace initializers.
      - Line 69: Use `while (askPlayAgain());` and remove `playAgain`.
      - You're using the same name style for variables and functions, this can lead to confusion.
      - Magic number: 20000. Pass `std::numeric_limits<std::streamsize>::max()`.
      - Line 59, 61: Duplicate comparison of `play` to 'y'.
      - Line 55 will fail if the stream is in a erroneous state. You need to `std::cin.clear()` first.
      - Inconsistent formatting. Use you're editor's auto-formatting feature.
      - Inconsistent namespace naming (lower case vs upper case).
      - Use --prefix unless you need postfix--.

      Good use of `constexpr` and no recursion!

      • potterman28wxcv

        Hello, thanks for your feedback!

        > - Use --prefix unless you need postfix--.

        I indeed noticed in the rest of the tutorial that this form was privileged.

        Is there any particular reason for this? Or is it more of a coding style?

        • If you don't remember the differences, re-read lesson 5.4.
          Postfix-- has to create a copy of the variable, which takes time and memory.
          --Prefix modifies the variable in-place and doesn't need any copies.

        • Alex

          I'm in the process of revamping all of the lessons and bringing them up to the latest best practices, as well as integrating new material. This is taking a while, so the later lessons haven't been retrofit yet. If you see an inconsistency, that's why.

          • potterman28wxcv

            It wasn't really an inconsistency - actually so far the use of ++i is pretty consistent.

            Thanks a lot for the tutorials! They help a lot

  • ratlab

    How can I improve this?

    (Main.cpp)

    (InputHandler.h)

    (InputHandler.cpp)

    (GameLogic.h)

    (GameLogic.cpp)

    (Randomizer.h)

    • - Initialize your variables with brace initializers.
      - Limit your lines to 80 characters in length for better readability on small displays.
      - If you use something, include its header. <utility> for `std::pair` in "main.cpp".
      - `while` wants a `bool`. Use `while (true)`.
      - `getNumber`, `getTries` and `getRange` are mostly duplicate code and should be merged.
      - GameLogic.cpp:13+ should be a `switch`.

      You're one of the very few who seeded their rng only once and didn't use magic numbers, good job!

  • Sam

    Here is my updated solution with a struct and various other fixes (Question #2)

  • Sam

    Here is my solution for Question #2 with full error checking/restart module. Any feedback is appreciated (nascardriver ;))

    I feel like this can definitely be refactored a ton, and the entire logic/structure of the program can be improved/re-written.

    • - Initialize your variables with brace initializers.
      - Use std::time instead of its global-namespaced counterpart.
      - `streammax` should be constexpr.
      - `std::tolower` doesn't modify its argument. You're not detecting an upper case 'N'.
      - `playAgain` should return a `bool`.
      - Put line breaks at the end of lines, not the beginning.
      - Line 52, 56: Repetition. Use the conditional operator to print "lives" or "life".
      - Magic number: 1, 100
      - Use braces for initialization only. Using them for assignments looks like initializer lists (Covered later).
      - `main`: Missing return statement.

  • Joe

    Hi @nascardriver if you're reading this could you review my code to please, this is what I had for the solution

    (Main.cpp)

    (HiLoGame.h)

    (HiLoGame.cpp)

    • Hi Joe!

      - Main.cpp:12: Initialize your variables with brace initializers.
      - Main.cpp:25, 27: Duplicate comparison. Move line 19-27 into its own function that returns a bool. Use that function as `main`'s loop's condition.
      - Use `constexpr` for compile-time constants.
      - HiLoGame.cpp:11: Magic number: 7.
      - `struct`s shouldn't contain functions. You'll learn about `class` later. It's the same thing, but classes are assumed to provide operations on their data. `struct` is usually used for storage alone. It's fine for now.
      - HiLoGame.cpp:29-35 should be checked before asking the user for another input.
      - HiLoGame.cpp:36-41: Should be moved to HiLoGame.cpp:51 and the condition removed.
      - `HiLoGame::checkStatus` would be better implemented as returning a `bool` and without `isDone`.

Leave a Comment

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