CS 161 Assignment #7

Due Monday, November 9th at 11:59pm
(Not accepted after Tuesday the 10th at CLASS TIME)

Introduction

Bill was pretty pleased with your frequency finder. He's already hard at work analyzing word frequencies in an effort to determine who's been writing all of those letters to his wife. With a few more letters, he's sure he'll have enough samples to find the author. But there's a new twist! The anonymous author has learned of Bill's plan, and has asked you to help conceal his identity. He wants to keep writing letters, but change his vocabulary so as to avoid detection.

You mention the MessageMunger we wrote in class as a possible solution — it could rewrite his messages, substituting one word for another to help disguise his writing patterns. He's pretty paranoid though, and proposes an even sneakier approach: A modified MessageMunger in which the choice of replacement word is random. For example, when replacing the word "happy", it might select randomly from "pleased", "glad", or "delighted". Randomizing the substitutions should help keep Bill from using word frequency information to learn the author's identity!

Overview

The goal will be to write a TextRandomizer class that can do random word replacement as outlined above. It will be trained to look for specific words, and will always replace occurrences of those words with something else, but the choice of replacement word is random. The output below shows sample runs in which the same input message is rewritten repeatedly. The TextRandomizer in this case has been trained to replace the words "good" and "happy" with randomly-chosen alternatives. (I've shown the println on a separate line from the for to keep the output prettier, but the codepad would require both to be on the same line.)

> TextRandomizer tr = new TextRandomizer();
> for(int i=0; i<10; i++) 
      System.out.println(tr.randomize("That's good news -- you must be happy"));
      
That's agreeable news -- you must be delighted 
That's favorable news -- you must be delighted 
That's agreeable news -- you must be glad 
That's favorable news -- you must be pleased 
That's positive news -- you must be thrilled 
That's positive news -- you must be glad 
That's favorable news -- you must be delighted 
That's super news -- you must be pleased 
That's favorable news -- you must be pleased 
That's super news -- you must be thrilled 

Before starting the assignment, make sure you read Chapter 5. Pay particular attention to the Responder class in the tech-support project. The book develops several versions, but one of them is very close to what you'll need for this assignment (the ArrayList-based version in tech-support2).

The Assignment

I'm not giving you a project to get you started this time — you'll need to create a new project via BlueJ. Also, before you submit, you'll need to copy submission.defs to your project folder, otherwise the submit menu item will be grayed out. The steps below will guide you through creating two classes: A modified Responder class that will randomly generate synonyms, and the TextRandomizer class that will use Responder instances for each of the words it wants to replace:

Breaking the project into two classes will simplify the task. An instance of the Responder class is only responsible for handling a single word. It remembers a collection of possible alternatives for a word, and can select one randomly when asked. The TextRandomizer class is similar to the MessageMunger we wrote in class, except it maps words to Responder instances. To replace a word from the input sentence, the TextRandomizer first looks it up in a map, just like MessageMunger does, but now the associated value is an entire Responder object! Once the corresponding Responder has been retrieved from the map, it can be asked to randomly generate a replacement word.

  1. Start by copying the Responder class from the tech-support2 project to the new project you've created. (You can use the "Add Class from File..." menu item under "File".) Open an editor and review the code. It keeps possible responses in an ArrayList of Strings, and selects one randomly when generateResponse() is invoked. One could imagine filling the ArrayList with "glad", "pleased", "thrilled", etc, and using the responder instance to supply a replacement for "happy".
  2. Our TextRandomizer will need to make use of more than one Responder instance — we'll need one full of options for "happy", one full of options for "good", etc. Since different Responder instances need to store different sets of words, we can't rely on the current definition of fillResponses. (We could plug in synonyms for "happy", but then how would we create a Responder for "good"?) Remove the fillResponses method, and replace it with a method called addResponse that takes a single String argument, and adds the String to the Responder's vocabulary. The interactions below show the updated Responder in use:

    > Responder colors = new Responder();
    > colors.addResponse("Red");
    > colors.addResponse("Green");
    > colors.addResponse("Blue");
    > colors.generateResponse()
    "Red"  (String)
    > colors.generateResponse()
    "Blue"  (String)
    
    > Responder pets = new Responder();
    > pets.addResponse("dog");
    > pets.addResponse("cat");
    > pets.generateResponse()
    "cat"  (String)
    > pets.generateResponse()
    "dog"  (String)
    
  3. Before moving on, add a toString() method to the Responder class. It should display all possible words that the responder can generate. Don't forget that ArrayLists have their own built-in toString method, so you don't need to write code to display the individual entries!

    > Responder colors = new Responder();
    > colors.addResponse("Red");
    > colors.addResponse("Green");
    > colors.addResponse("Blue");
    > colors.toString()
    "Options: [Red, Green, Blue]"  (String)
    > System.out.println(colors);
    Options: [Red, Green, Blue]
    
  4. Next, create a new class called TextRandomizer. Review MessageMunger for inspiration, since our task here is quite similar. MessageMunger keeps a HashMap that maps words (Strings) to replacement words. Now, instead of mapping a word to a single word, we want to map words to Responder instances. (Below, we'll make sure that the Responders contain synonyms for the corresponding key word.) Copy the field declaration (wordMap) and constructor from MessageMunger, and modify them so that the HashMap maps Strings to Responders instead of Strings to Strings.
  5. MessageMunger allowed the user to add and remove word mappings at any time. Our TextRandomizers won't allow that sort of flexibility. Instead, when they're constructed, they will build a fixed set of word mappings in much the same way that the original Responder class built a set of fixed strings. Modify the constructor so that it adds mappings for at least two words (if you borrow the examples shown on this page, you need at least two more of your own). Each of these mappings should have at least three possibilities. For each word, you'll need to create a new Responder instance, fill it with synonyms, then add the word and its corresponding Responder to the wordMap.
  6. Now define the randomize method. It takes a single String as its input, replaces specific words with randomly-selected alternatives, and returns the result. Note that this is very similar to what the munge method in MessageMunger does. The key difference is that munge retrieved a word from the HashMap, and here we need to retrieve a Responder instance and ask it to generate a word instead.
  7. > TextRandomizer tr = new TextRandomizer();
    > tr.randomize("That's good news -- you must be really happy")
    "That's positive news -- you must be really thrilled "  (String)
    > tr.randomize("That's good news -- you must be really happy")
    "That's agreeable news -- you must be really pleased "  (String)
    > tr.randomize("That's good news -- you must be really happy")
    "That's positive news -- you must be really glad "  (String)
    

  8. Finally, add a toString() method to the TextRandomizer class so that we can see the details of its mappings. (If it takes you more than one line of code, you're making it too hard.)
  9. > TextRandomizer tr = new TextRandomizer();
    > tr.randomize("That's good news -- you must be really happy")
    "That's positive news -- you must be really glad "  (String)
    > System.out.println(tr);
    Mappings are: {good=Options: [favorable, positive, super, agreeable], 
    happy=Options: [delighted, glad, pleased, tickled, thrilled]}
    

Comments

Each and every method should have a "javadoc-style" comment above it (the ones that use /** ... */). For full credit, you should use the @param and @return tags as appropriate in these method comments. Each instance variable should have a brief comment as well. Don't forget the main comment at the top of the class either — I'm looking for more than just a sentence or two.

Extending the Assignment

Submitting

Before submitting, test each of your methods thoroughly and double check for comments (including @param and @return directives) above each method. When you're convinced it's ready to go, submit the project electronically. Don't forget to put a copy of this file in your project folder before attempting to submit. (Submitting will work from off campus now.)


Brad Richards, 2009