//------------------------------------------------------------ // // x.459 Assignment #2 sample solution // 28-Feb-1997 // Michael Wolfe // // This program is an example solution for the "guess a word" // assignment for class on Feb. 24. It illustrates (I hope) // some elements of good style, but should be treated as one // of many possible solutions. // // Note: the major things this should illustrate are: // // o Break your program up into small functions which do very // well-defined pieces of work. Pass values between them // and avoid using global variables except for constants. // // o Use understandable function and variable names. // // o No "magic numbers". Turn constant values into #DEFINE // or global const values. Use MAXWORDLENGTH as an example. // // o Comment enough, but not too much. Explain any strange code, // and document towards your intended audience. My audience here // is the x.459 class, so I am writing the comment with that in // mind. // // o Don't declare extra variables unless they are needed // to help readability and clarify logic. // // o I'm using 'flush' in the output because people seem to // keep having trouble with output not being flushed in the VC++ // environment. // //------------------------------------------------------------ // Needed for stream i/o (cin and cout) #include // Needed for random number functions #include // Needed for string functions #include // Needed for time function used by random number seeding #include // // Words to choose from. Note: in a "real" solution // these would probably be read from a dictionary on disk. // We haven't learned file I/O yet, so we'll do it this way. // We would like to avoid this as a global variable in the // future. // char* words[] = { "steatopygic", "limoscene", "obsequious", "pusillanimous", "obfuscate" }; // // Constant defining the number of words. Would have // used #define if this were C, but we'll use 'const int' // here instead. We haven't learned this yet, but we will. // const int NUMWORDS = 5; // // The longest that we will allow user input to be. // const int MAXWORDLENGTH = 30; // // Give the user the game instructions // void userInstructions() { cout << "Hello! We are going to play the 'guess a word'" << endl; cout << "game, which should keep you entertained for hours" << endl; cout << "on end. I will choose a word and tell you how" << endl; cout << "many characters it is. You will then guess the" << endl; cout << "word until you get it right. I will give you clues" << endl; cout << "based on how many characters you got correct." << endl; cout << endl << flush; } // // Choose a word. Generate a random number and pick the right // word in the global word array to use. Here we can return // a char* because we are returning a pointer to a word that // has already been defined globally. // // Note: this is a good example of a place where it is better // not to declare extra variables. The fact that this routine // could be written in one line makes it more efficient and // doesn't sacrifice readability too badly. // char* chooseWord() { return (words[rand() % NUMWORDS]); } // // Tell the user which characters in the guessed word are // correct. // // Input: // guessedWord - The word that the user has guessed // correctWord - The correct answer, generated by the program // // Output: // The user gets the word spelled back out with the // characters guessed correctly filled in. // void userFeedback(char *guessedWord, char *correctWord) { if (strlen(guessedWord) != strlen(correctWord)) { cout << "Your word wasn't " << strlen(correctWord) << " characters long!" << endl << flush; } else { int charNum; // Yes, you can declare variables here cout << "Correct letters: "; for (charNum=0; charNum < strlen(guessedWord); charNum++) { if (guessedWord[charNum] == correctWord[charNum]) { cout << guessedWord[charNum]; } else { cout << '-'; } } cout << endl << flush; } } // // Play a single game of guess the word. Choose the random word, // tell the user how long it is, and loop until the user gets // it right. // void playOneGame() { char* correctWord = chooseWord(); // Get a new random word char guessedWord[MAXWORDL cout << endl << "I have chosen a word " << strlen(correctWord) << " characters long" <> guessedWord; // If word is not correct, give the user hints. if (strcmp(guessedWord, correctWord)) { userFeedback(guessedWord, correctWord); } } while (strcmp(guessedWord, correctWord)); cout << "Congratulations, you've guessed the word!"; cout << endl << endl << flush; } // // Ask the user if they want to play another game. The user must // return a 'y' to play again. Any other response is interpreted // as a "no". // int playAgain() { char answer; cout << "Would you like to play again ('y' for yes)? " << flush; cin >> answer; return (answer == 'y'); } // // Keep playing the guess a word game until the user decides // that enough is enough // // Note: a do-while loop is used here because the game is // always played once. // void playGames() { do { playOneGame(); } while (playAgain()); } // // The main program. Does all initializations (right now that // only includes the random-number generator), gives the user // instructions, and then plays games. // // Note: this function is very mini// an outline of the program and should do very little "real" // work. // void main() { // Seed the random number generator. In the VC++ // world the preferred way to do this seems to be // to send the number returned by the time() function. // time() returns the number of seconds elapsed since // 01/01/1970. It stores the value in a pointer you can // send to it, but if you send it a NULL, which we did here, // then it does not try to store the value. srand() expects // an unsigned int, so we must convert the signed int returned // by time() into an unsigned int. This is a safe conversion, // since the value returned by time() will always be a positive // number in the legal range of an unsigned int. srand(unsigned(time(NULL))); // Tell the user how to play userInstructions(); // Play games until the user wants to quit playGames(); // Thank you, and good night. cout << "Thank you for playing!!" << endl; }