1

So I'm currently reading Beginning C++ Through Game programming by Michael Dawson. I'm enjoying it quite a bit. However I have a question on his version of hangman. I'll provide the code but would someone be able to tell me why it is not randomly picking a different word each time? When I compile, the word is "guess" every time. Is that because THE_WORD = words[0] ?? If so, why would he do this if it is suppose to be a random word every time. Sorry if this is an obvious question and I'm not quite seeing it. Thank you!

 #include <iostream>
 #include <string>
 #include <algorithm>
 #include <ctime>
 #include <vector>
 #include <cctype>

 using namespace std;

 int main()
 {
   const int MAX_WRONG = 8;//max number of incorrect guesses allowed

   vector<string> words;
   words.push_back("GUESS");
   words.push_back("HANGMAN");
   words.push_back("DIFFICULT");

   srand(static_cast<unsigned int>(time(0)));
   random_shuffle(words.begin(),words.end());
   const string THE_WORD = words[0];           //word to guess

   int wrong = 0;                              //number of incorrect guesses
   string soFar(THE_WORD.size(), '-');         //word guessed so far
   string used;                           //letters already guessed

   cout<<"Welcome to Hangman! Good luck!\n";

   while ((wrong < MAX_WRONG) && (soFar != THE_WORD))
   {
    cout<<"\n\nYou have "<<(MAX_WRONG - wrong);
    cout<<" incorrect guesses left.\n";
    cout<<"\nYou've used the following letters:\n"<< used <<endl;
    cout<<"\nSo far, the word is:\n"<<soFar<<endl;

    char guess;
    cout<<"\n\nEnter your guess: ";
    cin>>guess;

    guess = toupper(guess); //make user can enter lower or uppercase
    while (used.find(guess) !=string::npos)
    {
        cout<<"\nYou've already guessed "<<guess<<endl;
        cout<<"Enter your guess: ";
        cin>>guess;
        guess = toupper(guess);
    }
    used += guess;

    if (THE_WORD.find(guess) != string::npos)
    {
        cout<<"That's right!"<<guess<<" is in the word.\n";

        //update soFar to include newly guessed letter
        for (unsigned int i = 0; i < THE_WORD.length(); i++)
        {
            if (THE_WORD[i] == guess)
            {
                soFar[i] = guess;
            }
        }
    }
    else
    {
        cout<<"Sorry, "<<guess<<" isn't in the word.\n";
        ++wrong;
    }
   }

   if (wrong == MAX_WRONG) {
       cout<<"\nYou've been hanged!";
   }
   else
   {
       cout<<"\nYou guessed it!";
   }

   cout<<"\nThe word was "<<THE_WORD<<endl;
   return 0;
}
Samuel Edwin Ward
  • 6,526
  • 3
  • 34
  • 62
Bob Ross
  • 61
  • 7
  • I would recommend not using [`std::random_shuffle`](http://en.cppreference.com/w/cpp/algorithm/random_shuffle), use `std::shuffle` instead which uses the better PRNGs out of the `` header. – Mgetz May 25 '14 at 03:59
  • Although time(0) should in theory work, try altering that line to time(NULL) and see if that helps. – SystemFun May 25 '14 at 03:59
  • @Vlad: [NULL and 0 are no different](http://stackoverflow.com/a/177007/445976), if you want to suggest a better alternative then say `nullptr`. – Blastfurnace May 25 '14 at 04:04
  • hmm @Vlad no luck with that note the nullptr – Bob Ross May 25 '14 at 04:07
  • @Mgetz How exactly do I implement that? What would the third argument be? – Bob Ross May 25 '14 at 04:07
  • @BobRoss std::shuffle(words.begin(), words.end(), std::default_random_engine()); – SystemFun May 25 '14 at 04:10
  • With only 3 items in the array I'd say randomizing the position you choose might be a better bet. – Retired Ninja May 25 '14 at 04:11
  • The program seems to be OK. I get different words when I run the program multiple times. – R Sahu May 25 '14 at 04:17
  • I agree with @RSahu, this code compiles on my PC and chooses all three words in multiple test runs. – Blastfurnace May 25 '14 at 04:20
  • @Blastfurnace hmm I am using Xcode.... so perhaps thats why... – Bob Ross May 25 '14 at 04:21
  • don't use "using namespace std" - use std::string, std::random_shuffle... see: [why-is-using-namespace-std-considered-bad-practice](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) – Daniel Heilper May 25 '14 at 04:28
  • Using "Apple LLVM version 5.1" it seems to choose "guess" every time. Although I only ran it like 10 times.... – Samuel Edwin Ward May 25 '14 at 04:29
  • @hellfire769 thanks for the heads up! the author makes it seem like it isn't a big deal but probably just uses it in the interest of saving time when he wrote his codes – Bob Ross May 25 '14 at 04:34
  • Both methods of shuffling the array seem about the same over many iterations, you may just be unlucky on the first one. [demo](http://ideone.com/aKPTr5) – Retired Ninja May 25 '14 at 04:41
  • @hellfire769 so should I avoid doing using std::cout; etc – Bob Ross May 25 '14 at 04:48
  • @BobRoss: Doing `using namespace std;` globally in a __header file__ is universally considered bad, other uses are debatable. See some of the answers in this question: [Why is “using namespace std;” considered bad practice?](http://stackoverflow.com/q/1452721/445976) – Blastfurnace May 25 '14 at 04:53
  • Assuming you don't get bit by the issues mentioned in the linked question it really is just a choice with people on both sides. `std` is short enough that I just prefix everything, where something like `boost::really::deep::stuff` is a good candidate for a [namespace alias](http://stackoverflow.com/questions/1211399/in-c-what-is-a-namespace-alias). – Retired Ninja May 25 '14 at 04:58
  • @Bob Ross, it depends on your tasks. In a product, you will rarely see std::out. But for simple usage there is not problem with std::out. – Daniel Heilper May 25 '14 at 05:06

2 Answers2

0

UPDATED: As was pointed out to me in the comments, the portion of my answer that described the iterators passed into the random_shuffle() method was incorrect. So I've removed it.

It may seem as though it always selects item 0 when you run it a few times because randomly selecting amongst 0, 1 and 2 may turn up 0 five times in a row... and then 1 six times in a row (who knows?), but over many iterations, it will balance out 33/33/33.

JVNick
  • 92
  • 4
  • `std::vector::end()` returns an iterator that is past the last item in the container. The additional item is not necessary. – R Sahu May 25 '14 at 04:18
  • Thank you @JVNick! I added a bunch of other words. I should of thought to add more but it kept me guessing how every time it was the first word. – Bob Ross May 25 '14 at 04:26
0

Short version:

#include <random>
...
default_random_engine rand = default_random_engine(static_cast<unsigned int>(time(0)));
shuffle(words.begin(), words.end(), rand);

Long version:

The source of randomness for the two argument version of std::random_shuffle is implementation defined. LLVM seems to use __rs_default, and there's no way to seed __rs_default, so you get the same results every time. This seems insane to me, so hopefully I'm missing something.

Samuel Edwin Ward
  • 6,526
  • 3
  • 34
  • 62