CIT 591 Assignment 8: Eliza
Fall 2009, David Matuszek

Purposes of this assignment:

General idea of the assignment:

The original ELIZA program was published by Joseph Weizenbaum in 1966. It simulated a Rogerian therapist; that is, most of what it said was merely repeating (with appropriate substitutions) what it was told. Here is a bit of sample dialogue (from Rich & Knight, Artificial Intelligence, 1983):

Person: Men are all alike.
ELIZA: In what way?
Person: They're always bugging us about something or other.
ELIZA: Can you think of a specific example?
Person: Well, my boyfriend made me come here.
ELIZA: Your boyfriend made you come here.
Person: He says I'm depressed much of the time.
ELIZA: I am sorry to hear you are depressed.
Person: It's true. I am unhappy.
ELIZA: Do you think that coming here will help you not to be unhappy?
Person: I need some help, that much seems certain.
ELIZA: What would it mean to you if you got some help?
Person: Perhaps I could learn to get along with my mother.
ELIZA: Tell me more about your family.

Many of Eliza's responses, you should notice, are simply restatements of what the user says. This is done by simply rearranging a few words and replacing a few pronouns. Other responses are simply keyed to specific words, such as "mother." When nothing works, the program might randomly choose one of a set of "all-purpose" phrases; for example, "Tell me more about that." Another good trick is to remember some previous topic, and revert to it. These tricks help to give the appearance that the program has some understanding of what the user says.

Your assignment is to write a simple ELIZA-like program that holds a plausible conversation with the user.

Concepts

ELIZA worked by applying pattern-matching rules. It looked for a rule whose left side matched the last sentence entered by the user, then used the right side of that rule to generate a response. Some sample rules might be:

I remember #1 => What made you think of #1?
#1 loves #2 => How does that make you feel about #1?
mother => Tell me more about your family.

The above are intended to show you the kind of rules you might have; it is not intended to say anything about how you should represent those rules in your program. While it is certainly possible to represent a rule by a string of the above form, for a small number of rules, it's probably easier just to write unique code for each kind of rule.

The conversation

Write an ELIZA-like program (in a class named Eliza) that interactively "converses" with the user. Your program should have the following features:

Try to get capitalization and punctuation correct, but don't worry too much about it.

Details

You should have (at least) the following classes, methods, and instance variables.:

class Eliza

private ArrayList<Rule> rules = new ArrayList<Rule>();
This is an instance variable. Use it to hold an arbitrary number of rules.
public static void main(String[] args)
Creates an instance of type Eliza, calls addRules, then calls the holdConversation method.
void addRules()
Creates a number of rules, one for each subclass of Rule, and puts them into the rules variable.
void holdConversation()
Prints out a welcoming message (of some sort) to the user. After that, it repeatedly reads in lines from the user (you can assume each thing the user "says" is on a single line), and computes and prints a response to it. Do the input/output in this method, but call getResponse to do the actual pattern matching. When the user enters a line beginning with the word "goodbye," quit the program.
 
String getResponse(String userInput)
Given an input line from the user, applies the rules to produce and return Eliza's response.

class Rule

String apply(String userInput)
Returns null. This method is never actually used; it's a "placeholder" method. Its whole purpose in life is to be overridden by methods in its subclasses.
other methods
If there are methods that you want to use in more than one subclass of Rule, define them here and they will be inherited by all subclasses.

class somethingOrOtherRule extends Rule

Write a separate class for each kind of rule that you can think of, and give each class an appropriate name. You should be able to come up with at least ten rules, including rules to do pronoun substitution and to provide random responses when nothing else applies. Each class should define the following:

@Override
String apply(String userInput)
Given a user input, do something with it to produce and return a response. Exactly what this method does depends on what kind of Rule class it is in. For example, if you write a class named PronounSubstitutionRule, the apply method in such a class will probably substitute pronouns in the input string to produce the output string.

The userInput will be a single line that was entered by the user; it's probably a sentence or a question, but when you are dealing with user input, you should not make any assumptions that you can avoid.

When an apply method is called, it may not find the pattern it is looking for, so it won't be able to do anything. You probably need to be able to detect this case. One possibility is to return null if the rule doesn't apply. Another possibility is to write another method (defined in Rule, overridden here) to test whether the rule applies. You decide.

Another situation you may need to consider is that some rules are "final," in the sense that once they have been applied, no additional rules should be applied. Other rules may modify the input in some way, but other rules should still be applied. If you need to make this distinction, figure out how.
Possibly other local methods
You can inherit methods from Rule. You can override methods inherited from Rule. You can define additional methods to be used only within this class, or within a JUnit test for this specific class.

Comments

All classes and all methods should be fully commented with javadoc comments. The comment should consist of at least one sentence that describes what the method does, and all relevant tags (such as @param). Eclipse will supply the tags you need, but you need to fill in the appropriate information after each tag.

When Eclipse inserts a comment for an overriding method, it inserts a non-javadoc comment with an @see tag that refers to the method being overridden. Replace this comment with a javadoc comment, but keep the tag and reference to the method being overridden.

JUnit testing

Your goal in this assignment is to write an Eliza-like program that produces a reasonable-sounding "conversation." That means coming up with a set of rules that do a good job. Since I am not specifying exactly what those rules should be, I cannot provide unit tests for them.

You should test (with JUnit) every method that you write, other than main and routines that do only input/output. In particular, it should be easy to tell what each of your apply methods is supposed to do, by looking at the JUnit test for it. Make each test reasonably readable and self-contained.

While it is common practice to have a test class for each application class, that isn't a Java requirement. In this assignment you will have a lot of subclasses of the Rule class, and there's nothing wrong in combining tests for those methods into one or a few test classes.

Due date:

Thursday, November 12, before midnight. Turn in your program electronically, using Blackboard. The usual late penalty of 5 points per day (out of 100 points) will apply.