#+TITLE: Project 2: Hangman #+AUTHOR: JirR02 * Project Overview The goal of this project is to implement a version of the popular guessing game known as Hangman (German: Galgenmännchen). The rules of the game are simple: the first player picks a word which the second player has to guess character by character — with a limited amount of guesses. When played on paper, a snapshot of the game typically looks as follows: [[./game.png]] or your version, we focus on the essence of the game: 1. Choosing a word and initialising the game 1. Repeatedly guessing characters, and uncovering them if they occur in the chosen word 1. Eventually announcing that the player won ... or lost ** A primer on strings and chars In =C++= (and in most other programming languages), sequences of characters, such as the word the player needs to guess, can be represented as strings. Strings will be introduced properly in your later, regular computer science course; for now, you only need to know the following about strings in order to implement Hangman: 1. Strings and operations thereon are provided by the *string* library: #+begin_src cpp #include using std::string; #+end_src The first line allows us to use the =c++= *string* library, the second line allows us to directly use =string= as a data type, instead of having to write =std::string= everywhere. 1. A variable named 'word' of type string can be declared as follows: #+begin_src cpp string word; #+end_src 1. String-typed variables can be set as follows: - By using string literals, which are enclosed in quotation marks: #+begin_src cpp word = "potato"; #+end_src - By reading a string from the keyboard: #+begin_src cpp std::cin >> word; #+end_src - By assigning it another string #+begin_src cpp word = chooseWord(); #+end_src The function chooseWord() returns a string value which is then assigned to the variable word. 1. The length (type unsigned int) of a string can be determined by using the length function, which returns the number of characters in the string: #+begin_src cpp string word = "hangman"; std::cout << word.length(); // outputs: 7 #+end_src 1. The nth character (type char) of a string can be accessed using the at function: #+begin_src cpp string word = "hangman"; std::cout << word.at(0); // Output: h (the first character) std::cout << word.at(6); // Output: n (the last character) #+end_src Note that the first character is at position 0 and the last character at position length() - 1! The nth character can also be replaced by using the at function, e.g. #+begin_src cpp string workingCopy = "_______"; workingCopy.at(3) = 'n'; std::cout << workingCopy; // Output: __n____ #+end_src where '_' denotes a single character (of type char). 1. Similar to whole strings, you can also declare variables for single characters (type char) and read them from the keyboard: #+begin_src cpp char guess; std::cin >> guess; #+end_src ** Stepwise development The program template that you start with has been developed using the /stepwise refinement/ technique, and just like for project 1, task 2, contains master implementations (functions =PART1=... etc.) that you must replace with your own code. We also provide you with output-generating functions (e.g. =printYouWon()=) that make it easier for you to please the tests (and thus the autograder). As before, we suggest that you develop your program step-by-step, and run or test it often. I.e. implement a single part, such as reading the next guess from the keyboard or updating the working copy, and then run and test the program to see if it (still) works as intended. ** Implementing the game Your job is to implement three major parts of the actual algorithm to play the game. As before, each part can be implemented separately. The following variables are already declared in the template given to you: - =string= word: This variable contains the word that must be guessed - =string= workingCopy: This variable is initialized with a string that has the same size as the string word. Each character in this string is initialized with an underscore (_). - =bool= done: If this variable is set to true the program will not play another round of the game (both in the case of winning and losing). - =int= wrongGuesses: This variable is initialized with 0 and must be incremented by one every time a guessed character doesn't occur in the word to guess. - =int= maxWrongGuesses: This variable is initialized with constant 6. Don't change it. - =bool= found: This variable indicates whether (true) or not (false) the guessed character occurs in the word to guess. *Note:* Each of the following parts is already implemented in a master implementation function (=PART=...), which you need to replace with your own code. The corresponding places in the code are marked by TODO comments. *** Part 1 This part is about reading the next guess from the keyboard. Comment out the call to =PART1_readCharacter= and replace it by an implementation that performs the following steps: 1. Output the text " =Your guess:= " 1. Read a single character from the keyboard into variable guess. You already know how to output text; see the string primer above (point 6) for how to input a single character. *** Part 2 This part involves checking if the guessed character occurs in the word, and updating the =workingCopy= accordingly: by replacing the underlines at the respective position(s) by the guessed character. Variable found indicates if the guessed character was found in the word. Comment out the call to =PART2_updateWorkingCopy= and replace it by an implementation that performs the requested operation. *Example (found):* If variable word is set to "success", variable =workingCopy= still contains the initial underlines ("=_______=") and the current guess is '=c=', then two actions must be performed: 1. Variable found must be set to =true=, because '=c=' occurs in the word "success" 1. All occurrences of the letter '=c=' must be uncovered in the working copy, thus variable =workingCopy= must be changed to have the content "=__cc___=" *Example (not found):* If variable word is set to "success", variable =workingCopy= still contains the initial underlines ("=_______=") and the current guess is '=x=', then the following is important: 1. Variable found must remain =false= (because '=x=' does not occur in "success" 1. Variable =workingCopy= must not be changed. *** Part 3 This part is about determining if the game continues, or if it is already won or lost. Comment out the call to =PART3_updateGameState= and replace it by an equivalent implementation of your own, as described next. If the player made a correct guess and we uncovered at least one further character in the working copy (in which case variable found has value =true=), and if variable =workingCopy= now equals variable word, then the game has been won. Two things must happen in this case: 1. Set variable done to true, to indicate that the game must not continue. 1. Call =printYouWon(word)=, which will output the result of the game (in a format required by the autograder). Otherwise, if the player guessed incorrectly (in which case variable found is =false=), variable =wrongGuesses= must be incremented by one. If this variable is afterwards equal to =maxWrongGuesses= then the game is lost, in which case the following two things must happen: 1. Set variable done to =true=, to indicate that the game must not continue. 1. Call =printYouLost(word)=, which will output the result of the game (in a format required by the autograder). In all other situations, just make sure that variable done remains =false=. This ensures that the game continues. ** Submitting your solution *Important:* you must replace all three master implementations (calls to functions =PART1/2/3=) with your own code! Your submission /will not be accepted/ if it still uses the master implementations, regardless of how many tests pass when you run them. To successfully complete this project, your solution needs to pass 12 out of 17 tests, i.e. you don't need to score 100%. ** Hints - Checking for equality of strings can be done using the ==== operator, the same way you would check equality of numbers or booleans. - Pimp your implementation by calling =showHangman(i)= to draw the gallows after =i= incorrect guesses (for i between 0 and 6) - You don't need to account for German umlauts, French accents etc. (such as ö or é) - You do not need to handle duplicated guesses in any special way. E.g. if the player guesses X twice, then either nothing happens the second time around (if X occurs in the word) or the player loses a point each time (if X does not occur in the word). - If you're interested (and brave enough), you can find more information about strings at https://en.cppreference.com/w/cpp/string * Solution #+begin_src cpp #include #include #include "hangman.h" using std::string; int main() { // Word the player needs to guess (randomly selected) string word = chooseWord(); //string word = "success"; // Initialise the "uncovered" word that is shown to the player: the uncovered // word is obtained by replacing each letter from the original word (variable // word) with an underscore (i.e. _). string workingCopy = createWorkingCopy(word); // This variable indicates whether or not the game is over bool done = false; int wrongGuesses = 0; // Number of wrong guesses int maxWrongGuesses = 6; // Maximum number of wrong guesses (don't change) // Draw the empty gallow showHangman(0); // Game loop (each iteration is a round of the game) while (!done) { printGameState(maxWrongGuesses, wrongGuesses); printWorkingCopy(workingCopy); /** Part 1: input next guess **********************************************/ char guess = '\0'; // TODO: replace the following line with your implementation //PART1_readCharacter(guess); std::cout << "Your guess: "; std::cin >> guess; /** Part 2: update working copy *******************************************/ bool found = false; // TODO: replace the following line with your implementation //PART2_updateWorkingCopy(word, guess, workingCopy, found); for (int i = 0; i != word.length(); i++) { if (guess == word.at(i)) { found = true; workingCopy.at(i) = guess; } } /** Part 3: update game state *********************************************/ // TODO: replace the following line with your implementation //PART3_updateGameState(word, workingCopy, found, maxWrongGuesses, done, wrongGuesses); if (workingCopy == word) { done = true; printYouWon(word); } else if (found == false) { wrongGuesses += 1; } if (wrongGuesses == maxWrongGuesses) { done = true; printYouLost(word); } } return 0; } #+end_src ----- Made by JirR02 in Switzerland 🇨🇭