C++ Hangman

Hey guys, so I'm almost done with my hangman program, its just I don't know how to call the display_hidden_word function which is on line 28.

The original function for the display_hidden_word is on line 123. Since the function has 2 arguments, I don't know the syntax for how I can call the function with two arguments. Any help would be appreciated.

Also, if anyone knows, how do I go about putting a space between words if the hangman answer is actually a phrase. If I type a phrase in, it just puts all the words together instead of spacing. Thanks again.

Here is the codepad: http://codepad.org/wQ12HYx1

The source code is also below:



#include "stdafx.h"
#include <string>
#include <iostream>
#include <ctime>
#include <cctype>
#include <vector>
#include <algorithm>


using namespace std;

char getGuess(string &used); //Prototype Function Declaration

char EntryIsCorrect();	 //Prototype Function Declaration



int main()

{

// set-up

const int MAX_Incorrect = 5;	 // maximum number of incorrect guesses allowed

vector<string> words;	 // collection of possible words to guess

display_hidden_word

words.push_back("CALIFORNIA!");



srand(static_cast<unsigned int>(time(0)));

random_shuffle(words.begin(), words.end());

const string SECRET_WORD = words[0]; // word to guess

int wrong = 0; // number of incorrect guesses

	string soFar(SECRET_WORD.size(), '-'); // word guessed so far

	string used = ""; // letters already guessed

	cout << "Welcome to the game of Hangman!\n";

	// main loop

while ((wrong < MAX_Incorrect) && (soFar != SECRET_WORD))

{

	cout << "\n\nYou have " << (MAX_Incorrect - wrong);

	cout << " incorrect guesses left.\n";

	cout << "\nYou have used the following letters:\n" << used << endl;

	cout << "\nCurrently, the word is:\n" << soFar << endl;


char guess = getGuess(used);

if (SECRET_WORD.find(guess) != string::npos)

{

	cout << "That is correct! " << guess << " is in the word.\n";

// update soFar to include newly guessed letter

for (unsigned int i = 0; i < SECRET_WORD.length(); ++i)

{

if (SECRET_WORD[i] == guess)

{

soFar[i] = guess;

}

}

}

else

{

cout << "Incorrect! " << guess << " is not in the word.\n";

++wrong;

}

}

// shut down

if (wrong == MAX_Incorrect)

	cout << "\nYou have lost!";

else

	cout << "\nYou guessed the word!";

	cout << "\nThe word was " << SECRET_WORD << endl;


system("pause");

return 0;

}




void display_hidden_word(

   const std::string& word,

   const std::vector<char>& already_found

)

{
   for(size_t i(0); i < word.size(); ++i)

      cout << (

         !isalpha(word[i]) ||

         (std::find(already_found.begin(), already_found.end(), word[i]) != already_found.end())

            ? word[i] : '-'

      );

}



//----getGuess Function Return starts here---

char getGuess(string & used)

{

char guess;

	cout << "\n\nEnter your guess: ";

	cin >> guess;

	guess = toupper(guess);

//make uppercase since secret word in uppercase

while (used.find(guess) != string::npos)	//if guess IS in the string, ask for guess again in while loop

{
	cout << "\nYou have already guessed " << guess << endl;

	cout << "Please enter another guess: ";

	cin >> guess;

	guess = toupper(guess);

}

	used += guess;

	return guess;

}

Declare the function prototype for display_hidden_word and call the function with

1
2
3
4
5
6
7

// ... if these were the variables ...
std::string my_word;
std::vector<char> my_already_found;

display_hidden_word(my_word, my_already_found);


As for the phrase instead of a word, use a vector of strings or replace the '-' with another () ? : statement that check for if the char is a space. E.g.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


void display_hidden_word(

   const std::string& word,

   const std::vector<char>& already_found

)

{
   for(size_t i(0); i < word.size(); ++i)

      cout << (

         !isalpha(word[i]) ||

         (std::find(already_found.begin(), already_found.end(), word[i]) != already_found.end())

            ? word[i] : ((word[i] == ' ') ? ' ' : '-')

      );

}


and after you construct the soFar variable, loop over the SECRET_WORD characters and replace the dash/underscore character with a space character in soFar when a space appears in SECRET_WORD
@jmadsen

Oh thank you, however I declared the void prototype for display_hidden_word but it says the display_hidden_word identifier is not found when I try to run it.
@jmadsen

And also, how would I go about looping over SECRET_WORD characters after constructing the soFar variable? Thanks again.
If anybody else can also help me out that would be great too, here is my code so far

http://codepad.org/SZR5JssJ

Thanks.

Last edited on
I'm not seeing the function prototype in the codepad link. You have the function DEFINED later on but it has to be DECLARED before you try to invoke a call to display_hidden_word in main. So at the top of the document, where you have the other prototypes getGuess and EntryIsCorrect you need to have

1
2
3
4
5

// Function prototype for function, defined after main but
// must be declared before main
void display_hidden_word(const std::string&, const std::vector<char>&);


also, as you have it in the code, display_hidden_word(word,already_found) is too early on and won't display anything because word is empty. You need to move that function call down to after you have initially constructed the soFar variable and call it with

1
2
3

display_hidden_word(soFar, already_used);


the loop for SECRET_WORD is pretty easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

// initial construction of soFar
string soFar(SECRET_WORD.size(), '-'); // word guessed so far

// Simple loop that always works
for(unsigned i = 0; i < SECRET_WORD.size(); ++i) {
   if(SECRET_WORD.at(i) == ' ') { soFar[i] = ' '; }
}

// or this is also possible if using C++11 (because of the 3rd argument which is a lambda)
std::for_each(SECRET_WORD.begin(), SECRET_WORD.end(),
                      [&soFar] (const char& SW_character)
                      {
                              if(SW_character == ' ') { soFar[i] = ' ' }
                      });


This is somewhat off topic, but using std::for_each with lambdas defeats the entire point of using std::for_each.

If you're going to use C++11 features, just use a range based for loop rather than that mess:

1
2
3
4
5
6
7
8
9
10
11
12
13
// compare this:
std::for_each(SECRET_WORD.begin(), SECRET_WORD.end(),
                      [&soFar] (const char& SW_character)
                      {
                              if(SW_character == ' ') { soFar[i] = ' ' }
                      });


// to this:
for(auto& SW_character : SECRET_WORD)
{
    if(SW_character == ' ') { soFar[i] = ' '; }
}



EDIT:

to clarify what I mean... for_each was sort of like the pre-C++11 way to do range based for loops. Now that range based for loops are implemented for reals in C++11 there's little need for for_each anymore.
Last edited on
@Disch

Actually I just realized neither of those work because "i" is not defined.

Anyway, I used an inline lambda in that example but the for_each style has the benefit of that you can define different lambdas and make cleaner code, e.g.:

1
2
3
4
5
6
7
8
9
10

// ignoring the "i" issue
// with lambdas "InsertSpace", "SubCorrectGuess", etc. defined

std::foreach(SECRET_WORD.begin(), SECRET_WORD.end(), InsertSpace);

...

std::foreach(soFar.begin(), soFar.end(), SubCorrectGuess);


Everything is in one line, and there are much fewer keywords and syntax characters (auto, :, &, {}, ... etc )

Either way works it's just different programming styles
Anyway, I used an inline lambda in that example but the for_each style has the benefit of that you can define different lambdas and make cleaner code, e.g.:


Any loop has that "benefit."

Everything is in one line, and there are much fewer keywords and syntax characters (auto, :, &, {}, ... etc )

There is more opportunity to do things wrong in for_each than there is in using a ranged-based for loop. For instance, depending on the types involved:

std::for_each(SECRET_WORD.begin(), soFar.end(), subCorrectGuess);

may compile without complaint, although it is clearly wrong and results in undefined behavior.
@jmadsen:

You're right, it is just a stylistic difference.

And yeah if you have lambdas predefined and are just using a function pointer to pass to for_each... that is what for_each is for.

Embedding the lambda within the for_each call, though, is just messy and unnecessary, IMO.

But even with the function pointer approach... range-based for loops produce shorter (and as cire mentioned... harder to screw up) code:

1
2
3
4
// compare
std::for_each(SECRET_WORD.begin(), SECRET_WORD.end(), InsertSpace);
// to
for(auto& x : SECRET_WORD) InsertSpace(x);


and there are much fewer keywords and syntax characters


I find it a little ironic that the person who brought up lambdas is using them as a selling point for simpler syntax.


EDIT:

Anyway... I'm sorry for derailing the thread.
Last edited on
@jmadsen

Thanks again for the help, I used the loop for the SECRET_WORD and that fixed the spacing problem, however I'm still having trouble with my other problem with the display_hidden_word(soFar, already_used); ,

It still doesn't fix my problem of how I want special symbols such as exclamation points, etc. to show when the game starts, without having to guess the special symbol if a special symbol is in the word. Also when you said call it with display_hidden_word(soFar, already_used); I assume you meant display_hidden_word(soFar, already_found); ? since I don't have a variable called already_used.

The function for the special symbols, such as !, to show already when the game starts, starts at the void display_hidden_word which is on line 147 according to the codepad pasted below.

Also, I ran into another problem, when I include lowercase letters in the secret word, such as changing CALIFORNIA! to California!, when you try to guess by typing a or capital A, it says the letter a or A is incorrect and is not in the word when it actually is in the secret word California!

Thanks again for all your help.

Here is my current up to date code:

http://codepad.org/8vKqAcDP
Last edited on
Any help from anyone would be much appreciated, thanks.

Also, I ran into another problem, when I include lowercase letters in the secret word, such as changing CALIFORNIA! to California!, when you try to guess by typing a or capital A, it says the letter a or A is incorrect and is not in the word when it actually is in the secret word California!


This is because of:

1
2
3
4
5
6
7
8
9
10
char getGuess(string & used)
{
    
    char guess;
    
	cout << "\n\nEnter your guess: ";
    
	cin >> guess;
    
	guess = toupper(guess);


You are translating the guess to uppercase so when you look for the guess in the word, it doesn't find the characters in the word that are lowercase. There are two ways to fix it (the second one is the easier fix). You need to change:

1
2
3
4
5
6
7
8
9
10
11
12
        if (SECRET_WORD.find(guess) != string::npos) {

            cout << "That is correct! " << guess << " is in the word.\n";

            // update soFar to include newly guessed letter
            for (unsigned int i = 0; i < SECRET_WORD.length(); ++i) {

                if (SECRET_WORD[i] == guess) {
                    soFar[i] = guess;
                }

            }


to

1
2
3
4
5
6
7
8
9
10
11
12
        if (SECRET_WORD.find(guess) != string::npos || SECRET_WORD.find(tolower(guess)) != std::string::npos) {

            cout << "That is correct! " << guess << " is in the word.\n";

            // update soFar to include newly guessed letter
            for (unsigned int i = 0; i < SECRET_WORD.length(); ++i) {

                if (toupper(SECRET_WORD[i]) == guess) {
                    soFar[i] = guess;
                }

            }


or, change

 
const string SECRET_WORD = words[0]; // word to guess 


to

1
2
3
4
5
const string SECRET_WORD = words[0]; // word to guess
for(unsigned i = 0; i < SECRET_WORD.length(); ++i) { const_cast<char&>(SECRET_WORD[i]) = toupper(SECRET_WORD[i]); }

// or in C++11 in a way that appeases Disch...
for(auto& c : SECRET_WORD) { const_cast<char&>(c) = toupper(c); }
@jmadsen

Oh I see, I forgot to check for lowercase letters as well, thanks a lot.

That just leaves one more problem which is the original problem where my program wont display special characters such as ! symbols etc, at the start of the game, and instead, you would have to guess the special symbol if its in the word.

How would I go about making special symbols, such as !, show up right away when the game starts and how they should not be covered up by a dash (-)?

Thanks again.
Oh yea, sorry. You need to change all the non-alphabetic characters (e.g. punctuation marks, spacing characters) back to what they are in SECRET_WORD. Change

1
2
3
    for(unsigned i = 0; i < SECRET_WORD.size(); ++i) {
	if(SECRET_WORD.at(i) == ' ') { soFar[i] = ' '; }
    }


to

1
2
3
    for(unsigned i = 0; i < SECRET_WORD.size(); ++i) {
        if(!isalpha(SECRET_WORD.at(i))) { soFar[i] = SECRET_WORD.at(i); }
    }
@jmadsen

Oh, I see. I didn't use isalpha in the syntax which is why the symbols showed up. Thank you very much for all of your help, I really appreciate it. =)
Topic archived. No new replies allowed.