Chapter 06: The Computer rolls the dice … a first simulation
Ok …
Here is an interactive project with a little Google research to find out about the random function in the C++ library.
1. Create a sub folder in the \2009cpp folder with the name chapt06_randomNumber
2. With your text editor, start a new file called guessIt.cpp and cut and paste a shell of a C++ program into it.
Save it.
Compile it to make sure it is good so far, (or fix it … so that it will compile with no significant error messages.)
3. Now let’s think about the bare bones of what we want the program to do:
I. Announce what the program is about.
II. Ask the user to input a guess in the range 2-12.
III. Get the computer to select randomly a number in that range.
IV. See if the number guessed matches the total on the die that the computer rolled
V. Report the results
VI. Ask if user wants to try again … or quit
Ok … let's get a shell started …
// guessIt
#include <iostream>
using namespace std;
// Note: procedure playgame is the main game ...
void playgame()
{
cout << "THE COMPUTER will roll the dice AFTER\n"
<< "YOU ENTER a number in the RANGE 2..12\n"
<< "Ok ... what is your guess: ";
int userNum;
cin >> userNum;
cin.sync(); // flush cin stream ...
// The following tasks are yet to be coded:
// 1. get two random numbers 1..6 for each die
// 2. see if sum of die matches the number the user guessed
// 3. report success or falure
cout << "You entered " << userNum << endl;
}
int main()
{
int reply;
// since we presume that the user wants to play at least one time ...
do // ... we can use the do..while(..) structure
{
playgame();
cout << "\nPlay again (y/n) ? ";
reply = cin.get(); // NOTE! cin.get() return type is an 'int'
cin.sync(); // flush cin stream ...
}while( !(reply=='n' || reply=='N') ); // i.e. until n or N entered
}
Ok …
It compiles and runs ok so far, but did you notice that the user can enter numbers that are way out of range?
Let’s fix that this way: (We will pass the value to a function to validate it.)
// This function returns true if x <= 12 ...
// otherwise it returns false
bool isValid( int x )
{
if(x <= 12)
return true;
// else ... if reach here ...
return false;
}
Ok …
Let’s plug that in and check it out so far …
// guessIt2
#include <iostream>
using namespace std;
// This function returns false if x < 2 or x > 12 ...
// otherwise it returns true (if x is a valid integer)
bool isValid( int x )
{
if( !cin.good() )
{
cin.clear(); // in case error flags set ...
cin.sync(); // flush cin stream ...
cout << "\nNon integer entered ... ";
return false;
}
if( x < 2 || x > 12 )
{
cout << "\nOut of range 2..12 ... ";
cin.sync(); // flush cin stream ...
return false;
}
// else ... if reach here ...
cin.sync(); // flush cin stream ...
return true;
}
// Note: void function (i.e. procedure) playgame is the main game ...
void playgame()
{
cout << "THE COMPUTER will roll the dice AFTER\n"
<< "YOU ENTER a number in the RANGE 2..12\n";
int userNum;
do
{
cout << "Your guess 2..12 : ";
cin >> userNum;
}while( !isValid( userNum ) );
// The following tasks are yet to be coded:
// 1. get two random numbers 1..6 for each die
// 2. see if sum of die matches the number the user guessed
// 3. report success or falure
cout << "You entered " << userNum << endl;
}
int main()
{
int reply;
// since we presume that the user wants to play at least one time ...
do // ... we can use the do..while(..) structure
{
playgame();
cout << "\nPlay again (y/n) ? ";
reply = cin.get(); // NOTE! cin.get() return type is an 'int'
cin.sync(); // flush cin stream ...
}while( !(reply=='n' || reply=='N') ); // i.e. until n or N entered
}
So far … so good.
But let’s do that research on the C++ random function, (if C++ has one?) … google C++ random and here is what came up near top just now (2009-03-02) ...
http://www.cplusplus.com/reference/clibrary/cstdlib/rand.htmlrand function
int rand ( void ); // include <cstdlib>
Generate random number
Returns a pseudo-random integral number in the range 0 to RAND_MAX.
This number is generated by an algorithm that returns a sequence of apparently non-related numbers each time it is called. This algorithm uses a seed to generate the series, which should be initialized to some distinctive value using srand.
RAND_MAX is a constant defined in <cstdlib>. Its default value may vary between implementations but it is granted to be at least 32767.
A typical way to generate pseudo-random numbers in a determined range using rand is to use the modulo of the returned value by the range span and add the initial value of the range:
( value % 100 ) is in the range 0 to 99
( value % 100 + 1 ) is in the range 1 to 100
( value % 30 + 1985 ) is in the range 1985 to 2014
Notice though that this modulo operation does not generate a truly uniformly distributed random number in the span (since in most cases lower numbers are slightly more likely), but it is generally a good approximation for short spans.
Parameters ... (none)
Return Value ... An integer value between 0 and RAND_MAX.
Example
/* rand example: guess the number */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main ()
{
int iSecret, iGuess;
/* initialize random seed: */
srand ( time(NULL) );
/* generate secret number: */
iSecret = rand() % 10 + 1;
do {
printf ("Guess the number (1 to 10): ");
scanf ("%d",&iGuess);
if (iSecret<iGuess) puts ("The secret number is lower");
else if (iSecret>iGuess) puts ("The secret number is higher");
} while (iSecret!=iGuess);
puts ("Congratulations!");
return 0;
}
Output:
Guess the number (1 to 10): 5
The secret number is higher
Guess the number (1 to 10): 8
The secret number is lower
Guess the number (1 to 10): 7
Congratulations!
In this example, the random seed is initialized to a value representing the second in which the program is executed (time is defined in the header <ctime>). This way to initialize the seed is generally a
good enough option for most (beginner programmer's) random number needs.
See also
srand ... Initialize random number generator (functions)
http://www.cplusplus.com/reference/clibrary/cstdlib/srand.htmlNow let’s see if we can plug it in and get it working in our program … (and notice some changes? / deletions? / improvements? … that were made also.)
// guessIt3
#include <iostream>
#include <cstdlib> // re. rand
#include <ctime> // re. time
using namespace std;
// This function returns false if x < 2 or x > 12 ...
// otherwise it returns true (if x is an integer)
bool isValid( int x )
{
if( !cin.good() )
{
cin.clear(); // in case error flags set ...
cin.sync(); // flush cin stream ...
cout << "\nNon integer entered ... ";
return false;
}
if( x < 2 || x > 12 )
{
cout << "\nOut of range 2..12 ... ";
cin.sync(); // flush cin stream ...
return false;
}
// else ... if reach here ...
cin.sync(); // flush cin stream ...
return true;
}
// Note: void function (i.e. procedure) playgame is the main game ...
void playgame()
{
cout << "THE COMPUTER will roll the dice AFTER\n"
<< "YOU ENTER a number in the RANGE 2..12\n";
int userNum;
do
{
cout << "Your guess 2..12 : ";
cin >> userNum;
}while( !isValid( userNum ) );
// Note: userNum is first passed to isValid
// Then isValid returns true or false.
// get two random numbers 1..6 for each die
int die1 = rand() % 6 + 1;
int die2 = rand() % 6 + 1;
// see if sum matches
if( die1 + die2 == userNum )
cout << "MATCH!\n"; // report success
else
cout << "NO match!\n"; // report failure
cout << "You entered " << userNum << endl
<< "And the computer rolled " << die1 << " & " << die2
<< " or " << die1 + die2 << "." << endl;
}
int main()
{
// initialize random seed
srand ( time(NULL) );
int reply;
// since we presume that the user wants to play at least one time ...
do // ... we can use the do..while(..) structure
{
playgame();
cout << "\nPlay again (y/n) ? ";
reply = cin.get(); // NOTE! cin.get() return type is an 'int'
cin.sync(); // flush cin stream ...
}while( !(reply=='n' || reply=='N') ); // i.e. until n or N entered
}
Well, these functions are working ok … but let’s make some more enhancements:
Let’s add a counter to keep track of the number of games. And suppose that you started out with so many cents, say 100, at the beginning and win the number of cents, (the value you guessed), when there is a match … but lose the number (the same number of cents) you guess for the sum of the dice if there is NO match … (Let’s use global variable’s for these to keep the design simple, ... so we won’t have to pass these values back and forth to/from the procedures or to use pass by reference syntax, since this is supposed to be a simple simulation?)
The game will be over when all your starting cents are used up, … or … if you choose to quit, (or if by some long shot, the computer loses all its cents!)
Make a final report of the success or failure. (What you started out with. How much you were up/down. Your new total and the computers new total. The number of games played.)
Are you ready to start programming?
// guessIt4
#include <iostream>
#include <cstdlib> // re. rand
#include <ctime> // re. time
using namespace std;
// using globals to reduce parameter passing ... for this simple simulation
int cCents = 100; // defaults for Computer and Player
int pCents = 100;
int cWins = 0;
int pWins = 0;
int numGames = 0;
// This function returns false if x < 2 or x > 12 ...
// otherwise it returns true (if x is an integer)
bool isValid( int x, int low, int high)
{
if( !cin.good() )
{
cin.clear(); // in case error flags set ...
cin.sync(); // flush cin stream ...
cout << "\nNon integer entered ... ";
return false;
}
if( x < low || x > high )
{
cout << "\nOut of range 2..12 ... ";
cin.sync(); // flush cin stream ...
return false;
}
// else ... if reach here ...
cin.sync(); // flush cin stream ...
return true;
}
// Note: void function (i.e. procedure) playgame is the main game ...
void playgame()
{
cout << "THE COMPUTER will roll the dice AFTER\n"
<< "YOU ENTER a number in the RANGE 2..12\n";
int userNum;
do
{
cout << "Your guess 2..12 : ";
cin >> userNum;
}while( !isValid( userNum, 2, 12 ) );
// Note: userNum is first passed to isValid
// Then isValid returns true or false.
// get two random numbers 1..6 for each die
int die1 = rand() % 6 + 1;
int die2 = rand() % 6 + 1;
// see if sum matches
if( die1 + die2 == userNum )
{
cout << "MATCH!\n"; // report success
pCents += userNum; // player wins
cCents -= userNum; // computer loses
}
else
{
cout << "NO match!\n"; // report failure
pCents -= userNum; // player loses
cCents += userNum; // computer wins
}
cout << "You entered " << userNum << endl
<< "And the computer rolled " << die1 << " & " << die2
<< " or " << die1 + die2 << "." << endl;
}
int main()
{
// initialize random seed
srand ( time(NULL) );
cout << "Computer stakes 100..1000 (cents) : ";
cin >> cCents;
cin.clear(); // in case non-integer entered ...
cin.sync();
if( cCents < 100 ) cCents = 100; // don't allow values less than 100
if( cCents > 1000 ) cCents = 1000; // don't allow values more than 1000
cout << "Player stakes 100..1000 (cents) : " ;
cin >> pCents;
cin.clear(); // in case non-integer entered ...
cin.sync();
if( pCents < 100 ) pCents = 100;
if( pCents > 1000 ) pCents = 1000;
int pCentsInitial = pCents; // get copies ...
int cCentsInitial = cCents;
cout << "\nThe computer staked " << cCents << " and the player staked "
<< pCents << " cents." << endl;
int reply;
// since we presume that the user wants to play at least one time ...
do // ... we can use the do..while(..) structure
{
++numGames;
playgame();
cout << "\nAfter game "<<numGames<<" player has "<<pCents<<" cents"
<< "\nand the computer has " << cCents << " cents\n";
cout << "\nPlay again (y/n) ? ";
reply = cin.get(); // NOTE! cin.get() return type is an 'int'
cin.sync(); // flush cin stream ...
}while( !(reply=='n' || reply=='N') && pCents > 0 && cCents > 0 );
// don't allow, (i.e. correct for), 'in-hole' stuations ...
if( pCents < 0 ) // player in hole
{
cCents -= -pCents; // i.e. decrease computer balance
pCents = 0; // so now can up balance of player to zero
}
else if( cCents < 0 ) // computer in hole
{
pCents -= cCents; // decrease player balance
cCents = 0; // so now can up balance of computer to zero
}
// can insert the starting 'stakes' here ...
// cCents, and pCents hold the values we need to print out with message
cout << "\nAfter " << numGames << " games, the computer has "
<< cCents << " cents\n"
<< "and the player has " << pCents << " cents" << endl;
// calclate the computer winnings
cWins = cCents - cCentsInitial;
// calculate the player winnings
pWins = pCents - pCentsInitial;
if( cWins > 0 ) // computer wins
{ // take the negative of pWins ...
cout << "The computer wins " << cWins
<< " and the player loses " << -pWins << endl;
}
else // computer loses
{ // take neg cWins ...
cout << "The player wins " << pWins
<< " and the computer loses " << -cWins << endl;
}
cout << "\nPress 'Enter' to continue ... " << flush;
cin.get(); // wait for 'Enter' ...
return 0;
}
Time to reflect ... a little ...
I've have noticed in some 'beginning' text-books ... that the text was recommended to students who had had previous practice with programming in some language ... Thus ... those books are not appropriate texts for someone who has never programmed before ... unless they are 'fleshed out at the beginning' with sufficient concepts and lots of exposure to small programs that introduce C++ key-words and concepts step by step.
With C++ ... the easy and useful things/objects like strings ... can be introduced on day two ... and the old C strings, can be dealt with later ... after you are comfortable with the much easier to use C++ strings.
Classically, for beginners, the ideal would be to build bottom up from the simple "Hello World" program ... learning to print some text on the console screen (after you have compiled and obtained an executable file from the hwxxx.cpp file that you hand to the C++ compiler to process) ... by adding one new C++ keyword/concept ... in one new program ... at a time ... and seeing what it does.
And then ... to make changes and see what that does ... And to make mistakes like leaving off a semi-colon or making a typo, (one at a time, at first) ... And seeing if you can follow the compiler's error messages to fix your program up so that it can be compiled and adjusted to give the EXACT desired output that you may want to change it to produce.
There is a lot to learn ... even how to let the compiler help you find your bugs. This is best learned at first, by starting out with a WORKING program shell. Then adding just one command or small block of commands at a time, so that you KNOW then, WHERE in the code to look, to fix it up, so that it will compile. And then to adjust/fine tune that code further ... to give the ('test') output that is EXACTLY what you want.
This all takes some time, but may be accellerated, if one has access to lots of demo code, that provide a working shell to start.
Again ... the great thing about PC's is that one can learn 'hands on'. And the rate of learning can be tremendous, if lots of well designed online demo code is quickly available to assist in seeing each new step.
The common pitfalls, that cause new students such stress in C/C++, are often poorly addressed.
1. Things like your program crashing when some non-numeric data was entered accidentally when your program was expecting numeric data.
2. Or your program NOT stopping for the next input of data because, unknown to you, there are STILL some characters, like a '\n' char in the cin stream left over from the previous data input, and NO body showed you how to 'flush' this very common and frustrating C/C++ problem away.
(The whole really easy concept of data validation, if introduced early, could so make the beginners programming experience, so much less frustrating ...and maybe even fun.)
3. And then for new programmers, the idea of calling a block of code that has been labeled with a function name ...and returning from that block ... to begin with the next command in the sequence after the place from which that block of code was called ...
4. The idea of passing in values to a function ...
5. And the idea of returning values from a function.
These all take some time and some practice before one may get a little comfortable.
One thing should be clear by now ... It took a lot of designing and work to build these computer systems ... hardware, software, Operating Sytems (OS's), computer languages for machine code, to assembly, to HLL like C++ and HLA and Python .... THEY OBVIOUSLY did NOT in any way happen by themselves ... Thus ... computer systems may help illustrate the absolute necessity of a Great Creator for a Great Creation ...
And so ... "In the beginning God created the heavens and the earth."
It did NOT just all happen by itself.
We all may do well to let that sink in 'real good' in the turbulent days ahead ...
Shalom,
David
P.S. For some insights about 'the turbulent days ahead' ... please continue on at the following link ...
http://sites.google.com/site/andeveryeyeshallseehim/home/he-comesP.P.S. You may also like to see this added (cleaned up) alternative version (that does not use global variables) at:
http://developers-heaven.net/forum/index.php/topic,127.msg476.html#msg476