Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   C++ (http://www.programmingforums.org/forum15.html)
-   -   Guess a Number (http://www.programmingforums.org/showthread.php?t=14655)

3n! Dec 1st, 2007 2:05 PM

Guess a Number
 
Yeah I know, it's a lame old game that beginner programmers make.

The book I'm working out of, "Beginning C++ through Game Programming" has a 'challenge' if you will at the end of Chapter 2. Instead of having the computer choose a random number and you guess, you have to choose the number and the computer guesses. I'm trying to figure out how to set up the random number generator to set its limits based on two variables.

What I'm trying to do is, guessLow = 1, guessHigh = 100. When the computer guesses a number, if its over my chosen number, it will assign the variable guessHigh with the computers guess, and loop back to the rand function, where it randomly chooses between guessLow and guessHigh again, eventually narrowing it down to my chosen number.

The problem I run into is with the rand function itself. How do I set it up to pick a number between two variables that already have numbers assigned to them?

I don't care about anything but getting this function to work.
Here is what I have so far, though obviously incomplete:

:

// Guess the Number
// The player chooses a number and the computer guesses what that number is.

#include "stdafx.h"
#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

int main()
{
        srand(time(0)); // seed the number generator with the time

        int guess;
        int guessLow = 1;
        int guessHigh = 100;
        int pNumber;
       
        cout << "Select a number between 1 and 100: ";
        cin >> pNumber;

        if (pNumber >= 101) // tell the player that if he chooses something above 100, he's wrong
                cout << "Please don't choose a number above 100, or while we're at it anything negative.";
        else
                cout << "Thank you, now the computer is going to guess your number.";


        /* The computer guesses between the lowest guess thus far, and the highest
        then based on whether its too high or too low, the number is assigned to the
        variables 'guessLow' and 'guessHigh'. Then the computer executes the same
        process a the top, resulting in it eventually being narrowed down to the
        player's number of choice.*/

        while (guess != pNumber)
        {
                int guess = rand(guessLow - guessHigh);
                cout << "\n\nComputers guess: " << guess << endl;
               
                if (guess < pNumber)
                {
                        cout << "\nToo low, guess again.";
                        guessLow = guess;
                }
        }
        return 0;
}


I know that "int guess = rand(guessLow - guessHigh);" is way out there, cause thats like Low minus High, what I'm looking for is the operator that says "through"

Jimbo Dec 1st, 2007 2:26 PM

Re: Guess a Number
 
for a simple solution, you could do something like this:
:

int guess = rand() % (guessHigh - guessLow); // this will get you within the value difference
guess += guessLow; // this will get you into your exact range

This will provide a decently random range [guessLow, guessHigh).

3n! Dec 1st, 2007 2:30 PM

Re: Guess a Number
 
So for instance, its 1-100, and my number is 50. The computer chooses 75. This will make it so next time it randomly chooses a number it will choose between 1 and 75?

Then for instance if it chooses 22 next, it will follow up again choosing between 22 and 75?

Thanks for the help.

Jimbo Dec 1st, 2007 3:03 PM

Re: Guess a Number
 
I changed your code a little bit to make sure mine was working like I thought. There were also a couple compiler warnings. Here's the new code (I only changed the loop):

:

  1.     // made this a do-while because you were using guess in the while
  2.     // condition when it was still uninitialized.  Since you have
  3.     // to go through the loop at least once, this change is better
  4.     // anyways.
  5.     do
  6.     {
  7.         cout << "Guessing range is between " << guessLow << " and " << guessHigh << endl;
  8.  
  9.         // I added the +1 so the range would be [guessLow, guessHigh]
  10.         guess = rand() % (guessHigh - guessLow + 1); // this will get you within the value difference
  11.         guess += guessLow; // this will get you into your exact range
  12.  
  13.         cout << "Computer's guess is: " << guess << endl;
  14.  
  15.         if (guess < pNumber)
  16.         {
  17.             cout << "Too low, guess again.\n";
  18.             guessLow = guess;
  19.         }
  20.         // added this part
  21.         else if (guess > pNumber)
  22.         {
  23.             cout << "To high, guess again.\n";
  24.             guessHigh = guess;
  25.         }
  26.  
  27.     }
  28.     while (guess != pNumber);

and here's the output to show how it works:
:

Select a number between 1 and 100: 35
Thank you, now the computer is going to guess your number.Guessing range is between 1 and 100
Computer's guess is: 32
Too low, guess again.
Guessing range is between 32 and 100
Computer's guess is: 93
To high, guess again.
Guessing range is between 32 and 93
Computer's guess is: 63
To high, guess again.
Guessing range is between 32 and 63
Computer's guess is: 49
To high, guess again.
Guessing range is between 32 and 49
Computer's guess is: 37
To high, guess again.
Guessing range is between 32 and 37
Computer's guess is: 33
Too low, guess again.
Guessing range is between 33 and 37
Computer's guess is: 35


3n! Dec 1st, 2007 3:12 PM

Re: Guess a Number
 
Beautiful!

Exactly what I was trying to do. So basically my mistakes were:
A) Using a while loop as apposed to a do/while loop and thats why I was getting the uninitialized 'guess' identifier error. Using a do/while loop places 'guess' at the bottom, avoiding that error.

B)The way I used the rand function. I had the concept down, just not the proper format.

Thank you much for the help. I much appreciate it.

Jimbo Dec 1st, 2007 3:21 PM

Re: Guess a Number
 
Quote:

Originally Posted by 3n! (Post 137898)
A) Using a while loop as apposed to a do/while loop and thats why I was getting the uninitialized 'guess' identifier error. Using a do/while loop places 'guess' at the bottom, avoiding that error.

That's the basic idea. You could also have done something like initializing guess when you declared it, e.g. int guess = 0; so that it would be initialized. I decided to use a do-while for 2 reasons: 1) you need to go through the loop at least once; and 2) the first thing done in the loop is to set the value of guess, so you know that its value won't be read until it's been properly assigned.

While I'm thinking about it... when you check that pNumber is 100 or less, you should also have a check that it's greater than 0. Just putting a message to the user (especially after the checks) is too late and too little. You should start a habit of always checking user input for any error conditions. :)

3n! Dec 1st, 2007 3:22 PM

Re: Guess a Number
 
I edited it a little bit to get rid of repeating numbers:
:

                if (guess < pNumber)
                {
                        cout << "\nToo low, guess again.";
                        guessLow = guess + 1;
                }

                else if (guess > pNumber)
                {
                        cout << "\nToo high, guess again.";
                        guessHigh = guess - 1;
                }


Though its rare, I'm OCD and had to get rid of them.

Thanks again for the help.

lectricpharaoh Dec 2nd, 2007 4:38 AM

Re: Guess a Number
 
Glad to see you got it sorted. However, if you want a uniform (as much as possible, anyways) distribution of your random numbers, don't use modulus. To illustrate why, imagine I have a function that returns a number from one to ten, but I want a number from one to four, so I get the result mod four, plus one. Let's also assume I call it ten times, and get a unique result each time (thus, it returns a perfectly uniform distribution). I then do the modulus:
:

1 % 4 + 1 = 2
 2 % 4 + 1 = 3
 3 % 4 + 1 = 4
 4 % 4 + 1 = 1
 5 % 4 + 1 = 2
 6 % 4 + 1 = 3
 7 % 4 + 1 = 4
 8 % 4 + 1 = 1
 9 % 4 + 1 = 2
10 % 4 + 1 = 3

As you can see, I get some results more frequently than others, and it's not because of a small set. It actually happens when the divisor for your modulus is not a factor of your initial range. There are numerous ways to remedy this (short of writing your own PRNG), but rather than try to explain, let me just refer you to Narue's excellent article on just this subject.

On another note, you should check for input validity. If the user enters "hello" instead of "54" for their guess, your program will die horribly. Use cin.good() or a similar method to ensure the input was successfully parsed and assigned to the variable, and then do range checking. I noticed you checked to see if the value was too high, but not if it was too low. You can combine both checks in the same expression:
:

if((pNumber < 1) || (pNumber > 100))
  // error, prompt for another number

Another solution is to use unsigned types, though in your case, you'd need to then make zero a valid choice (or keep both tests in the conditional). If you know all negative values are out of range, this option makes sense, provided you don't mix signed and unsignedarithmetic (weird things can happen if you're not careful).

Lastly, have you considered making the computer's algorithm 'smart'? You can use a divide-and-conquer approach similar to a binary search to get the number. Basically, you cut the range in half, and choose that number. When you determine the number is higher or lower, you know the new range (by excluding half of the previous range). Repeat until the correct number is found.


All times are GMT -5. The time now is 4:03 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC