Challenge 75

Description

The purpose of this challenge is to write an all-encompassing program which will include various flow control structures, loops, file access, functions and object-oriented concepts.

Requirements

Acquire the file called enable1.txt. This file contains all the words that are considered valid in Words With Friends (in earlier versions)

Word Morph – The Game

This challenge goes through the creation of a text-based word game. The game play is as follows:

    • The computer provides two words of equal length. For example, CAT and DOG.
    • The player will provide a word that similar to each previous word differing only by one letter. So, if the starting word is CAT, COT is a valid play. BIT would not be a valid play after CAT because this changes two letters.
    • Here’s how CAT could be turned into DOG : CAT-COT-DOT-DOG
    • Each word submitted by the player counts as a move. The gameplay consists of a “round” as the player tries to turn CAT into DOG. As the round starts, the player has 20 points. Each submitted word deducts from the current points. The score of the round depends on how many words were submitted to complete the conversion. If the player submitted 12 valid words to perform the conversion (the “Morph”), then the score for the round would be 20 – 12 = 8
    • The game play can also be such that the words are not provided by the computer but by other players (two-player mode)
    • The two words (starting and ending) must be of the same length. Usually, 3, 4, 5 letter words are best. The more letters in the word, the more difficult it is to find transition words.

Milestone 1

      1. Create a class called Words.
      2. In Words,
        1. Create two private int variables length, word_count.
        2. Create a private string pointer called list. This variable will be used to hold a dynamic string array.
        3. Create an overloaded constructor to set the value of length
        4. In the overloaded constructor, call count_matches() and assign its return value to word_count. Create a dynamic string array, of size word_count – this string array will be assigned to the list pointer. Lastly, call the load() function.
        5. Create a private function, int count_matches(). This function will open the file enable1.txt, and count how many words from this file are of length. The return value of this function is the number of words found that match this length.
        6. Create a private function, void load(). This function will open the file enable1.txt. This time, while reading each word, identify which words are of length, and fill up the list string array with each matching word.
        7. Create a public function int reload(int len). This function will:
          1. Check if list is not NULL. If so, delete the list array pointer
          2. Write the same code here as you have in your overloaded constructor.
          3. This function will return word_count.
          4. Since a constructor is only called once when an object is created, it is good for doing initialization things. However, in certains situations such as this, we want the ability to re-initialize things in the class at some point after initialization, but we can’t re-call the constructor. The reload() function allows us to re-initialize the class.
        8. Create a public function string pick_random(). This function will check that word_count contains a number greater than zero. If so, return a string out of the list array. Otherwise, return an empty string. HINT: rand() % 10 will generate a random number between 0 and 9, rand() % 100 will generate a random number between 0 and 99. Think about how you can use this phrase to randomly select an item out of the list array.
        9. Create a public function bool is_valid(string word). This function will return true if word is found in the list array. Otherwise, it returns false. This function will be used to validate that text passed in the parameter is a valid word or not.
      3. Create a global, non-class function int word_distance(string word1, string word2). This function will compare two words, word1 and word2. If the lengths of word1 and word2 are different, return -1. Otherwise, the function will return how many letters are different between the two words for each letter in equivalent positions. For example, cat and cot should return 1 (one letter is different) because C and T are the the same, but A and O are different. cat and dog should return 3 (3 letters are different) because all letters are different. HINT. Remember that variable.length() will return the number of characters in variable where variable is any string variable.
      4. For this milestone, use main to instantiate an instance of Words. The sample main instantiates a Words object with a value of 5. This means only 5-letter words will be selected from the file and added to your Words object.

Sample main() for Milestone 1

#include <iostream>
#include <ctime>
#include <cstdlib>

int main()
{
  srand(time(NULL));  // needs <ctime> and <cstdlib> included
  
  Words words(5);

  cout << words.pick_random() << endl;
  if (words.is_valid("hello"))
    cout << "Yes" << endl;
  else
    cout << "No" << endl;

  if (!words.is_valid("abcde"))
    cout << "No" << endl;

  cout << "cat vs cat : " << word_distance("cat", "cat") << endl;
  cout << "cat vs cot : " << word_distance("cat", "cot") << endl;
  cout << "cat vs dog : " << word_distance("cat", "dog") << endl;
  cout << "tame vs meat : " << word_distance("tame", "meat") << endl;
  cout << "cat vs sheep : " << word_distance("cat", "sheep") << endl;

}

Sample Interaction for Milestone 1

When you run your executable for Milestone 1, you should have output that looks similar to below:

[Run your program]
celeb (your word will be different)
yes
no
cat vs cat : 0
cat vs cot : 1
cat vs dog : 3
tame vs meat : 4
cat vs sheep : -1

Milestone 2

    1. Make sure Milestone 1 is complete. This Milestone will not work without Milestone 1.
    2. Create a class called WordMorph.
    3. In this class, create a protected section with the following data members
      char start_word[10];
      char end_word[10];
      char last_guess[10];
      int actual_moves;
      int game_score;
      int round_score;
      string moves[100];
    4. Write a private function void clear_moves(). This function will set all the elements of the moves string array to a blank string
    5. Write a private function void upper_case(char word[]). This function will convert every character of the word array to its upper case equivalent. Remember the toupper(char) function takes its character parameter and it returns its upper case — this function is already available.
    6. In the public section, write a function void init(string start, string end). In this function, start from a string type into a character-array type as below
      strcpy(start_word, start.c_str());
      1. Do the same thing as above with end_word and end. (The c_str() function that is applied to a string variable converts the string type to a character-array type).
      2. Call the upper_case() function to convert start_word and end_word separately.
      3. Similar to step 1, copy start_word to last_guess. Note that start_word is already a char-array, and you don’t need to call c_str()  on it.
      4. Call clear_moves()
      5. Set round_score and actual_moves to zero
    7. In the public section, create a default constructor which will set game_score, round_score and actual_moves to zero.
    8. Create an overloaded constructor which will pass in two string parameters start and end. In this function, set game_score to zero. Also, call the init() function passing into it the start and end parameter variables.
    9. Copy or move your global word_distance function from the global space into the public section of this class.  You will have to convert it to use character arrays instead of strings.
    10. Write a function int calc_score(). This function calculates the score as 20 – actual_moves. (Here’s how the gameplay and the scoring of the game works using the actual variables available:
      1. The player attempts to convert a word from start_word to end_word.
      2. The player starts off with a score of 20 for the round.
      3. Each word played increases actual_moves. 
      4. Each word played is added to the moves array
      5. Each word played reduces the starting value of 20. The final score of the round is based on how many moves the player used to convert the word. Obviously, taking more moves will reduce the score more. It’s best to solve the conversion in as few moves as possible. 
    11. Write a function bool submit(string played). In this function:
      1. Declare a char array guess[10]
      2. Copy played into guess. Remember that guess is a char-array and played is a string. You’ve done similar type conversions earlier in this challenge.
      3. Convert guess into upper case
      4. Check the word distance between last_guess and guess. If the distance is 1, then the word submitted is valid. Therefore,
        1. Copy guess into last_guess. It helps to remember what the player’s last guess is so we can compare it with their next guess.
        2. Store their last_guess into the moves array – Understand that the moves array will contain all the words played by the user in the current round. Also increment the actual_moves property
        3. Call calc_score() and store its return value in round_score
      5. This function returns true if the user’s word played is valid (1 word distance). Otherwise, it returns false.
    12. Write a function bool round_over(). This function returns true if the round is over. Otherwise, it returns false. The round is over when the player’s last_guess is the same as end_word (The conversion/morph is complete). If the round is over, increment game_score by round_score, and reset round_score to zero. This allows multiple rounds played per game, allowing each round’s score to be accumulatated into a total game score.
    13. Write a getter function for the game_score data member.
    14. —-THIS BULLET POINT IS A SEPARATOR BETWEEN CLASSES —-
    15. Write a class WordMorphConsole. This class will inherit from class WordMorph.
    16. Write a private function void show_moves(). This function displays the first word (start_word). Then it displays all the valid moves the player has used — these moves are stored in the moves array. Then it displays the last word (end_word). Something like below is a good way to do it:
           BET
            1 BAT
            2 CAT
            3 COT
            4 DOT
           DOG
    17. In public, write a default constructor
    18. In public, write an overloaded constructor that passes in two strings start and end. Using an initializer list, call the overloaded constructor from its parent WordMorph passing in the appropriate parameters. Remember that WordMorphConsole is a child of WordMorph.
    19. Write an overload for the << operator. In this operator function:
      1. Display text as below (the italicized text below represents the inherited variables)
        Morph start_word to end_word
        GAME SCORE: game_score
        Moves: actual_moves
      2. Show the players moves by simply calling the show_moves() function
    20. Comment out or remove the main() from Milestone 1. Use the sample main given below. It’s fine to use it exactly as is. You will modify it later for Milestone 3.

Sample main() for Milestone 2

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime> 
#include <iomanip>
#include <cstring>

int main()
{
 srand(time(NULL));
 Words words(4);

 string startword, endword, played;
 bool isvalid;

 startword = words.pick_random();
 endword = words.pick_random();

 WordMorphConsole game(startword, endword);

 // You can choose hard-coded words as below. It can
 // be helpful for debugging if you know what words
 // you are working with
 // WordMorphConsole game("fake", "lift");

 while (!game.round_over())
 {
   system("clear");       // this clears the screen
   cout << game << endl;  // this uses the operator overload

   do
   {
     cout << "What is your next word? ";
     cin >> played;

     isvalid = words.is_valid(played);
     if (!isvalid)
       cout << "--- " << played << " is not a valid word\n";
     else
     {
       isvalid = game.submit(played);

       if (!isvalid)
         cout << "--- You must change only one letter per move\n";
      }
   } while (!isvalid);
 }
 return 0;
}

Milestone 3

  1. Create a menu system that allows the player to select various options, similar to below
    LET'S PLAY WORD MORPH
    
    GAME SCORE: 0
    
     3 - Three-Letters
     4 - Four-Letters
     5 - Five-Letters
     T - Two-Player mode
    Choose: _
    
  2. The menu system is initially displayed as above, allowing the player to pick how many letters to play in the game.
    1. Options 3, 4, 5 will use the Words class to randomly generate the appropriate word lists and randomly selected words for the game.
    2. Two-Player mode will ask the user to provide two words of equal length (the Words class will not be used to generate the random strings for start_word and end_word)
  3. Once the player has selected a game option, the round will run allowing the player to play the game (similar to the gameplay in Milestone 2).
  4. Upon completion of each round, the player is asked if they want to play another round. If the user chooses to, then the menu above is displayed again allowing the user to choose their gameplay. If the user chooses not to play another round, then the game is over, and the program terminates normally. Example below:
    Morph KAB to OIL
    GAME SCORE: 0
    Moves: 5
    	KAB
    	   1 LAB
    	   2 TAB
    	   3 NAB
    	   4 NIB
    	   5 NIL
    	OIL
    
    What is your next word? oil
    ROUND COMPLETE - Play again?
    
    1. As more and more rounds are played, the game score should automatically accumulate and display the total score.
    2. HINT: The init() function of the WordMorph class allows you to reset the various game counters allowing the player to play a new round. This method of re-initializing the game also allows the game score to accumulate.
    3. EXTRA CREDIT/CHALLENGE: At the end of the round, when the user is done playing, ask the user to enter their initials. Then, save the player’s initials and their final game score into a file. Display an arcade-style list of players and their scores (HINT: Read from the text file). MORE HINTS: Create load() and save() functions to the WordMorphConsole class for the reading and saving of the players scores.
              HALL OF FAME
      	------------
      	mo	51
      	me	270
      	boo	12
      	boo	16
      	boo	13
      
      Enter your name for posterity: JJ
      	HALL OF FAME
      	------------
      	JJ	14
      	mo	51
      	me	270
      	boo	12
      	boo	16
      
      

 

 

LEGEND
PROGRAM OUTPUT
USER INPUT
FROM INPUT

CATALOG ID: CPP-CHAL0075

Print Requirements