JirR02 88e0b5ed69 Converted everything to orgmode
converted everything to orgmode and added solution to the README files
2025-03-31 08:40:43 +02:00

11 KiB

Project 2: Hangman

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:

/JirR02/ETH_Informatik/media/commit/88e0b5ed69a91709d2a4b08d2bda1c80ccb7d075/Vorkurs/Projekt_2/game.png

or your version, we focus on the essence of the game:

  1. Choosing a word and initialising the game
  2. Repeatedly guessing characters, and uncovering them if they occur in the chosen word
  3. 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:

     #include <string>
     using std::string;

    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.

  2. A variable named 'word' of type string can be declared as follows:

    string word;
  3. String-typed variables can be set as follows:

    • By using string literals, which are enclosed in quotation marks:

      word = "potato";
    • By reading a string from the keyboard:

      std::cin >> word;
    • By assigning it another string

      word = chooseWord();

      The function chooseWord() returns a string value which is then assigned to the variable word.

  4. 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:
string word = "hangman";
std::cout << word.length(); // outputs: 7
  1. The nth character (type char) of a string can be accessed using the at function:

    string word = "hangman";
    std::cout << word.at(0); // Output: h (the first character)
    std::cout << word.at(6); // Output: n (the last character)
    

    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.

    string workingCopy = "_______";
    workingCopy.at(3) = 'n';
    std::cout << workingCopy; // Output: __n____
    

    where '_' denotes a single character (of type char).

  2. Similar to whole strings, you can also declare variables for single characters (type char) and read them from the keyboard:

    char guess;
    std::cin >> guess;

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: "
  2. 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"
  2. 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"
  2. 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.
  2. 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.
  2. 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

#include <iostream>
#include <string>
#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;
}

Made by JirR02 in Switzerland 🇨🇭