|
Homework J8Due: Tuesday, 6 Dec 2005 by 10 a.m. |
This assignment will give you practice using Vectors and two-dimensional arrays as we finish building our simple text adventure game. In this assignment you will create a Map class that represents a grid of rooms, each connected to one or more of its neighbors. The grid will be represented as a two-dimensional array of Room objects; you will use loops to initialize each of the rooms in the array to contain a random description and, with some probability, a random monster and a random weapon. Constructors for the Map class will allow the user to specify the size of the grid (number of rows & columns), the percentage chance that a given room contains a monster/weapon, etc. You will also write a method to test whether all monsters have been killed. Finally, you will also implement a createMaze() method that connects the rooms in a random fashion to create a maze, using an algorithm we describe. When finished, you will have created a simple game in which a player wanders through a randomly-generated maze of rooms, finding weapons and fighting monsters. When all monsters are destroyed, the player wins.
The Map class needs to be in a Map.java file, for which we are providing the skeleton code with a few of the methods already implemented. Also, you will need the Game.java, which contains the main() method that will run the game.
Of course, we will build on the Creature, Parser, Weapon, Room, Descriptions, and MapPrinter classes that you have implemented over the last few homeworks and labs. To avoid being caught unawares by possible bugs in your code from previous assignments, you are encouraged to use the precompiled versions of these classes, provided here, while coding and debugging your assignment. Right-click on the class name, select "save as...", and save the file. Remember that to use the precompiled .class files, they must be saved to the same directory as the Map.java file that you are creating for this assignment.
Weapon and Creature classes from HW J6. The Creature class was slightly modified in HW J7 (which added the anger methods and the hit-points remaining message). The Weapon class needs to be modified a bit, as described below. The versions linked to here are the modified versions.
Room class from HW J7. This class also needs to be modified a bit, as described below. The versions linked to here are the modified versions.
Descriptions class from lab 10
MapPrinter class from lab11. Note that we use a lower case 'o' for an empty room instead of a space.
We strongly suggest you use the precompiled versions above while coding and debugging this assignment, unless you are very confident that your homework submission was completely correct and bug-free.
Note that when you compile the code, you will receive a warning saying, "Map.java uses unchecked or unsafe operations. Recompile with -Xlint:unchecked for details." You can safely ignore this warning (it deals with the fact that the Vector class uses generics, which is something that is not gone over until CS 201).
Documentation for these classes can be found here.
We are going to add a text description to the name of a weapon to let the user know how powerful it is. For example, if you have a katana and you find a pair of nunchucks, how do you know which is better? Note that for this game, all weapons do the same amount of damage (yes, this is not the most realistic, but much simpler for you to code). Thus, we will be adding a modifier to help the player determine how powerful of a weapon it is. The modifiers are: cursed, normal, shiny, high quality, elite, and magical. This modification need only be made to the getFullName() method in the Weapon class. The new code for this method is as follows.
public String getFullName() { String modifier = ""; if ( damageModifier < 0 ) modifier = "cursed"; else if ( damageModifier < 3 ) modifier = "normal"; else if ( damageModifier < 6 ) modifier = "shiny"; else if ( damageModifier < 9 ) modifier = "high quality"; else if ( damageModifier < 12 ) modifier = "elite"; else modifier = "magical"; return modifier + " " + getName(); }
Note that we cannot modify the getName() method, as that is needed elsewhere. The Weapon class that we provide has this method already implemented.
The Room class is from homework J7, with the extension described here. The Room class has been extended with two new fields:
and the following methods:
Lastly, when the Room class calls the getName() method from a Weapon object, it should (usually) call getFullName() instead. The one exception to this is when the user does a 'get' (to pick up a weapon), the Room class needs to check if the player's weapon is "bare hands" (via player.getWeapon().getName().equals("bare hands")). This should stay as getName().
The Room class that we provide has these methods and fields already implemented.
We provide this class in the Game.java file. As in the previous homework, the Game class provides the main() method for running the game program. Unlike the previous version, the Game class provided for this homework uses your Map class to set up the rooms, creatures, and weapons. Once this is set up, the game creates the Creature object that represents the player, prints out an introduction, and enters a loop that repeatedly calls the enterRoom() method of the Room object in which the player is located until the player dies, quits, or kills all of the monsters in the game.
You do not need to modify or submit Game.java.
Your Map class should use the following instance variables:
Room rooms[][]: a two-dimensional array of Room objects. The user will not need to manipulate this array directly, so this should be private and does not need accessor/mutator methods.
boolean visited[][]: a two-dimensional array of boolean variables. This array is used by the algorithm to generate a maze, described in the connectMaze() method below. The values should all be set to false, which Java will do by default - you do not need to include code for this purpose. Note that this method has nothing to do with whether the player has visited the room -- it is only used in the maze generation algorithm, below.
int height: the number of rows in the grid represented by the rooms[][] array. Because this is set when the Map is created and cannot be changed, you should provide an accessor (i.e. getHeight()) but no mutator.
int width: the number of columns in the grid represented by the rooms[][] array. Again, you should provide an accessor but not a mutator.
int maxMonsterStrength, maxMonsterSkill, maxMonsterHitPoints, maxMonsterArmorClass: the maximum strength, skill, hit points, and armor class of a monster in the Map, all with a default value of 20. You must provide accessor and mutator methods for each of these variables.
int maxWeaponDamageMod: the maximum damage modifier of a weapon in the map, with a default value of 5. You must provide accessor and mutator methods for this as well.
Descriptions desc = new Descriptions(): this object will be used in the constructor, below. Note that it should not be static.
Random rand = new Random(): this will be used to generate random numbers throughout the Map class code.
We provide two methods for you, as if they are coded incorrectly, the rest of your program will have problems working.
We also provide the skeleton code for isValidRoom() and getRoom(), but this method must be completed. It is included so that the Map.java file will compile properly.
Your Map class should provide the following methods:
Map(int columns, int rows): a specific constructor that
should create the rooms[][] array with the specified number of rows and
columns, then generate a Room object for each slot in the two-dimensional
array. The Room should be created using the
constructor that takes in a single String parameter. For this
parameter, you should generate a random description, using your desc
object,
of the form "<adjective> <room type>".
After each room is created, its row and column should be set to its i and
j indices using setColRow().
Also, the visited array should
be initialized to the same size as the rooms
array, with each value set to
false, and the width
and height fields should
be updated appropriately.
Map(int columns, int rows, int
seed): this specific constructor works the exact same as the
previous one, with the one addition that the rand instance variable is
initialized to a new Random object with the passed seed as
the parameter to the Random constructor. This will ensure that when this constructor is called,
the exact same map is produced for each value of
seed.
boolean isValidRoom(int i, int j): returns
true if
i and
j
are legal values for the rooms[][] array, meaning that:
0 <= i <
width
and
0 <= j <
height
otherwise returns false. We will call rooms with legal indices
valid rooms in this assignment.
Room getRoom(int i, int j): returns the Room reference
stored in array slot rooms[i][j], or
null if the user passes invalid
values for i and
j.
boolean allMonstersDead(): this method searches the entire Map, and returns true if there are no more monsters left (remember that if a monster is dead, then the getMonster() method for that Room will return null). If there is one or more monsters in the Map, then this method returns false.
The following methods are more complex and are explained in greater detail:
If you examine this algorithm carefully (you may want to trace it out
on paper for a small array such as 3x3 or 4x4) you can convince yourself
that it will create a nice random maze that connects all the rooms. You
can also convince yourself that the algorithm will not go on forever but
will eventually terminate (i.e. that the while loop will eventually exit
because the initial room in the vector v will eventually have no valid
unvisited neighbors, and will be removed).
Note that the Game.java file does not call this method by default --
instead, it calls the connectAllRooms() method. That
line needs to be commented out, and the generateMaze()
method uncommented.
Use the connectAllRooms() method first, since it is much simpler than the connectMaze() method. You can test the generation of monsters, weapons, random room descriptions, etc. on the resulting fully-connected Map, and move on to implementing the connectMaze() method once you are confident that the rest of the code is correct.
You will find lab 11 useful in debugging this homework, especially the generateMaze() method, since the lab develops a class to print out the rooms in a Map along with their connectivity. If you are having trouble debugging the generateMaze() method, you may wish to print out the map at every step of the maze algorithm -- this will help you track what the code is doing, step by step. After you print the maze each time, a call to stdin.nextLine() will pause the program until you enter a string (or just hit <return> to enter an empty string), allowing you to inspect the progress of the algorithm and then hit <return> to see the next step of the algorithm. This technique of stepping through a program is a very useful and general approach to debugging!
When you are finished, submit just your Map.java file. We will test it with the precompiled .class files that we provide above, so even if you have used your own versions of those classes, be sure to test your Map code with the precompiled classes as well!