Would like input

Started by
5 comments, last by rip-off 18 years, 3 months ago
I just finished writing this noob prog. It's the first that I did by my self, it's basicly just a guess the number program with 3 difficulties, and saves/loads scores for viewing. If you have any input on stuff I did wrong I would love to here it becuase thats really the only reason I wrote this, to get better at c++ :D. I know of the bugs like entering a non-int at the menu options but I don't know how to stop that :(

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ctype.h>
#include <limits>
#include <time.h>
using namespace std;

#define MENU_TEXT "What would you like to do: ";

//'EASY=10' means Difficulty setting EASY has number range from 1-10
enum DIFFICULTY{EASY=10, NORMAL=50, HARD=200};

class Score
{
      public:
             //Constructors
             Score(string userInit="Empty", string diff="None", int guesses=0):itsInitials(userInit), itsDiff(diff), itsGuesses(guesses) {};
             ~Score() {};
             //Member functions
             void SetInitials(const string initials) {itsInitials=initials;}
             void SetDiff(const string diff) {itsDiff=diff;}
             void SetGuesses(const int guesses) {itsGuesses=guesses;}
             void SetAll(const string diff, const string initials, const int guesses) {itsDiff=diff; itsInitials=initials; itsGuesses=guesses;}
             int GetGuesses() const {return itsGuesses;}
             string GetInitials() const {return itsInitials;}
             string GetDiff() const {return itsDiff;}
             //Member data
      private:
              string itsInitials;
              string itsDiff;
              int itsGuesses;
};

//function protoypes
void DisplayScores(const vector<Score> & theScores);
void SaveScores(const vector<Score> & scoreList);
void LoadScores(vector<Score> & scoreList);
inline bool ChangeDiff(int menuChoice, DIFFICULTY & gameDiff);
string CheckDiff(const DIFFICULTY & gameDiff);
void SetInitials( string & theInitials);
int GuessNumber(DIFFICULTY gameDiff, int whoGuessing);

//Main program
int main()
{
    vector<Score> scoreList(10); //keep track of 10 Scores
    Score newScore;
    string userInit;
    DIFFICULTY gameDiff=NORMAL; //Game difficulty
    string temp;
    bool Quit=false;
    bool success=false;
    int whoGuessing; //1 if user is guessing, 2 if computer is guessing
    int menuChoice;
    int numGuesses;
        
    cout << "          **********  You are playing Guess the Number!  **********\n";
    cout << "                             Coded by: DarkSuicide\n\n\n";
    
    cout << "Initials(ex: AOH): ";
    getline(cin, userInit);
    SetInitials(userInit);
    
    LoadScores(scoreList); //load scores from previous session
    
    srand(time(NULL));    //seed number gen
    
    while(!Quit)
    {
                cout << "\n(1)New Game\n(2)Display Scores\n(3)Change Difficulty | Current difficulty: " << CheckDiff(gameDiff) << "\n";
                cout << "(4)Change initials | Current initials: " << userInit << "\n(5)Exit\n";
                cout << MENU_TEXT;
                cin >> menuChoice;
                cin.ignore(numeric_limits<int>::max(), '\n');
                
                switch(menuChoice)
                {
                                  //new game
                                  case 1:
                                       cout << "\n\n(1)Me\n(2)Computer\n";
                                       cout << "\nWho will be guessing: ";
                                       cin >> whoGuessing;
                                       numGuesses=GuessNumber(gameDiff, whoGuessing);
                                       if(whoGuessing==1)//User guessing
                                               newScore.SetAll(CheckDiff(gameDiff), userInit, numGuesses);
                                       else //comp guessing
                                               newScore.SetAll(CheckDiff(gameDiff), "Computer", numGuesses);
                                       //update score list
                                       scoreList.insert(scoreList.begin(), newScore);
                                       scoreList.pop_back();
                                       break;
                                  //Display Scores.
                                  case 2:
                                       DisplayScores(scoreList);
                                       break;
                                  //Change Difficulty
                                  case 3:
                                       cout << "\n\nDifficulty:           Number Range:\n";
                                       cout << "\n(1)EASY";
                                       cout.setf(ios_base::internal);
                                       cout.width(30);
                                       cout << "1-10\n";
                                       cout << "(2)NORMAL";
                                       cout.setf(ios_base::internal);
                                       cout.width(28);
                                       cout << "1-50\n";
                                       cout << "(3)HARD";
                                       cout.setf(ios_base::internal);
                                       cout.width(31);
                                       cout << "1-200\n";
                                       do
                                       {
                                           cout << "\nChoose a difficulty: ";
                                           cin >> menuChoice;
                                           success=ChangeDiff(menuChoice, gameDiff);
                                       }while(!success);
                                       cout << "Difficulty changed to : " << CheckDiff(gameDiff) << "\n\n";
                                       break;
                                  //Change userInit
                                  case 4:
                                       cout << "Please enter the new initials: ";
                                       getline(cin, userInit);
                                       SetInitials(userInit);
                                       cout << "\nInitials changed to: " << userInit << "\n";
                                       break;
                                  //Exit program
                                  case 5:
                                       Quit=true;
                                       break;
                                  //not valid option
                                  default:
                                          cout << "\nNot a valid menu option.\n";
                }
    }
    SaveScores(scoreList);
    return 0;
}

//Function definitions

//Displays last 10 scores on a nicely formatted table
void DisplayScores(const vector<Score> & theScores)
{
     vector<Score>::const_iterator theIterator;
     cout << "                       ******* Scores ******\n";
     cout << "\nPlayer:";
     cout.setf(ios_base::internal);
     cout.width(20);
     cout << "Difficulty:";
     cout.setf(ios_base::right);
     cout.width(39);
     cout << "# of Guesses:\n\n";
     
     for(theIterator=theScores.begin(); theIterator!=theScores.end(); theIterator++)
     {
                                       cout.setf(ios_base::left);
                                       cout.width(8);
                                       cout << theIterator->GetInitials();
                                       cout.setf(ios_base::internal);
                                       cout.width(20);
                                       cout << theIterator->GetDiff();
                                       cout.setf(ios_base::right);
                                       cout.width(37);
                                       cout << theIterator->GetGuesses() << "\n";
     }
     cout << "\n\n\n\n";
}

//Saves scores stored in scoreList
void SaveScores(const vector<Score> & scoreList)
{
     vector<Score>::const_iterator theIter;
     
     ofstream scoreFile("scores", ios::binary);
     if(!scoreFile)
                   cout << "Unable to open 'scores' for writing.\n";
     else
     {
             for(theIter=scoreList.begin(); theIter!=scoreList.end(); theIter++)
                                            scoreFile << theIter->GetInitials() << " " << theIter->GetDiff() << " " << theIter->GetGuesses() << "\n";
             scoreFile.close();
     }
}

//Load scores from previous session
void LoadScores(vector<Score> & scoreList)
{
     vector<Score>::iterator theIter;
     int theGuesses;
     string theInitials;
     string theDiff;
     
     ifstream scoreFile("scores", ios::binary);
     if(scoreFile)
     {
         for(theIter=scoreList.begin(); theIter!=scoreList.end(); theIter++)
         {
                                        scoreFile >> theInitials;
                                        scoreFile >> theDiff;
                                        scoreFile >> theGuesses;
                                        theIter->SetAll(theDiff, theInitials, theGuesses);
         }
     }
}

//Chenges difficulty setting
bool ChangeDiff(int menuChoice, DIFFICULTY & gameDiff)
{
     bool success=false;
     switch(menuChoice)
     {
                       case 1:
                            gameDiff=EASY;
                            success=true;
                            break;
                       case 2:
                            gameDiff=NORMAL;
                            success=true;
                            break;
                       case 3:
                            gameDiff=HARD;
                            success=true;
                            break;
                       default:
                               cout << "\nNot a valid menu option.\n";
     }
     return(success);
}

//Checks the numeric value of gameDiff and tells you the difficulty setting as a string
string CheckDiff(const DIFFICULTY & gameDiff)
{
      string diff;
      if(gameDiff==EASY)
           diff="Easy";
      else if(gameDiff==NORMAL)
           diff="Normal";
      else
           diff="Hard";
      return(diff);
}

//Erases all but first 3 chars and converts those 3 to uppercase
void SetInitials( string & theInitials)
{
       if(theInitials.size()>3) //user typed more than 3 chars
                               theInitials.erase(3);
       for(int i=0; i<theInitials.size(); i++)//change to uppercase
               theInitials=toupper(theInitials);
}

//Handles everything dealing with the computer and users guesses
int GuessNumber(DIFFICULTY gameDiff, int whoGuessing)
{
    int theNumber;
    int theGuess;
    int numGuesses=0;
    bool correct=false;
    bool used=false;
    vector<int> usedGuesses;// see if number was already used for a guess
    vector<int>::iterator theIter;
    
    usedGuesses.reserve(200); //can hold up to 200 chars to support all difficulty levels;
    
    if(whoGuessing==1)//user is guessing
    {
                      cout << "\nYou will be the one guessing the number.\n";
                      cout << "Picking random number...";
                      theNumber=(rand() % gameDiff +1);
                      do
                      {
                                        cout << "\nPick a number between 1 and " << gameDiff << ": ";
                                        cin >> theGuess;
                                        
                                        //Check if user already used this number for a guess
                                        for(theIter=usedGuesses.begin(); theIter!=usedGuesses.end(); theIter++)
                                        {
                                                                         if(*theIter==theGuess)
                                                                         {
                                                                                               used=true;
                                                                                               break;
                                                                         }
                                        }
                                        
                                        if(!used)
                                        {
                                           //add current guess to usedGuesses list
                                            usedGuesses.insert(usedGuesses.begin(), theGuess);
                                            if(theGuess==theNumber)
                                            {
                                                                   numGuesses++;
                                                                   cout << "That's it the number was " << theNumber << " and you got it in " << numGuesses << " guess(s).\n";
                                                                   correct=true;
                                            }
                                            else if(theGuess > theNumber && theGuess <= gameDiff)
                                            {
                                                 cout << "\nToo high.\n";
                                                 numGuesses++;
                                            }
                                            else if(theGuess < theNumber && theGuess > 0)
                                            {
                                                 cout << "\nToo low.\n";
                                                 numGuesses++;
                                            }
                                            else
                                                cout << "\nNot a valid guess.\n";
                                        }
                                        else
                                            cout << "Already used " << theGuess << " as a guess :P.\n";
                                        used=false;
                      }while(!correct);
                      return(numGuesses);
    }
    else if(whoGuessing==2)
    {
         cout << "\nThe computer will be guessing the number.\n";
         theNumber=(rand() % gameDiff+1);
         
         do
         {
             //picks a random number in the difficulty range for the computers guess
             theGuess=(rand() % gameDiff+1);
             
             //goes through all the used guesses
             for(theIter=usedGuesses.begin(); theIter!=usedGuesses.end(); theIter++)
             {
         //if the current guess was already used set used=true so it skips everything and picks new guess
                                              if(*theIter==theGuess)
                                              {
                                                                    used=true;
                                                                    break;
                                              }
             }
    //if guess wasn't used insert it into the usedGuesses list and compare it to the random number
             if(!used)
             {
                 usedGuesses.insert(usedGuesses.begin(), theGuess);
                 if(theGuess==theNumber)
                 {
                                    numGuesses++;
                                    correct=true;
                 }
                 else
                     numGuesses++;
             }
             used=false;                                     
         }while(!correct);
         cout << "It took the computer " << numGuesses << " guess(s) to guess the correct number, " << theNumber << "\n";
    }
    return(numGuesses);
}


Advertisement
just at first glance, i think it's better to use const std::string MENU_TEXT instead of #define, just for type safety and all that.
Taking a glance at this I believe I have a lot to comment on (there is, after all, a lot of code), but unfortunately I won't have time to write it up for at least a week... bookmarking the thread now, will come back to it :)
Works well, I compiled it in Visual C++ 2005 with no problems at all and guessed the computers number after the first fail :)
Mordt: I'm just a beginner C++ programmer so take my advice with a grain of salt.Krayven Entertainment
I like the code there, much more complex than my first (in fact all) programs.
And while I can't exactly help you here, I can say that it was fun! Good work!
Quote:Original post by Zahlman
Taking a glance at this I believe I have a lot to comment on (there is, after all, a lot of code), but unfortunately I won't have time to write it up for at least a week... bookmarking the thread now, will come back to it :)


Zahlman is usually much more thourough and perceptive than I am in commenting on code. However, I'll post my opinion on your code here, to give you something to read while you wait for his reply.

Let me start by saying that despite the amount of comments you'll find in my post, your code is much better than most 'comment on my code' posts I've seen in this forum. I tried to give constructive critisism by describing what I think is wrong, why I think that, and what I would have done. Occasionally this will be my personal taste or opinion and should be marked as such. If any of my comments is unconstructive, unclear or simply dumb, feel free to ask for clarification, ignore my statements, call me names, or take any other action you deem appropriate ;)

Comments placed in source, written within /*** ***/ pairs and preceding the line I comment on:

#include <iostream>#include <fstream>#include <string>#include <vector>#include <ctype.h>#include <limits>#include <time.h>/*** * I don't know why people don't want to type std:: every now and then.  * Even if you don't want to, I would prefer only to do 'using' with the * classes and functions you actually use, to prevent polluting namespace  * with classes and functions I dont even use:using std::vector;using std::string;etc ***/using namespace std;/*** * as stated in a previous post, try using const instead of defines for constants * as it is much safer:const static string menu_text = "What would you like to do: "; ***/#define MENU_TEXT "What would you like to do: ";/*** * Useful comments are great. Thumbs up! ***///'EASY=10' means Difficulty setting EASY has number range from 1-10enum DIFFICULTY{EASY=10, NORMAL=50, HARD=200};/*** * Some comment on the purpose of the class might be nice :) * The name is obvious, but when looking at the variables and functions I start * wondering what on earth it's supposed to represent... ***/class Score{      public:             //Constructors/*** * I'm a const correctness fan, and I'm baffled that a self-proclaimed noob is aware of the concept. Thumbs up! * You don't use it consistently yet, you could try:			Score(const string &userInit = "Empty", const string &diff = "None", int guesses = 0) * The references are used to prevent needless copying of string data. * The const means that the contents of the reference won't (can't) be changed * by the function (which is good, as you'll only copy them anyway). ***/		    Score(string userInit="Empty", string diff="None", int guesses=0) :/*** * Don't use inline functions as an excuse to throw away lay-out! * In fact, I would rather prefer all functions of this class to be implemented * separate from the class definition, unless performance issues arise (which is unlikely in this case). ***/				itsInitials(userInit), 				itsDiff(diff), 				itsGuesses(guesses) 			{			}            ~Score() {};            //Member functions/*** * const string without the '&' (reference) is not so useful. Without the * reference, the string will be copied anyway and it doesn't really matter * if the function changes its own copy or not. * Lay-out again, and initials and diff could be a const reference:            void SetInitials(const string &initials) 			{				itsInitials = initials;			}            void SetDiff(const string &diff) 			{				itsDiff = diff;			} ***/            void SetInitials(const string initials) {itsInitials=initials;}            void SetDiff(const string diff) {itsDiff=diff;}/*** * No need to make basic type parameters constant as they will be passed by value anyway:            void SetGuesses(int guesses) 			{				itsGuesses = guesses;			} ***/            void SetGuesses(const int guesses) {itsGuesses=guesses;}/*** * Lay-out again, initials and diff could be a const reference:            void SetAll(const string &diff, const string &initials, int guesses) 			{				itsDiff = diff; 				itsInitials = initials; 				itsGuesses = guesses;			} ***/            void SetAll(const string diff, const string initials, const int guesses) {itsDiff=diff; itsInitials=initials; itsGuesses=guesses;}            			int GetGuesses() const {return itsGuesses;}            string GetInitials() const {return itsInitials;}            string GetDiff() const {return itsDiff;}            //Member data      private:/*** * I don't like these member names. This is, of course, partially subjective but I'll try to give * objective pointers: * - The 'its' prefix is meaningless. If you use it simply to separate between member variables and *   local/global variables, the 'm_' is a more commonly used prefix. I myself prefer no prefixes whatsoever,  *   but I understand why some people like 'm_'.  * - Diff is an abbrieviation most commonly used for 'difference' but from the *   fact that it's a string I deduce that it's supposed to represent 'difficulty' here. But if it is a difficulty *   why not use a variable of type DIFFICULTY, since you have this nice enum? * - Initials probably means initial values? What kind of initial values? Why is it a string? The name should *   tell us what it represents (like e.g. initialDifficultyLevel if that's what it's supposed to be). *   Ok, reading on I see you meant initials as in a persons name :) I argue that PlayerName would be a better *   choice still, as it's not as ambiguous. Besides, storing initials only is a bit too retro for my taste: PCs *   nowadays have plenty of space to store full names :) **/            string itsInitials;            string itsDiff;            int itsGuesses;/*** * This class contains only variables and getters and setters. I find that rather cumbersome in use. * If a class just contains random access data, you can simply make the data public and use it directly.  * If every member has a getter and a setter, your not really doing information hiding anyway. ***/};//function protoypesvoid DisplayScores(const vector<Score> & theScores);void SaveScores(const vector<Score> & scoreList);void LoadScores(vector<Score> & scoreList);inline bool ChangeDiff(int menuChoice, DIFFICULTY & gameDiff);string CheckDiff(const DIFFICULTY & gameDiff);void SetInitials( string & theInitials);int GuessNumber(DIFFICULTY gameDiff, int whoGuessing);//Main programint main(){    vector<Score> scoreList(10); //keep track of 10 Scores    Score newScore;    string userInit;    DIFFICULTY gameDiff=NORMAL; //Game difficulty    string temp;    bool Quit=false;    bool success=false;/*** * Like your difficulty enum, it would be nice to have a player enum:enum player {human = 1, computer = 2}; ***/    int whoGuessing; //1 if user is guessing, 2 if computer is guessing    int menuChoice;    int numGuesses;        /*** * IMO your main function is to big (and therefore harder to read) * I suggest moving the following code.... ***/    cout << "          **********  You are playing Guess the Number!  **********\n";    cout << "                             Coded by: DarkSuicide\n\n\n";        cout << "Initials(ex: AOH): ";    getline(cin, userInit);    SetInitials(userInit);        LoadScores(scoreList); //load scores from previous session        srand(time(NULL));    //seed number gen/*** * ... to a function called InitGame() or something: *	InitGame(); * * And move the following code... ***/    while(!Quit)    {		/***		 * Although not critical, the 5 menu options also could be an enum:		enum main_options {new_game = 1, show_score = 2, change_difficulty = 3, change_name = 4, quit = 5};		 * this avoids 'magic numbers' more on that topic later :)		 ***/		cout << "\n(1)New Game\n(2)Display Scores\n(3)Change Difficulty | Current difficulty: " << CheckDiff(gameDiff) << "\n";		cout << "(4)Change initials | Current initials: " << userInit << "\n(5)Exit\n";		cout << MENU_TEXT;		cin >> menuChoice;		cin.ignore(numeric_limits<int>::max(), '\n');				/***		 * I have a personal rule that cases of an if statement may not contain more than 2 statements		 * normally a function call and a break. I would suggest putting all the different case contents in		 * separate functions. This prevents your 'do-loop in switch-statement in while-loop'.		 ***/		switch(menuChoice)		{			//new game			case 1:		/***		 * Note that if you follow my advice on the player enum the following line should become:		 *		cout << "\n\n(" << human << ") Human\n(" << computer << ") Computer\n";		 ***/				cout << "\n\n(1)Me\n(2)Computer\n";				cout << "\nWho will be guessing: ";				cin >> whoGuessing;				numGuesses=GuessNumber(gameDiff, whoGuessing);		/***		 * Note that if you follow my advice on the player enum the following line would become:		 *			if(whoGuessing == human)		 * which reads a lot better ;)		 ***/				if(whoGuessing==1)//User guessing					newScore.SetAll(CheckDiff(gameDiff), userInit, numGuesses);				else //comp guessing					newScore.SetAll(CheckDiff(gameDiff), "Computer", numGuesses);				//update score list		/***		 * EDIT: removed some nonsense		 ***/				scoreList.insert(scoreList.begin(), newScore);				scoreList.pop_back();				break;				//Display Scores.			case 2:		/***		 * This case conforms to my two line case rule :)		 ***/				DisplayScores(scoreList);				break;				//Change Difficulty			case 3:				cout << "\n\nDifficulty:           Number Range:\n";				cout << "\n(1)EASY";				cout.setf(ios_base::internal);		/***		 * The 30 in the following line is a 'magic number'. I can have a guess that the number		 * has something to do with layout. However, I don't know why it's 30 and what it actually		 * represents. Replacing it with a 'const uint LeftMargin' (or whatever it represents) is		 * more easy to grasp. The same goes for all the other width statements of course...		 ***/				cout.width(30);				cout << "1-10\n";				cout << "(2)NORMAL";				cout.setf(ios_base::internal);				cout.width(28);				cout << "1-50\n";				cout << "(3)HARD";				cout.setf(ios_base::internal);				cout.width(31);				cout << "1-200\n";				do				{					cout << "\nChoose a difficulty: ";					cin >> menuChoice;					success=ChangeDiff(menuChoice, gameDiff);				}while(!success);				cout << "Difficulty changed to : " << CheckDiff(gameDiff) << "\n\n";				break;				//Change userInit			case 4:				cout << "Please enter the new initials: ";				getline(cin, userInit);				SetInitials(userInit);				cout << "\nInitials changed to: " << userInit << "\n";				break;				//Exit program			case 5:		/***		 * This case conforms to my two line case rule :)		 ***/				Quit=true;				break;				//not valid option			default:				cout << "\nNot a valid menu option.\n";		}    }/*** * ... to a function called RunGame() or something: *	scoreList = RunGame(); * * Which would reduce your main to three or four lines... ***/    SaveScores(scoreList);    return 0;}//Function definitions//Displays last 10 scores on a nicely formatted tablevoid DisplayScores(const vector<Score> & theScores){     vector<Score>::const_iterator theIterator;     cout << "                       ******* Scores ******\n";     cout << "\nPlayer:";     cout.setf(ios_base::internal);     cout.width(20);     cout << "Difficulty:";     cout.setf(ios_base::right);     cout.width(39);     cout << "# of Guesses:\n\n";  /*** * EDIT: removed some nonsense ***/     for(theIterator=theScores.begin(); theIterator!=theScores.end(); theIterator++)     {                                       cout.setf(ios_base::left);                                       cout.width(8);                                       cout << theIterator->GetInitials();                                       cout.setf(ios_base::internal);                                       cout.width(20);                                       cout << theIterator->GetDiff();                                       cout.setf(ios_base::right);                                       cout.width(37);                                       cout << theIterator->GetGuesses() << "\n";     }     cout << "\n\n\n\n";}//Saves scores stored in scoreListvoid SaveScores(const vector<Score> & scoreList){	vector<Score>::const_iterator theIter;		ofstream scoreFile("scores", ios::binary);	if(!scoreFile)		cout << "Unable to open 'scores' for writing.\n";	else	{		/***		 * EDIT: removed some nonsense		 ***/		for(theIter=scoreList.begin(); theIter!=scoreList.end(); theIter++)			scoreFile << theIter->GetInitials() << " " << theIter->GetDiff() << " " << theIter->GetGuesses() << "\n";		scoreFile.close();	}}//Load scores from previous sessionvoid LoadScores(vector<Score> & scoreList){     vector<Score>::iterator theIter;     int theGuesses;     string theInitials;     string theDiff;          ifstream scoreFile("scores", ios::binary);     if(scoreFile)     {	/***	 * EDIT: removed some nonsense	 ***/         for(theIter=scoreList.begin(); theIter!=scoreList.end(); theIter++)         {                                        scoreFile >> theInitials;                                        scoreFile >> theDiff;                                        scoreFile >> theGuesses;                                        theIter->SetAll(theDiff, theInitials, theGuesses);         }     }}//Chenges difficulty settingbool ChangeDiff(int menuChoice, DIFFICULTY & gameDiff){	bool success = false;/*** * This switch violates my two lines per case rule. This might be a good time * to tell you about my other rule: the two lines per case rule need not always apply :) * This switch is clear and to the point. ***/	switch(menuChoice)	{	case 1:		gameDiff=EASY;		success=true;		break;	case 2:		gameDiff=NORMAL;		success=true;		break;	case 3:		gameDiff=HARD;		success=true;		break;	default:		cout << "\nNot a valid menu option.\n";	}	return(success);}//Checks the numeric value of gameDiff and tells you the difficulty setting as a stringstring CheckDiff(const DIFFICULTY & gameDiff){/*** * Good, but you should never store the string (like in the Score class). Always store the enum value * and use this function only for printing the name. That way, difficulty is always consistently represented * by the same type: DIFFICULTY. ***/      string diff;      if(gameDiff==EASY)           diff="Easy";      else if(gameDiff==NORMAL)           diff="Normal";      else           diff="Hard";      return(diff);}//Erases all but first 3 chars and converts those 3 to uppercasevoid SetInitials( string & theInitials){/*** * How retro ***/    if(theInitials.size()>3) //user typed more than 3 chars        theInitials.erase(3);    for(int i=0; i<theInitials.size(); i++)//change to uppercase        theInitials=toupper(theInitials);}//Handles everything dealing with the computer and users guesses/*** * The name of this function implies that only one number is set, whereas it in fact is the * Main part of the game. A more descriptive name would be nice. ***/int GuessNumber(DIFFICULTY gameDiff, int whoGuessing){    int theNumber;    int theGuess;    int numGuesses=0;    bool correct=false;    bool used=false;    vector<int> usedGuesses;// see if number was already used for a guess    vector<int>::iterator theIter;    /*** * No need to reserve 200. push_back and insert will expand the vector if necessary ***/    usedGuesses.reserve(200); //can hold up to 200 chars to support all difficulty levels;    /*** * Nesting as deep as you do here within one function is never good. Try to put the inner loops  * in separate functions. That will increase abstraction and readability ***/    if(whoGuessing==1)//user is guessing    {		cout << "\nYou will be the one guessing the number.\n";		cout << "Picking random number...";		/***		 * Using modulo would work in practice, but mathematically it's incorrect as it will slightly affect		 * the distribution of the random number. 		 * It's probably better to use (rand() / RAND_MAX) * gameDiff. Unfortunately, for that to work we need 		 * to use floating point calculations, which leads to the following casting mess:		 static_cast<int>		 (		  (		   static_cast<float>(rand()) / 		   static_cast<float>(RAND_MAX)		  ) * 		  static_cast<float>(gameDiff)		 )		 * But it is more correct :) 		 ***/		theNumber=(rand() % gameDiff +1);	/***	 * The inside of this do loop could be moved to a function for which the name GuessNumber(...) actually 	 * would be appropriate (since we are then really guessing only a single number)	 ***/		do		{			cout << "\nPick a number between 1 and " << gameDiff << ": ";			cin >> theGuess;						//Check if user already used this number for a guess			for(theIter=usedGuesses.begin(); theIter!=usedGuesses.end(); theIter++)			{				if(*theIter==theGuess)				{					used=true;					break;				}			}						if(!used)			{		/***		 * insert() is not very efficient for std::vector. Since the		 * order of guesses would make no difference for the algorithm in this case,                  * try usedGuesses.push_back(theGuess) instead.                 ***/				//add current guess to usedGuesses list				usedGuesses.insert(usedGuesses.begin(), theGuess);				if(theGuess==theNumber)				{					numGuesses++;					cout << "That's it the number was " << theNumber << " and you got it in " << numGuesses << " guess(s).\n";					correct=true;				}				else if(theGuess > theNumber && theGuess <= gameDiff)				{					cout << "\nToo high.\n";					numGuesses++;				}				else if(theGuess < theNumber && theGuess > 0)				{					cout << "\nToo low.\n";					numGuesses++;				}				else					cout << "\nNot a valid guess.\n";			}			else				cout << "Already used " << theGuess << " as a guess :P.\n";			used=false;		}while(!correct);		return(numGuesses);    }    else if(whoGuessing==2)    {		cout << "\nThe computer will be guessing the number.\n";		theNumber=(rand() % gameDiff+1);			/***	 * The inside of this do loop could be moved to a function named something like ComputerGuessNumber(...) 	 ***/		do		{			//picks a random number in the difficulty range for the computers guess			theGuess=(rand() % gameDiff+1);						//goes through all the used guesses			for(theIter=usedGuesses.begin(); theIter!=usedGuesses.end(); theIter++)			{				//if the current guess was already used set used=true so it skips everything and picks new guess				if(*theIter==theGuess)				{					used=true;					break;				}			}			//if guess wasn't used insert it into the usedGuesses list and compare it to the random number			if(!used)			{				usedGuesses.insert(usedGuesses.begin(), theGuess);				if(theGuess==theNumber)				{					numGuesses++;					correct=true;				}				else					numGuesses++;			}			used=false;                                     		}while(!correct);		cout << "It took the computer " << numGuesses << " guess(s) to guess the correct number, " << theNumber << "\n";    }    return(numGuesses);}


Tom

EDIT: To those wondering what rip-off is talking about, I had overlooked that the array size was initialized at ten and concluded some nonsense from that. I removed references to that... Thanx rip-off, don't know how I overlooked that, especially since he wrote this in his comments :)

[Edited by - dimebolt on January 10, 2006 9:22:00 AM]
in reply to dimebolt, his vector of scores is initialised to 10 elements, so adding one at the front and taking one off the back will keep it at a constant ten elements.

the score list always contains 10 elements.

This topic is closed to new replies.

Advertisement