Project 2: Adventure Game Due Friday 7/23 at 12PM (NOON) That's right, your second project is a game! We'll be making a program similar to text adventures that you may have played with before. In this project, you will use object-oriented programming to design a framework for a game. By the time you finish, you will be able to load up a game that runs off of your framework, and play it to completion! What we've provided is pretty bare-bones. It's provided mainly as a jumping off point. Your job is to implement all the new features that will make this feel less like a sandbox and more like a playable game. This project is designed to be done by two people working in parallel. At various points in the project, we will ask you to stop and combine your code. Take a moment with your partner to decide who will be Person A and who will be Person B. The project begins with two exercises that everyone should do. These exercises do not require you to write any new code; rather, they are intended to familiarize you with the provided program. After you complete these intro questions together, each person will have their own features to implement for the remainder of the project. Please start early on this project! This project used to be split into two parts, but due to the fast pacing of the summer curriculum, we felt it would be better to release the entire project at once. (Acknowledgement: This assignment is loosely based on an MIT homework assignment in their version of this course.) In this assignment, we will be exploring two key ideas: the simulation of a world in which objects are characterized by a set of state variables, and the use of message passing as a programming technique for modularizing worlds in which objects interact. OBJECT-ORIENTED PROGRAMMING is becoming an extremely popular methodology for any application that involves interactions among computational entities. Examples: -- operating systems (processes as objects) -- window systems (windows as objects) -- games (asteroids, spaceships, gorillas as objects) -- drawing programs (shapes as objects) GETTING STARTED To start, copy the following five files into your directory: ~cs61a/lib/obj.scm The object-oriented system* ~cs61a/lib/tables.scm An ADT you'll need for some parts* ~cs61a/adv/adv.scm The adventure game program ~cs61a/adv/adv-world.scm A world you can use for debugging * -- You do not need to read/understand these programs You can copy the files using the CP unix command. For instance, if you wanted to copy adv-world.scm to your current directory, you would use the following command: cp ~cs61a/adv/adv-world.scm . Note the extra dot; CP won't work without it. To begin working on this project, all of the required files must be located in a single directory. From there, you can load up adv-world.scm, which is a sandbox world where you can try out the new features you implement. Later on, we'll also release a file named game.lvl. game.lvl is intended to show off the kinds of games you can create with your adventure game framework! When this file is released, you should be able to load it and play a short game. However, how far you'll be able to get in this game depends on how many features you've implemented. Get cracking! Before getting started on the actual assignment, you should also make sure to familiarize yourself with the Objected-Oriented Programming: Above the Line and the Object-Oriented Programming: Reference Manual sections in the old course reader. These resources are available on the course website; check the calendar section of the course website. If you have any questions, feel free to get clarification on the Google Group, but remember: DON'T POST YOUR CODE! In this program there are three classes: THING, PLACE, and PERSON. Here are some examples selected from adv-world.scm: ;;; construct the places in the world (define Soda (instantiate place 'Soda)) (define BH-Office (instantiate place 'BH-Office)) (define Sproul-Plaza (instantiate place 'Sproul-Plaza)) (define Telegraph-Ave (instantiate place 'Telegraph-Ave)) (define Noahs (instantiate place 'Noahs)) (define Intermezzo (instantiate place 'Intermezzo)) (define s-h (instantiate place 'sproul-hall)) ;;; make some things and put them at places (define bagel (instantiate thing 'bagel)) (ask Noahs 'appear bagel) (define coffee (instantiate thing 'coffee)) (ask Intermezzo 'appear coffee) ;;; make some people (define Brian (instantiate person 'Brian BH-Office)) (define hacker (instantiate person 'hacker Soda)) ;;; connect places in the world (can-go Soda 'south sproul-plaza not-locked) (can-go sproul-plaza 'north soda not-locked) Having constructed this world, we can now interact with it by sending messages to objects. Here is a short example. ; We start with the hacker in Soda. > (ask Soda 'exits) (NORTH SOUTH) > (ask hacker 'go 'north) HACKER moved from Sproul Plaza to SODA We can put objects in the different places, and the people can then take the objects: > (define Jolt (instantiate thing 'Jolt)) JOLT > (ask Soda 'appear Jolt) APPEARED > (ask hacker 'take Jolt) HACKER took JOLT TAKEN The first two exercises in this part should be done by everyone -- that is, everyone should actually sit in front of a terminal and do it! It's okay to work in pairs as long as you all really know what's going on by the time you're finished. Nevertheless, you should only hand in one solution that you both agree on. The following problems should be done in adv-world.scm: 1. Create a new person to represent yourself. Put yourself in a new place called Dormitory (or wherever you live) and connect it to campus so that you can get there from here. Create a place called Kirin, north of Soda. (It's actually on Solano Avenue.) Put a thing called Potstickers there. Then give the necessary commands to move your character to Kirin, take the Potstickers, then move yourself to where Brian is, put down the Potstickers, and have Brian take them. LIST ALL THE MESSAGES THAT ARE SENT DURING THIS EPISODE. It's a good idea to see if you can work this out in your head, at least for some of the actions that take place, but you can also trace the ask procedure to get a complete list. You don't have to hand in this listing of messages. (Do submit a transcript of the episode without the tracing.) The point is that you should have a good sense of the ways in which the different objects send messages back and forth as they do their work. [Tip: we have provided a MOVE-LOOP procedure that you may find useful as an aid in debugging your work. You can use it to move a person repeatedly.] 2. It is very important that you think about and understand the kinds of objects involved in the adventure game. Please answer the following questions: 2A. What kind of thing is the value of variable BRIAN? Hint: What is returned by Scheme if you type BRIAN into the prompt? 2B. List all the messages that a PLACE understands. (You might want to maintain such a list for your own use, for every type of object, to help in the debugging effort.) 2C. We have been defining a variable to hold each object in our world. For example, we defined bagel by saying: (define bagel (instantiate thing 'bagel)) This is just for convenience. Every object does not have to have a top-level definition. Every object DOES have to be constructed and connected to the world. For instance, suppose we did this: > (can-go Telegraph-Ave 'east (instantiate place 'Peoples-Park)) ;;; assume BRIAN is at Telegraph > (ask Brian 'go 'east) What is returned by the following expressions and WHY? > (ask Brian 'place) > (let ((where (ask Brian 'place))) (ask where 'name)) > (ask Peoples-park 'appear bagel) 2D. The implication of all this is that there can be multiple names for objects. One name is the value of the object's internal NAME variable. In addition, we can define a variable at the top-level to refer to an object. Moreover, one object can have a private name for another object. For example, BRIAN has a variable PLACE which is currently bound to the object that represents People's Park. Some examples to think about: > (eq? (ask Telegraph-Ave 'look-in 'east) (ask Brian 'place)) > (eq? (ask Brian 'place) 'Peoples-Park) > (eq? (ask (ask Brian 'place) 'name) 'Peoples-Park) Okay. Suppose we type the following into Scheme: > (define computer (instantiate thing 'Durer)) Which of the following is correct? Why? (ask 61a-lab 'appear computer) or (ask 61a-lab 'appear Durer) or (ask 61a-lab 'appear 'Durer) What is returned by (computer 'name)? Why? 2E. We have provided a definition of the THING class that does not use the object-oriented programming syntax described in the handout. Translate it into the new notation. 2F. Sometimes it's inconvenient to debug an object interactively because its methods return objects and we want to see the names of the objects. You can create auxiliary procedures for interactive use (as opposed to use inside object methods) that provide the desired information in printable form. For example: (define (name obj) (ask obj 'name)) (define (inventory obj) (if (person? obj) (map name (ask obj 'possessions)) (map name (ask obj 'things)))) Write a procedure WHEREIS that takes a person as its argument and returns the name of the place where that person is. Write a procedure OWNER that takes a thing as its argument and returns the name of the person who owns it. (Make sure it works for things that aren't owned by anyone.) Procedures like this can be very helpful in debugging the later parts of the project, so feel free to write more of them for your own use. Now it's time for you to make your first modifications to the adventure game. This is where you split the work individually. PERSON A: A3. Currently, our game lacks depth. One potential reason is that we don't know very much about the places we visit. In fact, aside from their names and exits, places are completely indistinguishable! To remedy this problem, we would like to give each place its own description. To accomplish this, we will give each place object a new instance variable named description. Hopefully, this will give players a better idea of their surroundings. Description should have an initial value of the empty word. However, having a description instance variable won't accomplish very much unless we have a way to set it as well! Add a method set-desc! to the place class. set-desc! should take a doubly-quoted string and set the description variable to this string. > (ask sproul-plaza 'set-desc! "Flyers are scattered all over the plaza around you. A fountain nearby is overflowing with soap suds." 'okay > (ask sproul-plaza 'description) "Flyers are scattered all over the plaza around you. A fountain nearby is overflowing with soap suds." Lastly, add a method LOOK to the PERSON class, which simply displays the DESCRIPTION of the person's current location. NOTE: Strings are something we don't talk about very often in this course, and you won't be responsible for understanding them. Here, we use strings so that our descriptions can have capital letters (since in words and sentences, capitalization is not preserved). A4a. We've provided people with the ability to say something using the messages TALK and SET-TALK!. As you may have noticed, some people around this campus start talking whenever anyone walks by. We want to simulate this behavior. In any such interaction there are two people involved: the one who was already at the place (hereafter called the TALKER) and the one who is just entering the place (the LISTENER). We have already provided a mechanism so that the listener sends an ENTER message to the place when entering. Also, each person is ready to accept a NOTICE message, meaning that the person should notice that someone new has come. The talker should get a NOTICE message, and will then talk, because we've made a person's NOTICE method send itself a TALK message. (Later we'll see that some special kinds of people have different NOTICE methods.) Your job is to modify the ENTER method for places, so that in addition to what that method already does, it sends a NOTICE message to each person in that place other than the person who is entering. The NOTICE message should have the newly-entered person as an argument. (You won't do anything with that argument now, but you'll need it later.) Test your implementation with the following: (define singer (instantiate person 'rick sproul-plaza)) (ask singer 'set-talk "My funny valentine, sweet comic valentine") (define preacher (instantiate person 'preacher sproul-plaza)) (ask preacher 'set-talk "Praise the Lord") (define street-person (instantiate person 'harry telegraph-ave)) (ask street-person 'set-talk "Brother, can you spare a buck") Please include a transcript of adv-world.scm in which your character walks and triggers these messages. A4b. It's great that people can now say something to your character once they notice him or her, but what if we want a person to do something in addition to simply saying something? Give the PERSON class a new instance variable ON-NOTICE-PROCS. This variable will hold a list of procedures for a person to perform once he/she notices someone. Procedures added to this list should take a PERSON object as an argument. Also give the PERSON class a new method, ADD-NOTICE-PROC. This method should take a procedure as an argument and add it to the END of the ON-NOTICE-PROCS list. Next, give the PERSON class a new method, CLEAR-NOTICE-PROCS. This method should take no arguments and simply reset ON-NOTICE-PROCS to the empty list. Finally, modify the NOTICE method so that, after TALKing, a person should call every procedure in ON-NOTICE-PROCS, passing them the noticed person as an argument. You may find the higher-order procedure FOR-EACH helpful. Test your implementation by placing a person in Sproul Plaza. This person should, upon noticing someone else, perform the following actions: 1. Say "Please take a flyer!" 2. Instantiate a new THING named FLYER 3. Make the flyer appear at his current location 4. Force the noticed person to take the flyer using that person's TAKE method. Please include a transcript of adv-world.scm in which your character walks and triggers this action. A5. Now we're getting somewhere! Our game is starting to come to life: our places have descriptions, our people can talk, and they can perform actions whenever someone passes by. However, there's still a major problem here: players are free to go wherever they choose. Obviously, a game can't be very challenging without obstacles in the way, but as it stands right now a player could very easily skip all the beginning rooms and stroll straight into the final room! To combat this problem, we've implemented a rudimentary LOCK class. Note that each PLACE has an instance variable called EXIT-LIST. EXIT-LIST is an association list, where the keys are direction names (such as NORTH, WEST, and UP) and the values are two-element lists. The first element of these lists is a place indicating where this particular exit leads, and the second element is either #f (indicating an unlocked exit) or a LOCK object (which indicates that this exit is locked). Whenever a PERSON attempts to pass through an exit, it first checks to see if the exit is locked. If the exit is open (indicated by #f) or the LOCK has already been unlocked (indicated by the LOCK's STATE instance variable), the PERSON is free to pass. Otherwise, access is denied. However, the problem with the way LOCKs are implemented right now is that there is no way to unlock them! The way things stand, players will never be able to pass through a locked exit. As a solution, we will create a new class called ITEM-LOCK. An ITEM-LOCK initially acts like a normal LOCK. However, if the PERSON attempting to pass through the locked exit possesses a certain THING (a KEY, for example), the lock should unlock, and the exit should now be passable. Create the ITEM-LOCK class. ITEM-LOCK should be a subclass of LOCK. It should take an instantiation variable ITEM, which represents the THING required to unlock it. It should also have an instance-variable, CORRECT-MSG, which has an initial value of "". When a player INTERACTs with the ITEM-LOCK, it should check to see if the player is holding the ITEM required to unlock it. If so, it should remove the item from the player's location and inventory, change its own state to unlocked, and display CORRECT-MSG. If the player is NOT holding the required item, the ITEM-LOCK should simply display its MSG. (MSG is an instance variable of the LOCK class.) You should also provide a method SET-CORRECT-MSG!, which takes a string as an argument and sets the lock's CORRECT-MSG to be that string. Part of the exercise is for you to determine exactly how LOCKs work. To get started, you should look at the class definition for a basic LOCK, as well as the GO method from the PERSON class. INTERMISSION: Your partner also has a labeled intermission. When both of you reach your intermissions, take a moment to combine your code, and test to ensure everything still works. The two of you should also explain to each other the new features you have added and how they work, since the remaining portion of the project will rely on understanding your partner's code. A6. For this question, you will need to understand the attribute table that your partner implemented in part B4/B5. If your partner hasn't already explained this part of the project to you, go ask him/her about it now. Your partner has implemented health and strength already, but we still don't have a way for people to become stronger. In real life, people get stronger in a number of ways, most of which include exercise. In this case, since walking around is the only real exercise people get, we'll use the number of steps a person has taken as a way to determine how strong the person should be. Add a new attribute (not an instance variable!) to the PERSON class, STEPS, which should start at 0. Write a method, UPDATE-STEPS, that increases the STEPS counter by one. UPDATE-STEPS should also check to see if the number of steps taken has reached a multiple of 7 or 11. At multiples of 7, UPDATE-STEPS should increase STRENGTH by 1, and at multiples of 11, it should increase MAX-HEALTH by 5 and set HEALTH to the new MAX-HEALTH value. Each time you increase a stat, display a helpful message such as "You feel stronger" or "Your max health increased by 5". Now modify the PERSON class so that every time a person successfully moves in a direction, it also calls its own UPDATE-STEPS method. A7. We need a way to restore health after someone has lost it, so we're going to create a FOOD class. We will give things that are food two properties, an EDIBLE? property and a CALORIES property. EDIBLE? will have the value #T if the object is a food. If a PERSON eats some food, the person's HEALTH should increase by the food's CALORIES (without going over MAX-HEALTH). (Remember that the EDIBLE? property will automatically be false for objects other than food, because of the way properties were implemented in question B4. You don't have to go around telling all the other stuff not to be edible explicitly.) Write a definition of the FOOD class that uses THING as the parent class. It should return #T when you send it an EDIBLE? message, and it should correctly respond to a CALORIES message. Replace the procedure named EDIBLE? in the original adv.scm with a new version that takes advantage of the mechanism you've created, instead of relying on a built-in list of types of food. Now that you have the FOOD class, invent some child classes for particular kinds of food. For example, make a bagel class that inherits from FOOD. Give the bagel class a class-variable called NAME whose value is the word bagel. Make an EAT method for people. Your EAT method should look at your possessions and filter for all the ones that are edible. For each food in your possessions, it should increase your health by its calorie value, display a message such as "George ate the bagel and restored 3 health," and make the food item disappear (no longer be your possessions and no longer be at your location). You should not need to access the HEALTH attribute directly; instead, use the helper methods that your partner wrote in part B5b. A8. The last thing we're going to do is implement another type of LOCK. A hallmark of text adventure games is solving puzzles, so we're going to implement a PUZZLE-LOCK. A puzzle-lock is a lock that, instead of requiring a certain item or stat value, asks the player to answer a question instead. If the player correctly answers the question, the lock should open, and the player should be allowed to pass. A puzzle-lock should have an instantiation variable, SOLN, that represents the correct answer to the puzzle. It should also have two new instance variables, CORRECT-MSG and WRONG-MSG, both with an initial value of "". When you attempt to open a puzzle-lock, the puzzle-lock should display its MSG and prompt you to type in a response. If your answer is correct, the lock should display its CORRECT-MSG and unlock; otherwise, it should simply display the WRONG-MSG. You should also provide SET-CORRECT-MSG! and SET-WRONG-MSG!, which should take a string as an argument and simply set the CORRECT-MSG or WRONG-MSG to be that new string. You may find the following snippet of code useful: (read-line) ;;; only include this line while testing in ;;; adv-world.scm, remove afterwards (let ((input (read-line))) ( [code here] )) This line will prompt the player for input, then bind the input variable to whatever the player typed. Using this snippet of code, as well as everything you have learned about locks and the adventure game, create a PUZZLE-LOCK. NOTE: There are a few quirks with (read-line) that you may run into while testing your implementation: 1. If you type (ask stat-lock 'interact) directly into STk, you'll find that you need the first call to read-line in order for the input to be read correctly. However, if you're playing game.lvl, you'll find that it will prompt you for input twice and simply discard the first input. We advise you to include the extra (read-line) only while testing in adv-world.scm, and to remove it while playing game.lvl and for submission. 2. read-line takes the user input and places it in a list. This means that your SOLN should be stored as a list. In other words, SOLN to the question "1 + 1 = ?" should be stored as (2), not 2. --- End of Person A PART I, PERSON B: B3. Define a method TAKE-ALL for people. If given that message, a person should TAKE all the things at the current location that are not already owned by someone. B4a. Right now, every PERSON in our world is exactly the same. To make them a little more unique, we want people in our world to have attributes like STRENGTH or INTELLIGENCE. However, we aren't going to clutter up the person class by adding a local STRENGTH variable. That's because we can anticipate wanting to add lots more attributes as we develop the program further. People can have CHARISMA or WISDOM; things can be FOOD or not; places can be INDOORS or not. Therefore, you will create a class called BASIC-OBJECT that keeps a local variable called PROPERTIES containing an abstract data type called Table. A Table in this case is one-dimensional; you can lookup a key and find the corresponding value. constructor: (make-table) returns a new, empty table. mutator: (insert! key value table) adds a new key-value pair to a table. selector: (lookup key table) returns the corresponding value, or #F if the key is not in the table. You'll learn how tables are implemented in 3.3.3 (pp. 266-268). For now, just take them as primitive. You'll modify the person, place and thing classes so that they will inherit from basic-object. This object will accept a message PUT so that > (ask George 'put 'strength 9001) does the right thing. Also, the basic-object should treat any message not otherwise recognized as a request for the attribute of that name, so > (ask George 'strength) 9001 should work WITHOUT having to write an explicit STRENGTH method in the class definition. Don't forget that the property list mechanism in 3.3.3 returns #F if you ask for a property that isn't in the list. This means that > (ask George 'charisma) should never give an error message, even if we haven't PUT that property in that object (although we all know that George's real-life charisma value is actually very high). This is important for true-or-false properties, which will automatically be #F (but not an error) unless we explicitly PUT a #T value for them. B4b. You'll notice that the type predicate PERSON? checks to see if the type of the argument is a member of the list '(person place thief). This means that the PERSON? procedure has to keep a list of all the classes that inherit from PERSON, which is a pain if we make a new subclass. We'll take advantage of the property list to implement a better system for type checking. If we add a method named PERSON? to the person class, and have it always return #T, then any object that's a type of person will automatically inherit this method. Objects that don't inherit from person won't find a PERSON? method and won't find an entry for person? in their property table, so they'll return #F. Similarly, places should have a PLACE? method, and things a THING? method. Add these type methods and change the implementation of the type predicate procedures to this new implementation. B5a. Now that our attribute table has been properly set up, we can begin to give people different stats. Let's begin by implementing strength. Give each person an attribute, STRENGTH, which is a number indicating how strong that person is. Give people an initial STRENGTH value of 2. (We can't all be as strong as George.) In other words, every time you instantiate a new person, that person should already have a STRENGTH entry, and its value should be 2. For now, we'll say that STRENGTH will remain fixed. Your partner will later implement a way for people to get stronger (which will happen naturally as they walk around). B5b. Now that we've implemented strength, let's implement health. Each person will have an attribute, HEALTH, which is a number indicating how healthy a person is. If this number reaches zero, then we say the PERSON has been knocked out. In addition, each person will have an attribute, MAX-HEALTH, which represents an upper limit for the HEALTH attribute. Give people initial HEALTH and MAX-HEALTH values of 10. However, unlike strength, which remains fixed, there isn't much of a point to having a HEALTH attribute if we can't change it! Implement two new methods, INCREASE-HEALTH and DECREASE-HEALTH. INCREASE-HEALTH should take a number as an argument and increase the person's HEALTH by that amount. However, HEALTH should never increase beyond MAX-HEALTH. INCREASE-HEALTH should return the amount by which HEALTH was actually increased. DECREASE-HEALTH should take a number as an argument and decrease the person's HEALTH by that amount. However, HEALTH should never drop beyond 0. If a call to DECREASE-HEALTH causes a person's HEALTH to drop to or beyond 0, invoke a person's FAINT method. DECREASE-HEALTH should return the amount by which HEALTH was actually decreased. The FAINT method has already been provided, and simply causes a person to drop all their possessions and disappear from their current location. INTERMISSION: Your partner also has a labeled intermission. When both of you reach your intermissions, take a moment to combine your code, and test to ensure everything still works. The two of you should also explain to each other the new features you have added and how they work, since the remaining portion of the project will rely on understanding your partner's code. B6a. Now that we've implemented stats, we want to impose some limits on how TAKE works. We want to change TAKE so that before actually taking the object, we check if we're allowed to with (ask thing 'MAY-TAKE? receiver) If a thing belongs to no-one, a person should always be allowed to take it. The MAY-TAKE? method for a thing that belongs to someone should compare the strength of its owner with the strength of the requesting person to decide whether or not it can be taken. The method should return #F if the person may not take the thing, or the thing itself if the person may take it. This is a little more complicated than necessary right now, but we are planning ahead for a situation in which, for example, an object might want to make a clone of itself for a person to take. Note the flurry of message-passing going on here. We send a message to the taker. It sends a message to the thing, which sends messages to two people to find out their strengths. B6b. Let's change it up a little now. Let's create something called a BIG-THING. A BIG-THING is exactly like a normal THING, except it also has an extra instantiation variable, WEIGHT. Big-things are naturally very heavy, so only people strong enough to lift their weight should be able to TAKE them. Create the BIG-THING class, and modify the MAY-TAKE? method so that, in addition to its previous behavior, it also checks to see if the person attempting to take it has a strength greater than our equal to its weight. Again, if the person may not take the thing, return #F, otherwise return the BIG-THING itself. Do not copy your code from part B6a. Instead, use USUAL to call the parent's MAY-TAKE? method. B7. The last thing we will do with stats is ensure that only people with a certain amount of strength can unlock a particular exit. This might be the case if, say, an exit in a cave was blocked by rubble; a person would need to be strong enough to lift the rocks away to open the exit. To accomplish this, we will define a new type of lock, STAT-LOCK. A stat-lock should work similarly to the ITEM-LOCK your partner has created, except that instead of checking for an item, it should check how high a particular attribute is. This means that a stat-lock will need to take two instantiation variables: STAT, which tells the lock which attribute to examine, and VALUE, which represents the minimum value required to open this lock. It should also take an instance-variable CORRECT-MSG, which has an initial value of "". A STAT-LOCK should display its normal MSG when it has not been successfully unlocked, and should display its CORRECT-MSG when it IS successfully unlocked. (MSG is an instance variable of the vanilla LOCK class.) Create the STAT-LOCK class. If you need help figuring out how locks work, ask your partner to explain how an ITEM-LOCK is created, and take a look at the definition of a normal LOCK. B8a. Define a method (GO-DIRECTLY-TO new-place) for the PERSON class. Go-directly-to does not require that the new-place be adjacent to the current place. So by calling (ask SOMEONE 'go-directly-to SOMEWHERE) will send the person SOMEONE to SOMEWHERE immediately. B8b. We will use this new method to create a new type of place, BUS-STOP. Walking all over the place can get boring really quickly, so a BUS-STOP should allow people to move very quickly to another location. Create the BUS-STOP class as a subclass of the PLACE class. BUS-STOP should take an instantiation variable PASS, which represents the item required to ride the bus (after all, you can't ride the bus unless you have a bus pass). It should also have an instance variable, DESTINATION, which holds either #F or another PLACE. Finally, we need a method RIDE, which takes a person as an argument. If the person given as an argument to RIDE is holding the proper item and the bus stop is not out of order (its destination isn't #f), tell that person to GO-DIRECTLY-TO the bus stop's destination. --- End of Person B 9. Combine the two partners' work. For example, both partners have created new methods for the PERSON class. Both partners have done work involving strengths of kinds of people; make sure they work together. After this, you can submit! Don't forget to include your transcripts. OPTIONAL: Try playing through the game we made. It shows off how to use all the features we've implemented to make a game that is hopefully at least somewhat fun to play!