Turn-in Checklist:
  1. Bring in to class a stapled turn-in containing your written answers to all the questions. If you worked with a partner, you and your partner should submit a single writeup with both of your names and UVA Email Ids on it. You must clearly indicate both names and UVA IDs in big block letters if you are working in a team. Use our automatic adjudicator service to submit a zip file People.zip containing all of the files in the People package. In the Student.java file, be sure to add the UVA Email Id of each member of your team to the authors array. Each partner must separately submit a copy zip file.
Note: You have twelve days for this problem set. It introduces a new programming language, and involves more code than previous problem sets, and the last questions provides an opportunity for creative work, so do not delay in getting started on it. (This is in direct contrast to all of those other assignments where we encouraged you to slack off. But still.) It is not necessary to understand all of the provided code for this problem set, but you will need to understand parts of it well to answer the questions.

Collaboration Policy - Read Very Carefully

You may work on this assignment alone or with one partner of your choice so long as it is not someone with whom you have partnered on two or more previous problem sets.

Regardless of whether you work alone or with a partner, you are encouraged to discuss this assignment with other students in the class and ask and provide help in useful ways.

Remember to follow the pledge you read and signed at the beginning of the semester. For this assignment, you may consult any outside resources, including books, papers, web sites and people, you wish except for materials from previous cs1120, cs150, and cs200 courses. You may consult an outside person (e.g., another friend who is a CS major but is not in this class) who is not a member of the course staff, but that person cannot type anything in for you and all work must remain your own. That is, you can ask general questions such as "can you explain recursion to me?" or "how do arrays work in Java?", but outside sources should never give you specific answers to problem set questions. If you use resources other than the class materials, lectures and course staff, explain what you used in your turn-in.

You are strongly encouraged to take advantage of the scheduled help hours and office hours for this course.

Purpose

The lab computers should already have Eclipse, the Java interface installed. Eclipse is like PyCharm and comes with Java.
Download: Download ps6.zip to your machine. Then open Eclipse and select File->Import from the menu at the top of Eclipse. Select Existing Projects into Workspace, then press Next.


On the next screen, choose Select archive file, then press Browse... and locate PS6.zip on your machine. Ensure that PS6 (PS6) is checked in the list of projects below. Then click Finish.


You should now have a folder labled PS6 in the Project Explorer tab. This project contains three packages:
Remember that packages are just special folders that contain Java code and help us keep all of our classes organized.

Background

In the 1961, Digital Equipment Corporation (later part of Compaq, which is now part of Hewlett-Packard) donated a PDP-1 mini-computer to MIT, hoping they would use it to solve important scientific problems. Instead, Steve Russell invented the first computer game: Spacewar!. Ten years later, Will Crowther produced the first interactive fiction game, Colossal Cave Adventure (or Adventure for short). Inspired by Adventure, a group of MIT students created Zork and released a version for the Apple II. The game became wildly popular, and was the cause of many students failing out of school.

For this problem set, you will create your own adventure game. Our game takes place in a far-off fictional land known as The University in Charlottansville, East Virginia. Set on bucolic grounds of rolling hills, grassy fields, and red-brick buildings, the characters in our adventure game will take us to imaginary places like the Cabal Hall, Cdrs Hill, the Recursa, and Oldbrushe Hall. All of the places and characters in our game are purely fictional (although most are not purely functional, since they use mutation). Any similarity to real persons or places, of course, is purely coincidental.

Programming an adventure game involves modeling objects in a fictional world. Hence, you will build your adventure game using techniques known as object-oriented programming.

Q: So some of the locations in the game are based on real places?
A: Except for a few. As far as I know, there is no eldricth altar at which students are sacrificed to nameless gods. But then, I was never a professor, so I can't be sure.
Dave Lebling, on The Lurking Horror adventure game

Java Warmup: Introduction to HashMaps

One data structure that is useful in computer science is the HashMap. You can think of a HashMap as a Dictionary (in fact it was called a Dictionary in Python). A HashMap is just a set of Key and Value pairs — just as a real-life dictionary is a set of Word and Definition pairs. In fact, a list is similar to a HashMap in that the "Key" for a value in a list is just the index at which the value appears. HashMaps, however, allow you to choose any type of key, not just integers. For example, let's say we wanted to store the names of people and their associatd ages. This is what it would look like (make sure you import java.util.HashMap in your file):
HashMap<String, Integer> ages = new HashMap<String, Integer>();
ages.put("Charles", 21);
ages.put("Dru", 21);
ages.put("Jonathan", 21);
String Lenny = "Lenny";
int ageLenny = 20;
ages.put(Lenny, ageLenny);
ages.put("Mohsin", 25);
The first line creates our HashMap. <String, Integer> tells Java that we want our HashMap to have Keys of type String and Values of type Integer. The next few lines uses the .puts(Key, Value) method to put some Key/Value pairs into our HashMap. Note that we can add variables to our HashMap as well, as long as they satisfy the type requirements of our HashMap. Now let's see how we would use this.
System.out.println(ages.get("Charles")); //prints out 21
We use the .get(Key) method to get the value for a certain Key. What if we wanted to modify our HashMap?
ages.remove("Charles"); // The Key/Value pair "Charles"/21 is no longer in our HashMap
ages.put("Lenny", 22); // The Key "Lenny" now is associated with the value 22
We use the .remove(Key) method to remove a key/value pair from our HashMap. The remove method takes the Key you want to remove as its parameter. You cannot have duplicate keys in your HashMap The .put(Key, Value) method, instead, modifies the value of the key that's already in the HashMap.
Question 1: Write a method (on paper) that takes in two ArrayLists of strings as its parameters, and returns a HashMap where each element in the first ArrayList is the key for the corresponding element in the second ArrayList. You may assume the ArrayLists have equal lengths. Here's how it should look:
public static HashMap<String, String> makeHashMap(ArrayList<String> keys, ArrayList<String> values)
{
	HashMap<String, String> return_value = new HashMap<String, String>();
	//Your code here...
	return return_value;
}
For example, if keys = {"Charles", "Dru", "Jon", "Lenny", "Mohsin"} and values = {"Hearn", "Knox", "Burket", "Li", "Ahmed"}, then makeHashMap would return a HashMap that mapped a TA's first name to his last name. [Hint]

Objects

For our game, we need to represent three basic kinds of things: people, places and things. Every object we make will be a person, a place or a thing, so we will need classes that implement each kind of object.

All of the objects in our game will be instances of subtypes of the SimObject class, defined in Resources. The SimObject class defines this constructor:

  public SimObject(String name_) {
    name = name_;
    // We use the underscore_ as a convention to distinguish
    // between the passed-in formal parameter (name_) and
    // the object field (name).
  }
(The SimObject class defines some other methods, not shown.)

All objects in our fictional world have names (rumors about a "Nameless Field" are as yet unsubstantiated), so the constructor takes a name parameter.

We also define several subclasses of SimObject (in Resources):

Question 2: By examining the code in Resources and understanding what it means to inherit methods in subclasses, explain (on paper) what happens when:
  1. The is_ownable method is invoked on an OwnableObject.
  2. The is_ownable method is invokved on a MobileObject.
You can check your answers by writing code in Main.java, invoking the methods. For example,
OwnableObject thing = new OwnableObject("book");
System.out.println(thing.is_ownable());

People

Our world also contains people, defined by the Person class in Resources. Person is a subclass of MobileObject.

A Person object has two addition instance variables, which we initialize in the constructor:

public class Person extends MobileObject {
	ArrayList<OwnableObject> possessions; // what am I carrying? 
	double restlessness; // do I stand still? 

	// The constructor for the Person class
	public Person(String name) {
		// Start with the constructor for the superclass
		super(name);
		// Initialize the possessions list so that it
		// starts with no items
		possessions = new ArrayList<OwnableObject>();
		// A person's restlessness is 0 by default
		restlessness = 0.0;
	}
The constructor first calls the superclass' constructor using the keyword super, and then initializes the new instance variables.

The Person class defineds several methods for manipulating people including:

say(String utterance)
Say the words passed in as the parameter (as a string).
look()
Report on the immediate surroundings (only works for installed people).
have_fit()
Throw a temper tantrum.
go(String direction)
Move in the indicated direction. The direction is a string representing the direction, such as "north" (only works for installed people).
Note that many of the provided Person methods only work for objects that have been installed in a place (using the install method provided by its superclass PhysicalObject).

Exercise A: (you do not need to turn anything in for this, but later questions will build on it, so don't skip it) In Main.java, create a Person object named "Alyssa", and ask her to say something. (Try this yourself first, but if you are stuck see the hints.)

Places

The Place subclass is defined in Resources. Its constructor is:

	public Place(String name) {
		// Since Place is a SimObject we must first call
		// the constructor for SimObject
		super(name); 
		
		// Initialize the things and neighbors tables so
		// that they start empty
		this.things = new ArrayList<PhysicalObject>();
		this.neighbors = new HashMap<String,Place>();
	}
A Place object maintains a HashMap of the neighbors of the place. In this case, we use the direction (a string) as the HashMap key. Hence, we can look up the neighboring place in a given direction by looking up the direction in the neighbors HashMap:
	public Place neighbor_towards(String direction) {
		if (neighbors.containsKey(direction)) {
			return neighbors.get(direction);
		}
		else {
			return null;
		}
	}
The .containsKey(Key) method checks if the neighbors HashMap contains the key, Key.

A Place object also maintains a list of the things in a place (this can include any SimObject object, such as a Person). It provides methods for keeping track of the things in a place:

ArrayList<PhysicalObject> get_things()
Returns a list of the things in this place. The list is a new copy of the list of things (so mutating it does not effect the things in the place).

void add_thing(PhysicalObject thing)
Modifes the place by adding the thing to the place.

boolean has_thing(PhysicalObject thing)
Returns true if the place has the thing; otherwise, returns false.

void remove_thing(PhysicalObject thing)
If the place does not contain this thing, produces an error. Otherwise, removes this thing from the place.
Exercise B: (you do not need to turn anything in for this, but later questions will build on this) Examine the code in Resources to see how the SimObject, PhysicalObject, MobileObject, OwnableObject and Place classes are defined. Draw an inheritance diagram showing the relationships between these clasees.

In Main.java, create two Place objects and make them neighbors. Install a Person object in one of your places. Create an OwnableObject and install it in the same place. Ask the person to look, then take the thing, then ask the person to go to the neighboring place. Use get_things to inspect the things in both places you created. (Try this yourself first, but if you are stuck see the hints.)

Visiting Charlottansville

In Library.java we define some places in our imaginary world. The procedure setup_Cville() creates some places and sets up connections between them. Note that because all of the methods in the Library class have the keyword static in front of them, we can call them without creating a new Library object.

We can experiment with our world by creating a new World object.

World cville = Library.setup_Cville();

and then asking objects in our world to do things. This code is in Main.java.

For example:

	World cville = Library.setup_Cville();
	Place cabal = cville.get_place("Cabal Hall");
	System.out.println(cabal.get_exits());
    //[south, north]

	System.out.println(cabal.neighbor_towards("north").name);
    //Bart Statue
Our world needs some people in it, so let's create one, and install him in our world:
	Person JT = new Person("Jeffus Thomerson");
    //<simobject: Jeffus Thomerson>: Installing at <place: Cdrs Hill>

	cville.install_thing(JT, "Cdrs Hill");
    //<simobject: sandwich>: Installing at <place: Cdrs Hill>
We can also make things and add them to our world. For example, let's create a sandwich and install it where JT is now. Then, Mr. Thomerson looks around, sees the sandwich and takes it:

	OwnableObject sandwich = new OwnableObject("sandwich");
	cville.install_thing(sandwich, "Cdrs Hill");
	JT.look();
	//At Cdrs Hill:  Jeffus Thomerson says -- I see sandwich
	//At Cdrs Hill:  Jeffus Thomerson says -- I can go north, south
	
	JT.take(sandwich);
	//At Cdrs Hill:  Jeffus Thomerson says -- I take sandwich
Try playing the adventure game. In Main.java, create people and make them move around. Get a feel for how objects work before moving on.

Inheritance

Generic people are okay, but for an interesting game we need to have people who can do special things.

Our Person object provides a say(String utterance) method:

	Person bill = new Person("Bill");
	bill.say("To apply or to eval, that is the question");
	//Bill says -- To appy or to eval, that is the question.

What if we have lots of different kinds of people and we want to make them speak different ways. For example, a Lecturer is a kind of Person, except that he can lecture as well as say. When lecturing, the lecturer follows every comment with "you should be taking notes".

We can make a Lecturer a subclass of Person that defines a new method (this is defined in People):
	// Lecture is just like "say", but with an extra
	// comment at the end
	public void lecture(String stuff) {
		say(stuff + " - you should be taking notes");
	}
When a method is invoked on a Lecturer object, first it will apply the subclass method if there is one. In this case, say is not definied in the Lecturer class. If the Lecturer subclass does not define the method, evaluation will the continue to look for the method in the superclass. The next superclass is Person, which does define a say method, so it will invoke that.

Question 3: A Professor is even more arrogant than a Lecturer. We will define a Professor class that is a subclass of Lecturer. Define a method profess in your Professor class that is like lecturing, but precedes every statement with "It is intuitively obvious that ". Complete the Professor class found in the People package. (Your Professor class definition should not be more than about 12 lines long.)

Use the commented code in Main.java to test question 3.

	Professor ev = new Professor("Evan Davis");
	v.profess("there exist finitely-describable numbers that are not computable");
	// Evan Davis says -- It is intuitively obvious that there exist finitely describable numbers that are not computable - you should be taking notes
	
	ev.lecture("There exist numbers that have no finite description"); 
	// Evan Davis says -- There exist numbers that have no finite description - you should be taking notes

Question 4: A Dean is like a Professor, but follows every statement with a request for donations. Define a Dean class that is a subclass of Professor. Your Dean class should override the say method (it should define it's own say method) so that after every utterance it makes a request for money. (Your Dean class definition should not be more than about 10 lines long.) [Syntax hint: to access the methods from a class's superclass, use super.method_name(...)]. Complete the Dean class found in the People package.

Use the commented code in Main.java to test question 4.

	Dean dean = new Dean("Deany Duck");
	dean.say("Hello");
	// Deany Duck says -- Hello - We need your help, please send money now.
	
	dean.profess("You should study all the liberal arts.");
	//Deany Duck says -- It is intuitively obvious that You should study all the liberal arts. - you should be taking notes - We need your help, please send money now.

Note that when we invoke the profess method, the Professor class's profess method is used. When that method invokes lecture, the Lecturer class's lecture method is used. When that method invokes say, the Dean class's say method is used. In each case, searching for a method always begins with the object's most specific subclass (that is, the class used to create the object), and searches up the superclasses until the appropriate method is found.

State

More interesting objects in our game will need to use state to keep track of things that might change during an execution. Consider the Person class defined in Resources (and described above). It is pretty long because a Person has many methods. Here we show some of the code, but leave out some methods:
public class Person extends MobileObject {
	ArrayList<OwnableObject> possessions; // A list of the Person's belongings
	
	// How inclined a person is to move in a given time step.
	// If set to 1.0, the person will always move.  If set to 0.0,
	// the person never moves, and if set to 0.3, the person will
	// move 3 times out of 10
	double restlessness;
	
	// The constructor for the Person class
	public Person(String name) {
		// As usual, we start with the constructor for
		// the superclass
		super(name);
		// Initialize the possessions list so that it
		// starts with no items
		possessions = new ArrayList<OwnableObject>();
		// A person's restlessness is 0 by default
		restlessness = 0.0;
	}
	
	// Return a copy of the list of the Person's possessions.
	// Why a copy? We may want to perform calculations on the
	// possession list once we have it. If we didn't return a copy,
	// any changes would affect the original list
	public ArrayList<SimObject> get_possessions() {
		return (ArrayList<SimObject>) possessions.clone();
	}
	
	// Tell the Person to declare what possessions they have
	public void display_possessions() {
		say("I have " + possessions.toString());
	}

    ...

A person has an instance variable possessions that is an ArrayList of objects the person is carrying (we'll get to the restlessness instance variable later). The method get_possessions can be used to see what a person is holding. Note that instead of returning possessions directly, get_possessions returns a new copy of possessions by calling the clone method (don't worry too much about the syntax here). This means if the caller of Person.get_possessions mutates the resulting list, it does not change the actual state of the Person object. This preserves encapsulation, since it means that the only way a Person object's possessions can be modified is by using the Person methods.

Question 5: A Student is a special kind of Person (this doesn't necessarily mean all students are special or kind, just that they are all persons). In this question, we will define a Student class that is a subtype of Person.

Some of the students in Charlottansville have a strange habit of getting undressed and running around the Green, so the Student class needs an instance variable dressed that indicates whether or not the student is clothed. Initially, all students are dressed, so the dressed variable is initialized to true. So, your Student class needs to override the constructor for Person, similarly to how the Person class overrides the constructor of MobileObject.

In addition, your Student class should implement three methods:

Use the commented code in Main.java to test question 5.

	Student aph = new Student("Alyssa P. Hacker");
	aph.install(new Place("The Green"));
	System.out.println(aph.is_dressed());
	//true
	aph.get_undressed();
	//At The Green: Alyssa P. Hacker says --- Brr! It's cold!
	
	aph.get_undressed();
	//At The Green: Alyssa P. Hacker says --- I have no clothes left to remove.
	
	System.out.println(aph.is_dressed());
	//false
	
	aph.get_dressed();
	//At The Green: Alyssa P. Hacker says --- I feel much better now.
	
	System.out.println(aph.is_dressed()); 
	//true
	

The University administration does not condone streaking, and has decided to strategically place police officers on The Green to apprehend streakers.

We have provided (in the People package) the beginnings of a PoliceOfficer class. A PoliceOfficer is a special type of Person, with the ability to arrest other people.

The provided constructor takes an additional parameter jail, which is a Place that arrestees are sent to, and stores the value passed in as jail in an instance variable:

public class PoliceOfficer extends Person {
	public Place jail;
	public PoliceOfficer(String name, Place jail_) {
		super(name);		
		set_restlessness(0.5);
		jail = jail_;
	}
	
	...
For example, here is how a PoliceOfficer object is constructed in play_game_as_APH (defined in Library.java):
	PoliceOfficer krumpke = new PoliceOfficer("Officer Krumpke", cville.get_place("Jail"));
This works since cville is initalized as setup_Cville() which produces a world that includes a Jail place.
Question 6: Define a method in your PoliceOfficer class that makes an arrest. Your method should be called arrest and take a Person as its parameter.

If the criminal is not in the same location as the PoliceOfficer, the arrest method should not perform an arrest, but instead the officer should say something about not being able to arrest someone who is not at the same location.

If the criminal is at the same location, the officer should say that the criminal is under arrest, and read them their Miranda rights, and move the criminal to the jail (use the Person method move that takes a location as its input).

Use the commented code in Main.java to test question 6. PoliceOfficer class:
	World cville = Library.setup_Cville();
	PoliceOfficer krumpke = new PoliceOfficer("Officer Krumpke", cville.get_place("Jail"));
	Student aph = new Student("Alyssa P. Hacker");
	cville.install_thing(aph, "The Green");
	//<simobject: Alyssa P. Hacker>: Installing at <place: The Green>
	
	cville.install_thing(krumpke, "The Green");
	//<simobject: Officer Krumpke>: Installing at <place: The Green>
	
	krumpke.arrest(aph);
	//At The Green: Officer Krumpke says --- Alyssa P. Hacker, you are under arrest!
	//At The Green: Officer Krumpke says --- You have the right to remain silent, call methods and mutate instance variables.
	//Alyssa P. Hacker moves from The Green to Jail
	//At Jail: Alyssa P. Hacker says --- It's lonely here...

	krumpke.arrest(aph);
	//At The Green: Officer Krumpke says --- Alyssa P. Hacker is not here

Automating Objects

This kind of world doesn't make for a very challenging or interesting game. All the people and things only do what the player tells them to. For a more interesting game, we need to have people and things that act autonomously.

We do this by creating a list of all the characters to be moved by the computer and by simulating the passage of time in our World class. The World includes a things instance variable that maintains a list of all the things in our world. The tick method invokes the tick method on each object that provides a tick method:

public class World {
	public String name; 
	public HashMap<String,Place> places;			// "places" maps place names to their corresponding Place objects
	public HashMap<String,PhysicalObject> things;	// "things" maps thing names to their corresponding Thing objects
	
	// Constructor for World class (takes only the name of the world)
	public World(String name_) {
		name = name_;
		// Initialize the places and things maps so that they are initially empty 
		places = new HashMap<String,Place>();
		things = new HashMap<String,PhysicalObject>();
	}

    ...
	
	// Put an object at a certain location
	public void install_thing(PhysicalObject thing, String pname) {
		assert has_place(pname); // Check that the desired location exits in this World
		// Don't want to have too objects with the same name
		if (has_thing(thing.name)) {
			throw new Error("Attempt to add thing with duplicate name.");
		}
		// Add the object to the "things" table
		things.put(thing.name, thing);
		// Call the object's "install" method
		thing.install(get_place(pname));		
	}
	
    ...
	
	// Run a time step in our simulation. This amounts to simply
	// running the tick() function of every object in our World.
	public void tick() {
		// Go through every thing in the "things" list and call
		// tick() on each one
		for (int i=0; i<things.size(); i++) {
            // Get the i-th element from the set of values in the HashMap
			PhysicalObject obj = Library.HashMapGet(things,i);
			obj.tick();
		}
	}

People hang about idly until they get bored enough to do something. To account for this, we give people a restlessness instance variable that indicates how likely they are to get bored enough to move randomly. A person will move in a random direction with restlessness probability with each clock tick. For example, if restlessness is 1.0, the person will move randomly every clock tick. If restlessness is 0.5, the person will move half the time (but not necessarily every other tick, since the decision whether to move or not is random). If restlessness is 0.0, the person will never move randomly. The Person class defines a method set_restlessness(double value) that sets the restlessness instance variable to the input value.

Before tackling the next question, we need to learn a little bit more about the type rules in Java. Suppose we have the following line of code:

PhysicalObject obj = new MobileObject("widget");
Since obj is a MobileObject, we might want to call the change_location method defined in the MobileObject class. Because we defined obj as a PhysicalObject, however, we can only call methods that work on all PhysicalObjects:
PhysicalObject obj = new MobileObject("widget");
obj.install(new Place("The Green"));
obj.change_location(new Place("The Recursa"));
/* Exception in thread "main" java.lang.Error ...
    The method change_location(Place) is undefined for the type PhysicalObject */
To work around this issue, we use casting to tell Java that obj is indeed a MobileObject. In order to cast obj to a MobileObject, we simply put (MobileObject) before we refer to obj:
((MobileObject) obj).change_location(new Place("The Recursa"));
// <simobject: widget>: Installing at <place: The Green>
We can only cast an object to a specific type if that object is indeed an instance of the corresponding class. We can check whether an object is an instance of a specific class using instanceof:
if (obj instanceof MobileObject) {
    ((MobileObject) obj).change_location(new Place("The Recursa"));
}
You may need to use both casting and instanceof in your answer to the next question.
Question 7: Define a tick method for your PoliceOfficer class. The tick method should automatically arrest everyone streaking in the same place as the police officer. (That is, any Student objects that are not dressed should be arrested.) If no one is streaking in the officer's location, the police officer should act like a normal person — that is, it should then invoke the superclass tick() method.

Here are some sample interactions, but note that your results will be different since the Person.tick method makes the person move randomly (for more controlled testing, you might want to invoke krumpke.set_restlessness(0)).

	PoliceOfficer krumpke = new PoliceOfficer("Officer Krumpke", cville.get_place("Jail"));  
	Student aph = new Student("Alyssa P. Hacker");
	cville.install_thing(aph, "The Green");
	//<simobject: Alyssa P. Hacker>: Installing at <place: The Green>
	
	cville.install_thing(krumpke, "The Green");
	//<simobject: Officer Krumpke>: Installing at <place: The Green>
	
	cville.tick();
	//At The Green: Officer Krumpke says --- No one to arrest. Must find donuts.
	//Officer Krumpke moves from The Green to The Recursa
	//At The Recursa: Officer Krumpke says -- It's lonely here...
	
	aph.get_undressed();
	//At The Green: Alyssa P. Hacker says -- Brr! It's cold!
	
	cville.tick();
	//At The Recursa: Officer Krumpke says -- No one to arrest.  Must find donuts.
	//Officer Krumpke moves from The Recursa to The Green
	//At The Green: Officer Krumpke says -- Hi Alyssa P. Hacker
	
	cville.tick();
	//At The Green: Officer Krumpke says -- Alyssa P. Hacker, you are under arrest!
	//At The Green: Officer Krumpke says -- You have the right to remain silent, call methods, and mutate instance variables.
	//Alyssa P. Hacker moves from The Green to Jail
	//At Jail: Alyssa P. Hacker says -- It's lonely here...
	
	cville.tick(); 

Note that Alyssa is not arrested on the tick() where Officer Krumpke moves to The Green and greets Alyssa. The Officer only makes an arrest if there is a streaking student at its location at the beginning of the tick. (Our PoliceOfficer is friendly, but a bit slow to observe her surroundings.)

The World class defines a method play_interactively(Person character) that provides a better interface to playing the game. The play_game_as_APH() procedure (defined in Library.java) sets up Cville, installs two students and one restless police officer in our world, and starts playing interactively as Alyssa P. Hacker.

Here's what a typical game might look like:

	Library.play_game_as_APH();
Output:
<simobject: Alyssa P. Hacker>: Installing at <place: The Green>
<simobject: Ben Bitdiddle>: Installing at <place: Cdrs Hill>
<simobject: Officer Krumpke>: Installing at <place: Bart Statue>
what next? look
At The Green: Alyssa P. Hacker says -- I see nothing
At The Green: Alyssa P. Hacker says -- I can go west, north, south
At Bart Statue: Officer Krumpke says -- No one to arrest.  Must find donuts.
Officer Krumpke moves from Bart Statue to Cabal Hall
At Cabal Hall: Officer Krumpke says -- It's lonely here...
what next? go north
Alyssa P. Hacker moves from The Green to The Recursa
At The Recursa: Alyssa P. Hacker says -- It's lonely here...
At Cabal Hall: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? get undressed
At The Recursa: Alyssa P. Hacker says -- Brr! It's cold!
At Cabal Hall: Officer Krumpke says -- No one to arrest.  Must find donuts.
Officer Krumpke moves from Cabal Hall to South Green
At South Green: Officer Krumpke says -- It's lonely here...
what next? go south
Alyssa P. Hacker moves from The Recursa to The Green
At The Green: Alyssa P. Hacker says -- It's lonely here...
At South Green: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? go south
Alyssa P. Hacker moves from The Green to Bart Statue
At Bart Statue: Alyssa P. Hacker says -- It's lonely here...
At South Green: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? look
At Bart Statue: Alyssa P. Hacker says -- I see nothing
At Bart Statue: Alyssa P. Hacker says -- I can go north, south
At South Green: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? look
At Bart Statue: Alyssa P. Hacker says -- I see nothing
At Bart Statue: Alyssa P. Hacker says -- I can go north, south
At South Green: Officer Krumpke says -- No one to arrest.  Must find donuts.
Officer Krumpke moves from South Green to Cabal Hall
At Cabal Hall: Officer Krumpke says -- It's lonely here...
what next? look
At Bart Statue: Alyssa P. Hacker says -- I see nothing
At Bart Statue: Alyssa P. Hacker says -- I can go north, south
At Cabal Hall: Officer Krumpke says -- No one to arrest.  Must find donuts.
Officer Krumpke moves from Cabal Hall to Bart Statue
At Bart Statue: Officer Krumpke says -- Hi Alyssa P. Hacker
what next? look
At Bart Statue: Alyssa P. Hacker says -- I see Officer Krumpke
At Bart Statue: Alyssa P. Hacker says -- I can go north, south
At Bart Statue: Officer Krumpke says -- Alyssa P. Hacker, you are under arrest!
At Bart Statue: Officer Krumpke says -- You have the right to remain silent, call methods, and mutate instance variables.
Alyssa P. Hacker moves from Bart Statue to Jail
At Jail: Alyssa P. Hacker says -- It's lonely here...
what next? go north
You cannot go north from <place: Jail>
At Bart Statue: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? go south
You cannot go south from <place: Jail>
Ben Bitdiddle moves from Cdrs Hill to University Ave
At University Ave: Ben Bitdiddle says -- It's lonely here...
At Bart Statue: Officer Krumpke says -- No one to arrest.  Must find donuts.
what next? look
At Jail: Alyssa P. Hacker says -- I see nothing
At Jail: Alyssa P. Hacker says -- I can go nothing
Ben Bitdiddle moves from University Ave to Cdrs Hill
At Cdrs Hill: Ben Bitdiddle says -- It's lonely here...
At Bart Statue: Officer Krumpke says -- No one to arrest.  Must find donuts.
Officer Krumpke moves from Bart Statue to Cabal Hall
At Cabal Hall: Officer Krumpke says -- It's lonely here...
what next? quit
Better luck next time.  Play again soon!

Extensions

With a full command of Java programming, object-oriented programming, and inheritance, you are now ready to start making an addictive game. Keep in mind that, historically, computer games have been a colossal waste of time for humankind. As simple as this game is, it's easy to get carried away. Spend your time on the problems, not the game.

Question 8: Design a non-trivial extension to this simulated world. Use your imagination!

You can do what you want (so long as it is in good taste, of course). (As you may have noticed, the course staff has a fairly liberal notion of "good taste", but if you aren't sure, it's best to ask.)

A good extension will use inheritance to create new types of objects in your game. For example, you may want to create a new types of people, places and things that have new behaviors. A strong answer to this question should demonstrate at least five of the following challenges:

  1. A subclass overriding its parent's response to a message (i.e., not invoking the parent behavior and doing something else instead).
  2. A subclass extending its parent's response to a message (i.e., invoking the parent behavior but also doing something else).
  3. A subclass that does something interesting on tick().
  4. Behavior that depends on the state of the world (e.g., different responses based on the current location, the number of exits, the name of the current location, etc.).
  5. An object that creates other objects (careful!).
  6. A non-trivial use of a possession (taking and losing things).
  7. Pursuit or evasion (e.g., adding something like an expert police officer that, rather than moving randomly, moves to an adjacent location if it contains a streaker; adding a squirrel that moves away from persons — or not!).
To add new behaviors for interactive games, you will also need to modify the play_interactively method in the World class to call the new methods.

Your answer to this question should include:

  1. A short explanation of your extensions that describes the objects you added.
  2. An inheritance diagram showing your new classes and the classes they inherit from.
  3. Code for all new procedures you write, or old procedures that you modify. You should only modify provided code if there is no way to provide the behavior you want by defining a new subclass instead. If you modify an existing method, you should print out the entire method but highlight the changed code (e.g., with a physical highlighter, or underlining, or somesuch).
  4. A transcript that shows your game in action.
  5. Explicit indication of which of the challenges above you are addressing. For example, put a big 1 in a circle (or in a comment, or whatnot) in the writeup where you are addressing challenge 1.

Now invite a friend over and teach them how to play. But, remember that this is a fictional world. Don't try anything from the game at home (or on the green). If you do get a chance to visit Charlottansville, make sure to see Monty's Viola, the favorite instrument of the founder of the University and author of the influential 1976 treatise, The Ultimate Declarative, which led many Schemers to revolt.

Submitting Your Assignment

Since all of the Java classes you need to modify are in separate files, we'll need to combine them into a zip file for submission. Fortunately, Eclipse makes this process fairly straightforward. First, go to File->Export. You should see a prompt like the one below.


Now select Archive File and press Next.


You only need to upload the files in the People package, so select only the checkbox for the People folder on the left. This should check off all of the .java files in the People package on the right.


Now click Browse and choose some location to save your zip file. You should enter People.zip in the File name field. Now click Finish to create your zip file.
Credits: This problem set was developed by Portman Wills and David Evans for CS200 Spring 2002 and slightly revised for CS200 Spring 2003 and CS150 Fall 2005 by David Evans, and then revised again for Spring 2009 by Westley Weimer. It was originally done using Scheme, but translated to use Python for cs1120 Fall 2009. It was then translated to use Java for cs1220 Fall 2012 by Jonathan Burket and Lenny Li. Portman is solely responsible for all the streaking references, however. It is based on a problem set first used in MIT 6.001 Fall 1989, and subsequently in many other years including 1997, on which we based some of our code. The MIT version of the adventure game involved Deans smashing beer and party-troll eating Deans. A course at UC Berkeley also had an adventure game problem set. Their acknowledgment was, "This assignment is loosely based on an MIT homework assignment in their version of this course. But since this is Berkeley we've changed it to be politically correct; instead of killing each other, the characters go around eating gourmet food all the time. N.B.: Unless you are a diehard yuppie you may feel that eating gourmet food does not express appropriate sensitivity to the plight of the homeless. But it's a start." Despite Question 4, we like our Deans much more than they do at MIT, and UVA students don't eat much gourmet food. We thought about making our adventure game about eating Berkeley students, but were worried that might improve their student-faculty ratio and ruin our chances of ever surpassing them in the US News rankings.