cin and numbers

• 08-16-2006
Hikaru
cin and numbers
I'm writing a guessing game. The program picks a random number and says whether guesses are less than, greater than, or equal to it. All of that works, but I have two problems.

First, I want to be able to type q and quit the program, but when I type q, it goes through all of the turns as if I typed the same number. I don't really understand cin good enough to figure out what's going wrong, and I can't find my problem. I think I'm looking for the wrong things.

Second, the number that the program picks is always the same. I read about rand and srand, and how srand can change the numbers that rand gives, but I can't figure out how to make the number I give srand change without recompiling the program.

Here's my program.
Code:

```#include <cstdlib> #include <iostream> using std::cout; class GuessingGame { private:     int rightNumber;     int turns; public:     enum Result     {         LESS,         EQUAL,         GREATER     };     GuessingGame(int low, int high)     {         rightNumber = (std::rand() % (high - low)) + low;         turns = 0;     }     Result Guess(int guess)     {         ++turns;         if (rightNumber < guess)         {             return LESS;         }         else if (rightNumber > guess)         {             return GREATER;         }         else         {             return EQUAL;         }     }     int Turns()     {         return turns;     } }; int main() {     const int low = 0;     const int high = 100;     const int maxTurns = 10;     GuessingGame game(low, high);     do     {         int guess;         GuessingGame::Result result;         cout << "Pick a number between " << low << " and " << high << "\n";         std::cin >> guess;         result = game.Guess(guess);         if (result != GuessingGame::EQUAL)         {             int turnsLeft = maxTurns - game.Turns();             if (turnsLeft != 0)             {                 if (result == GuessingGame::LESS)                 {                     cout << "The number is less than " << guess << "\n";                 }                 else                 {                     cout << "The number is greater than " << guess << "\n";                 }                 cout << "You have " << turnsLeft << " turns left\n";             }             else             {                 cout << "Game over. You have no more turns\n";                 break;             }         }         else         {             cout << "You won! The number was " << guess << "\n";             break;         }     }     while (true);     return 0; }```
Any other suggestions are welcome too. :) Thanks!
• 08-16-2006
twomers
For the second, use srand( time(0 ); or something at the start or main (include time, or ctime. I can never remember which). For the first, consult the FAQ I think.

EDIT - Why do you use cout, and std::cin?
• 08-16-2006
Daved
You can use if (!(std::cin >> guess)) to detect when a non-number was entered and quit on that.
• 08-16-2006
Hikaru
Quote:

Originally Posted by twomers
For the second, use srand( time(0 ); or something at the start or main (include time, or ctime. I can never remember which).

When I do that the compiler gives me a warning.
Code:

`warning C4244: 'argument' : conversion from 'time_t' to 'unsigned int', possible loss of data`
I can use a type cast and it goes away, but I read that type casts were bad most of the time. Is this one of the times where it's okay?
Quote:

Originally Posted by twomers
For the first, consult the FAQ I think.

I'm sorry, I didn't notice that. I'll search there next time before starting a new thread. :o
Quote:

Originally Posted by twomers
EDIT - Why do you use cout, and std::cin?

I use cout a lot, but cin once. For things I use more than once I do a using command because typing std:: all the time is tedious. :)
Quote:

Originally Posted by Daved
You can use if (!(std::cin >> guess)) to detect when a non-number was entered and quit on that.
Today 07:44 PM

How does that work?
• 08-16-2006
Richie T
>>use srand( time(0 ); or something at the start or main

yes, and make sure that you only call it once - beginners tend to think they need
to call srand every time they call rand - which isn't much good. srand is in
iostream methinks, but you'll need <ctime> to call time (0).

In reply to your first question, what is happening is a common problem for
beginners:

you call cin >> guess;
guess is defined as type int
'q' is a char
cin refuses to read a char into an int variable
the 'q' character is left in the input buffer
cin >> guess is called again, and the problem repeats itself.

Solution? Well it's actually quite straightforward if you're familiar with the
problem - read the input from the user as a string, and parse it accordingly.
There are a number of ways to do this, but the C++ way would be to use a
stringstream - read your value as a string, put it into a stringstream, attempt
to extract the number, if that fails, attempt to extract the letter, if that fails,
or if the letter is not 'q', you can prompt user to enter input again or whatever.

Very basic intro to stringstreams

Check if a stream failed
Clear stream error status
• 08-16-2006
twomers
My 'FAQ' is a link to a FAQ site regarding this error. Click it, and copy the code, or click this - http://faq.cprogramming.com/cgi-bin/...&id=1043284392

You can try - http://faq.cprogramming.com/cgi-bin/...&id=1043284385 for random
• 08-16-2006
Daved
>> I read that type casts were bad most of the time. Is this one of the times where it's okay?
Yes.

>> How does that work?
operator>> returns the stream, which is converted to a boolean value (true or false). It is false if some error occurred and true if everything was ok. So the if will be entered if the cin >> call returns false so you can quit the program.

>> srand is in iostream methinks
<cstdlib>
• 08-16-2006
Richie T
>> srand is in iostream methinks
>><cstdlib>

That's what I thought too, but this compiles in MSVS C++ Express 2005 - but
reference says cstdlib as well - non conformance issue!!?

Code:

```#include <iostream> #include <ctime> int main (void) {         srand (time (0));         std::cout << rand () << std::endl;         return 0; }```
• 08-16-2006
Daved
The compiler is still conforming. The C++ standard allows standard headers to include parts or all of other standard headers. That's not really a good thing for the programmer, because then you accidentally rely on it and then when you switch to another compiler it just stops working.
• 08-17-2006
jafet
Always remember: you can do it (eg. srand() inside <iostream>) doesn't mean you should do it. When a reference text says "function foobar() is inside <baz.h>" this means "foobar() is guaranteed to be inside <baz.h>", it doesn't mean "foobar() can only be found inside <baz.h>".

Suddenly you use a different compiler or a different library set and srand() isn't in <iostream> anymore. Nobody ever said srand() was guaranteed to be in iostream. This can be easily fixed by including <cstdlib>, but imagine if you used 100+ functions which don't belong in <iostream>. You'd go nuts trying to find the correct header files. Better get it right the first time.
• 08-17-2006
Prelude
>>> I read that type casts were bad most of the time. Is this one of the times where it's okay?
>Yes.
No. The warning is there for a reason; time_t is not required to be representable by an unsigned integer. While that cast usually works (I have yet to see it fail), the code is inherently non-portable. The only reason it's acceptable is because the idiom is so common, but that doesn't make it correct. A much better solution is to hash the time_t into a suitable unsigned value:
Code:

```unsigned time_seed() {   time_t now = time ( 0 );   unsigned char *p = (unsigned char *)&now;   unsigned seed = 0;   size_t i;   for ( i = 0; i < sizeof now; i++ )     seed = seed * ( UCHAR_MAX + 2U ) + p[i];   return seed; } srand ( time_seed() );```
• 08-17-2006
CornedBee
Quote:

Originally Posted by Prelude
No. The warning is there for a reason; time_t is not required to be representable by an unsigned integer.

But it is required to be an arithmetic type, i.e. either some integer or some floating point number. Arithmetic conversion to all of these is well-defined for the value 0, so the cast is safe. In addition, the C standard itself specifies that time returns (time_t)-1 on error, so it must work.
• 08-17-2006
Prelude
>Arithmetic conversion to all of these is well-defined for the value 0, so the cast is safe.
It's not safe at all. By "arithmetic type", the standard allows time_t to be a floating-point value which could easily be out of range for the largest unsigned integral type. In such a case the rules for unsigned wrapping don't apply and you've invoked undefined behavior because the value being converted cannot be represented by the type being converted to.

>In addition, the C standard itself specifies that time returns (time_t)-1 on error, so it must work.
I don't quite follow your logic on this one. Just because (time_t)-1 is required to work doesn't mean that all other operations are also required.
• 08-17-2006
CornedBee
Quote:

Originally Posted by Prelude
>Arithmetic conversion to all of these is well-defined for the value 0, so the cast is safe.
It's not safe at all. By "arithmetic type", the standard allows time_t to be a floating-point value which could easily be out of range for the largest unsigned integral type.

Well possible. But if you read the thing I wrote and you quoted again, you'll find that I only mentioned the value 0.

(Admittedly, I just realized that only being able to convert 0 doesn't really help, because I confused the parameter to time() and that to srand(). But at least what I wrote was only off-topic, not wrong. ;) )
• 08-17-2006
Prelude
>But if you read the thing I wrote and you quoted again, you'll find that I only mentioned the value 0.
I know. And I know what you meant too. I thought about pointing it out, but decided against it in favor of attacking the intention of your statement rather than the semantics.

>But at least what I wrote was only off-topic, not wrong.
By being off-topic, it was wrong. But I forgive you. Have a cookie for being a good sport. ;)