Search

6.x — Chapter P.6 comprehensive quiz

Words of encouragement

Congratulations on reaching the end of the longest chapter in the tutorials! Unless you have previous programming experience, this chapter was probably the most challenging one so far. If you made it this far, you’re doing great!

The good news is that the next chapter is easy in comparison. And in the chapter beyond that, we reach the heart of the tutorials: Object-oriented programming!

Chapter summary

Arrays allow us to store and access many variables of the same type through a single identifier. Array elements can be accessed using the subscript operator ([]). Be careful not to index an array out of the array’s range. Arrays can be initialized using an initializer list or uniform initialization.

Fixed arrays must have a length that is set at compile time. Fixed arrays will usually decay into a pointer when evaluated or passed to a function.

Loops can be used to iterate through an array. Beware of off-by-one errors, so you don’t iterate off the end of your array. Range-based for-loops are useful when the array hasn’t decayed into a pointer.

Arrays can be made multidimensional by using multiple indices.

Arrays can be used to do C-style strings. You should generally avoid these and use std::string_view and std::string instead.

Pointers are variables that store the memory address of (point at) another variable. The address-of operator (&) can be used to get the address of a variable. The dereference operator (*) can be used to get the value that a pointer points at.

A null pointer is a pointer that is not pointing at anything. Pointers can be made null by initializing or assigning the value nullptr (before in C++11, 0) to them. Avoid the NULL macro. Dereferencing a null pointer can cause bad things to happen. Deleting a null pointer is okay (it doesn’t do anything).

A pointer to an array doesn’t know how large the array it is pointing to is. This means sizeof() and range-based for-loops won’t work.

The new and delete operators can be used to dynamically allocate memory for a pointer variable or array. Although it’s unlikely to happen, operator new can fail if the operating system runs out of memory. If you’re writing software for a memory-limited system, make sure to check if new was successful.

Make sure to use the array delete (delete[]) when deleting an array. Pointers pointing to deallocated memory are called dangling pointers. Using the wrong delete, or dereferencing a dangling pointer causes undefined behavior.

Failing to delete dynamically allocated memory can result in memory leaks when the last pointer to that memory goes out of scope.

Normal variables are allocated from limited memory called the stack. Dynamically allocated variables are allocated from a general pool of memory called the heap.

A pointer to a const value treats the value it is pointing to as const.

A const pointer is a pointer whose value can not be changed after initialization.

A reference is an alias to another variable. References are declared using an ampersand (&const -- they must be initialized with a value, and a new value can not be assigned to them. References can be used to prevent copies from being made when passing data to or from a function.

The member selection operator (->) can be used to select a member from a pointer to a struct. It combines both a dereference and normal member access (.).

Void pointers are pointers that can point to any type of data. They can not be dereferenced directly. You can use static_cast to convert them back to their original pointer type. It’s up to you to remember what type they originally were.

Pointers to pointers allow us to create a pointer that points to another pointer.

std::array provides all of the functionality of C++ built-in arrays (and more) in a form that won’t decay into a pointer. These should generally be preferred over built-in fixed arrays.

std::vector provides dynamic array functionality that handles its own memory management and remember their size. These should generally be favored over built-in dynamic arrays.

Thanks to iterators, we don’t have to know how a container is implemented to loop through its elements.

The algorithms library helps us to save a lot of time by providing many off-the-shelf functions. In combination with iterators (and later lambdas), the algorithms library is an important part of C++.

Quiz time

To make the quizzes a little easier, we have to introduce a couple of new algorithms.

std::reduce applies a function, by default the + operator, to all elements in a list, resulting in a single value. When we use the + operator, the result is the sum of all elements in the list. Note that there’s also std::accumulate. std::accumulate cannot be parallelized, because it applies the function left-to-right. std::reduce segments the list, which means that the function is applied in an unknown order, allowing the operation to be parallelized. If we want to sum up a list, we don’t care about the order and we use std::reduce.

Author's note

std::reduce is currently not fully implemented in all major standard libraries. If it doesn’t work for you, fall back to std::accumulate.

std::shuffle takes a list and randomly re-orders its elements.

Possible output

10
10
2 1 4 3

Question #1


Pretend you’re writing a game where the player can hold 3 types of items: health potions, torches, and arrows. Create an enum to identify the different types of items, and an std::array to store the number of each item the player is carrying (The enumerators are used as indexes of the array). The player should start with 2 health potions, 5 torches, and 10 arrows. Write a function called countTotalItems() that returns how many items the player has in total. Have your main() function print the output of countTotalItems() as well as the number of torches.

Show Solution

Question #2


Write the following program: Create a struct that holds a student’s first name and grade (on a scale of 0-100). Ask the user how many students they want to enter. Create a std::vector to hold all of the students. Then prompt the user for each name and grade. Once the user has entered all the names and grade pairs, sort the list by grade (highest first). Then print all the names and grades in sorted order.

For the following input:

Joe
82
Terry
73
Ralph
4
Alex
94
Mark
88

The output should look like this:

Alex got a grade of 94
Mark got a grade of 88
Joe got a grade of 82
Terry got a grade of 73
Ralph got a grade of 4

You can assume that names don’t contain spaces and that that input extraction doesn’t fail.

Show Solution

Question #3


Write your own function to swap the value of two integer variables. Write a main() function to test it.

Show Hint

Show Solution

Question #4


Write a function to print a C-style string character by character. Use a pointer to step through each character of the string and print that character. Stop when you hit the null terminator. Write a main function that tests the function with the string literal “Hello, world!”.

Show Hint

Show Solution

Question #5


What’s wrong with each of these snippets, and how would you fix it?

a)

Show Solution

b)

Show Solution

c)

Show Solution

d)

Show Solution

e)

Show Solution

Question #6


Let’s pretend we’re writing a card game.

a) A deck of cards has 52 unique cards (13 card ranks of 4 suits). Create enumerations for the card ranks (2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, King, Ace) and suits (clubs, diamonds, hearts, spades). Those enumerators will not be used to index arrays.

Show Solution

b) Each card will be represented by a struct named Card that contains a rank and a suit. Create the struct.

Show Solution

c) Create a printCard() function that takes a const Card reference as a parameter and prints the card rank and suit as a 2-letter code (e.g. the jack of spades would print as JS).

Show Hint

Show Solution

d) A deck of cards has 52 cards. Create an array (using std::array) to represent the deck of cards, and initialize it with one of each card. Do this in a function named createDeck and call createDeck from main. createDeck should return the deck to main.

Hint: Use static_cast if you need to convert an integer into an enumerated type.

Show Solution

e) Write a function named printDeck() that takes the deck as a const reference parameter and prints the cards in the deck. Use a range-based for-loop. When you can printDeck with the deck you generated in the previous task, the output should be

2C 3C 4C 5C 6C 7C 8C 9C TC JC QC KC AC 2D 3D 4D 5D 6D 7D 8D 9D TD JD QD KD AD 2H 3H 4H 5H 6H 7H 8H 9H TH JH QH KH AH 2S 3S 4S 5S 6S 7S 8S 9S TS JS QS KS AS

If you used different characters, that’s fine too.

Show Solution

f) Write a function named shuffleDeck to shuffle the deck of cards using std::shuffle. Update your main function to shuffle the deck and print out the shuffled deck.

Reminder: Only seed your random number generator once.

Show Solution

g) Write a function named getCardValue() that returns the value of a Card (e.g. a 2 is worth 2, a ten, jack, queen, or king is worth 10. Assume an Ace is worth 11).

Show Solution

Question #7


a) Alright, challenge time! Let’s write a simplified version of Blackjack. If you’re not already familiar with Blackjack, the Wikipedia article for Blackjack has a summary.

Here are the rules for our version of Blackjack:

  • The dealer gets one card to start (in real life, the dealer gets two, but one is face down so it doesn’t matter at this point).
  • The player gets two cards to start.
  • The player goes first.
  • A player can repeatedly “hit” or “stand”.
  • If the player “stands”, their turn is over, and their score is calculated based on the cards they have been dealt.
  • If the player “hits”, they get another card and the value of that card is added to their total score.
  • An ace normally counts as a 1 or an 11 (whichever is better for the total score). For simplicity, we’ll count it as an 11 here.
  • If the player goes over a score of 21, they bust and lose immediately.
  • The dealer goes after the player.
  • The dealer repeatedly draws until they reach a score of 17 or more, at which point they stand.
  • If the dealer goes over a score of 21, they bust and the player wins immediately.
  • Otherwise, if the player has a higher score than the dealer, the player wins. Otherwise, the player loses (we’ll consider ties as dealer wins for simplicity).

In our simplified version of Blackjack, we’re not going to keep track of which specific cards the player and the dealer have been dealt. We’ll only track the sum of the values of the cards they have been dealt for the player and dealer. This keeps things simpler.

Start with the code you wrote in quiz #6. Create a function named playBlackjack(). This function should:

  • Accept a shuffled deck of cards as a parameter.
  • Implement Blackjack as defined above.
  • Returns true if the player won, and false if they lost.

Also write a main() function to play a single game of Blackjack.

Show Solution

b) Extra credit: Critical thinking time: Describe how you could modify the above program to handle the case where aces can be equal to 1 or 11.

It’s important to note that we’re only keeping track of the sum of the cards, not which specific cards the user has.

Show Solution

c) In actual blackjack, if the player and dealer have the same score (and the player has not gone bust), the result is a tie and neither wins. Describe how you’d modify the above program to account for this.

Show Solution


7.1 -- Function parameters and arguments
Index
6.18 -- Introduction to standard library algorithms

816 comments to 6.x — Chapter P.6 comprehensive quiz

  • hugo

    Hey. Thanks for coming up with this great coding practice BlackJack! Hope could get some feedback on the code I wrote! (No changes for extra credits)

    • nascardriver

      - Use single quotation marks for characters (' ' vs " ", '\n' vs "\n"). Strings are slower.
      - Line 126, 128: Magic numbers, use your enums.
      - `getValue` can be reduced to

      - Your `printCard` functions should be right after one another.
      - You don't need `ifHit::choice`, you can return right away.
      - Line 242: You don't need the conditional operator. The comparions yields a `bool` already.

      Good job :D

  • Patricia

    In question two in the end of the program why do you use references ?

  • Abhishek A Udupa

    Good day!

    I've verified the code and it works. Could you please check and let me know for improvements. Thanks!
    By the way before I forget: Who ever you are, thank you for spending your time make our's better. Bless you :)

    • nascardriver

      - Disable precompiled headers and remove `#include "pch.h". Your code only works in VS.
      - Line 87, 88: Magic numbers.
      - Line 97 and more: Iterate with const references. Just like calling functions, iterating over copies is slow.
      - Line 111-115: You could `[[fallthough`]] here to prevent the repetition of the return-statement.
      - Line 139 and more: You have `index_t`.
      - Line 147: You want aces to have special handling, not cards with value 11.
      - Your ace advantage check requires a lot of code from the caller. It's easier to write a function that uses the best ace value possible. Have a look at some other submissions or the solution.

      Very good code structure, nice types, keep it up :)

  • Tom

    At question #2, I don't understand why my answer doesn't work. It seems pretty similar to the Solution. Can anyone see the mistake I couldn't find?

    • nascardriver

      "doesn't work" doesn't help.
      What do you expect to happen?
      What actually happens?
      Which warnings/errors are you getting?
      Where does the problem occur in the code?

      Given your code is short, I spotted a mistake, but don't know if that's the one you're stuck at. Please always answer the above questions if you have a problem.

      In line 30, you're iterating over the elements by value, ie. you're creating a copy of every element. You then fill their name and grade, and the copy gets discarded. Iterate over the elements by reference

      • Tom

        Thank you very much! That was exactly my problem!
        And, yeah, you're right, my comment wasn't very descriptive. I will keep that in mind for next time.

  • Chromewardroid

    Hi, I am struggling my way through this and just need someone to point me to a page that explains how/why to use this line below in the first exercise.

    I sort of understand why we need it but don't remember seeing anything in the lessons.
    Thanks (and great work!)
    CWD

    • nascardriver

      We don't _need_ it, we could replace every `inventory_type` with `std::array`, but that's more typing, requires more maintenance, and allows for human error. By declaring the type as a type alias, our code is way easier to update. Type aliases are covered in lesson 6.13.

  • Ahmed

    why we used auto instead of std::vector <Students> ?

  • The solution to question 2 does not contain the necessary '#include <vector>'...

  • Am

    So, I took my own liberties and tried to go a little beyond your bar of asking so I hope you're happy to see the work I've put in! :)

    Some things I opted to try and implement:
    >Player and Dealer Tie
    >Limited Card Deck (drawn card cannot be re-drawn)
    >Keeping track of what cards are in the Dealer and Player's hand (using Struct containing an "std::vector")
    >Toggling Card Value of Ace (and re-calculating Hand Sum) when "Hand Sum" exceeded 21.

    Also curiously, sometimes does std::array or std::vector throw the exception "Expression: subscript out of range" when running it, but most of the time it runs just fine.
    (Curious what exactly is causing the inconsistency? I'd love to know!)

    Here's my Blackjack game!
    "constants.h"

    Blackjack code:

    As always, thanks for your continued efforts, I really hope you enjoyed taking a look at my abomination of code haha~

    • Am

      Must have forgotten to update this section to reflect the implementation of Limited Deck in the above:

      and realizing now upon testing it out that getting Double Aces turns all Aces into a 1 Value, fixed that with:

      Since we only need to change one Ace if we go over.

    • nascardriver

      Hi!

      - Magic type `std::array`. Use a type alias to decrease typing and increase maintainability.
      - Line 102-121 should be moved into their own function. It's likely that you'll want to get a card's value somewhere else too.
      - re-shuffling doesn't help. If the mersenne twister is random (Which we assume), once random is the same as twice random. Say you roll a die. It doesn't get any more random if you pick it up and roll it again. Similarly, drawing a random card from a shuffled deck doesn't add randomness. It's the same as drawing the first card.
      - Avoid abbreviations. They save time typing, but waste time whenever they're read. And they're read more often than they're written.
      - I'd expect a function called "calculateX" to do a calculation and give the the result of the calculation. It shouldn't modify anything and not return anything it didn't calculate.
      - You're going nuts on the returns. Copying data is slow and should be avoided. You take your parameters by reference. Modifying them affects the original object, there's no need to return it (Other than for chaining, in which case you shouldn't return by value).
      - Line 176, 180: Magic number. This is an important number, because you're using it for indexing, which can lead to issues as the one you're having with the out-of-range index. Use `cardDeck.size()`.
      - Line 182 should be a plain `else`.
      - Line 184 doesn't do anything. Your compiler should've warned you about this. Did you mean to use `=`?
      - By returning `playerContinue` from `playAgain` you're adding work, because the caller has to check the value again. There are only 2 options, use a `bool` like you did in `playerDecision`.
      - Line 160, 163: Avoid shadow variables (Different variables that are both visible in a scope but have the same name). This will quickly lead to trouble.

      > Player and Dealer Tie
      You don't need to check `playerBust` and `dealerBust` in line 323. If either of them is `true`, one of the previous cases in the if-statement will have been entered already.

      > Limited Card Deck
      You're brute forcing your way through the deck until you find a card that hasn't been drawn yet, that's slow. If you shuffle the deck first (But only one time), you can always pick the card that's "on top of the deck", ie. keep track of which index is on top.

      > Keeping track of what cards are in the Dealer and Player's hand
      That's looking alright. Since you're using a `std::array` and not re-shuffling the array, you could store pointers in the player's hands to prevent copying the cards. Depending on what you want to do with the hand, a copy might just be what you need.

      > Toggling Card Value of Ace
      You already found the double-ace issue. If you implement this function in a non-modifying manner, you could either count the aces the player has or you can keep track of up to which index you already iterated to prevent iterating over the same range twice.

      > sometimes does std::array or std::vector throw the exception "Expression: subscript out of range"
      Using your debugger, you should see exactly where this exception is coming from and which values are causing it.

      > what exactly is causing the inconsistency?
      You're using randomness. Random things fail in random situation ;)

      That's a lot of comments, don't feel discouraged, you did a great job! Going beyond the quiz questions is a great way to learn more :D
      If you have any questions about my comments, please ask and I'll elaborate.

      • Am

        Huge thanks once again! I really appreciate the feedback you've left me (it was really insightful and made me realize there's many different approaches to one solution), after checking over the code again it looks like I've done an "Off-by-One" error with the Random Card Indexing should have been (0, 51), and it works perfectly now.

  • Nathan

    Hows this?

    • nascardriver

      - Initialize variables with brace initialization for higher type-safety.
      - If you're checking for a limited set of values, use a `switch` rather than `if-else-if`.
      - Initialize random number generators only once. Initializing them multiple times can cause the sequence to be reset.
      - Using single quotation marks for characters ('A' vs "A" etc.). Strings are more expensive.
      - If you don't modify a pointer/reference parameters, make it `const`.
      - You can use a type alias to get rid of the repetitions of the deck type.
      - Line 118, 120: Magic numbers. Use `deck.size()` and your enum.
      - Line 159: You want to know if the card is an ace, check the card's type, not its value.
      - If you don't use a variable's initial value, initialize it with empty curly braces to avoid confusion. (`hitStand`).
      - There's duplicate code in `gameManager`. When you notice duplicate code, try moving it into a separate function.

  • Sepon

    Hi guys, I think I made something wrong, but I'm not sure.
    Regarding question 6d, I thought that I have to create deck inside of the createDeck function, so I made it static and asked the function to return by reference:

    Is it wrong to do so?

    Thank you in advance

    • nascardriver

      Yes that's wrong. Don't use `static` for anything that could be used multiple times but not be the same. With your implementation, you can't play two games at the same time.

  • Malachi

    Thanks so much for the lessons. I've been enjoying them heaps. I decided to do a little extra with the last question. What do you think of this:

    • nascardriver

      Hi!

      - "functions" is a poor name. The functions in your namespace do IO (Input/output), so "io" would be a better name.
      - Pass fundamental types by value, non-fundamental types by reference. This includes loop variables (eg. line 27).
      - Checking `!validInput` in line 36 doesn't make sense. There's no reason why `!validInput` would imply that the input was corrupted.
      - Use single quotation marks for characters ('2' vs "2", etc.). Full quotation marks means that the value is a string. Strings take up more memory and they're harder to process.
      - Use ++prefix unless you need postfix++. ++prefix is faster.
      - Line 160, 162, 253: Magic numbers, use your enums.
      - Nice reusable functions!
      - `addCardFromDeckToHand` and `addCardsFromDeckToHand` contain duplicate code. Separating them into 2 functions is good, but the actual drawing of cards (Line 223-226) can be moved into a third function.
      - Line 224-246: Use `cardIsAce`.
      - You don't need `std::to_string` in combination with io streams. IO streams can convert types themselves.
      - `getPlayerAction`: Avoid recursion. Use loops instead. We'll talk about error handling later, then you can abort when a bug (ie. invalid return value of `getChar`) occurs.
      - Avoid `continue` and `break`. Your `break` can be avoided by using a do-while loop.

      `std::string` has a `find` member functions, which searches for a substring (or character).

      I can see that you put a lot of effort into your code, keep up the good work! In your ace handling, you don't need `acesAs1`. You can decrease `aces` until it hits 0.

  • ntdong2002

    - In question 2, I use vector and sort in algorithm and some reference from stackoveflow.com. My code is here. I will try to use pointer in modified code.

    - Thank for all comments to make me do better.

    • nascardriver

      A `std::vector` and `std::sort` were the right choice, good job!

      - Inconsistent formatting. Use your editor's auto-formatting feature.
      - Non-fundamental types should be passed by const reference, not by value. Passing them by value is usually slow.
      - Use ++prefix instead of postfix++. Postfix++ is slower.
      - Calling `std::cin.ignore()` without arguments ignores only 1 character.

  • wuntwo

    I'd like to thank everyone who's constantly working on enhancing and updating the website, you're doing a laborious work for virtually no payback for the website's readers' benefit. Also, your quizzes are quite challenging and fun to do, and the best thing about them is that it makes the reader utilize everything they've learned thus far.

    I'd like to have your input on my take on the last question: