Map class assignment

From WLCS

Objective

  • You will learn to create a class that makes use of Vectors and two-dimensional (2D) arrays

Purpose

  • The purpose of this class is to encapsulate all the attributes and methods of the entire maze. The Map class 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.

Resources

Attributes

  • Your Map class should use the following attribute 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.

Methods

  • Your Map class should implement the following methods:

Constructors

  • Map(int rows, int cols): 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 setRowCol(). 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 rows, int cols, 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.

Accessors/Mutators

  • All the attributes are declared private, so you should create accessor (getters) and mutator (setters) methods for each attribute.

Other Methods

  • boolean isValidRoom(int r, int c): returns true if r and c are legal values for the rooms[][] array, meaning that:
   0 <= r < height 

and

   0 <= c < width

otherwise returns false. We will call rooms with legal indices valid rooms in this assignment.

  • Room getRoom(int r, int c): returns the Room reference stored in array slot rooms[r][c], or null if the user passes invalid values for r and c.
  • 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.
  • void populate(int percentWeapon, int percentMonster): visits each of the rooms and generates a random integer between 0 and 100 using the Random.nextInt() method. If the resulting random number is less than the specified percentWeapon parameter, a random Weapon is created and assigned to the room using the Room.setItem() method. A second random number is generated and tested similarly to decide whether to create a random Monster object according to the percentMonster probability.
    • To create the random monster and/or random creature, you will need to use random numbers between 0 and the values stored in the instance variables. For example, the number of hit points for a randomly generated monster is a number between 1 and maxMonsterHitPoints, which you can generate by calling 1+rand.nextInt(maxMonsterHitPoints). The reason for adding 1 is so that you don't get a skill (or armor class, etc.) of 0, as that can cause other parts of your code to have issues. Recall from HW J6 the specific constructor for the Creature class: Creature(int hp, int str, int skl, int ac, String name, String type, String pronoun). Each of the first four values (hp, str, skl, ac) are random numbers between 0 and the maximum value stored in the appropriate instance variable (maxMonsterHitPoints, maxMonsterStrength, maxMonsterSkill, and maxMonsterArmorClass, respectively). name can be anything (all your monsters can be named Fred, for example), type is a the random creature type from the desc.getNextMonsterName() method, and pronoun can just be "its".
  • void generateMaze(): constructs a maze by connecting the rooms in the rooms[][] array using a simple algorithm. Described in English, the basic idea of the algorithm is to visit each of the rooms in the array, connect the room to a random neighbor, then visit that neighbor and repeat, connecting to one of its neighbors, and so on. However, the algorithm does not allow connecting a room to a neighbor that has already been visited. If the algorithm gets "stuck", meaning that all of the neighbors of the current room have been visited, then it backtracks to the previously visited room. This notion of backtracking is implemented using a Vector: as each room is visited, it is added to end of the Vector, when the current room has no more valid neighbors, it is removed from the end of the Vector and the room which is now at the end of the (now shorter) Vector becomes the current room.
    • In more detail:
      1. Create a Vector variable named v
      2. Add the Room object at rooms[0][0] to v.
      3. Mark this room as visited by setting visited[0][0] to true.
      4. While the vector is not empty (i.e., while (v.size() > 0)), do the following:
        1. Set a local variable currentRoom to the Room at the end of the vector (i.e., Room currentRoom = (Room) v.get(v.size()-1))
        2. Check whether currentRoom has any valid neighbors that are not yet visited. A room [r][c] is not visited if visited[r][c] == false.
          1. If there are still valid neighbors that are not yet visited:
            1. Randomly choose one of those neighbors (repeatedly pick a neighbor at random until the chosen neighbor is valid and not visited). Let us call the chosen neighbor N. This is done as described in the getUnvisitedNeighbors() method, above.
            2. Connect currentRoom to N by calling the appropriate method to add an exit from currentRoom to N (for example, currentRoom.setRoomToEast(N), if the neighbor to the east was chosen as N) .
            3. Also connect N to currentRoom (for example, N.setRoomToWest(currentRoom)).
            4. Add N to the vector v (i.e., v.add(N)). Note that this means that the next time through the loop, N will become the new currentRoom.
            5. Update the visited array appropriately: the location in the visited array that corresponds to the location of the new room N should be set to true.
          2. Otherwise (all valid neighbors have been visited), remove the room at the end of the vector (i.e., v.remove(v.size()-1)). Note that this means that the next time through the loop, currentRoom will be set to a previous value -- this is how we implement the "backtracking" discussed above.

Testing

  • You are provided with Media:MapGame.java which creates a Map and tests your class
  • Note that Media:MapGame.java file does not call generateMaze() by default, but instead calls the connectAllRooms() method. If you comment out connectAllRooms(), you can uncomment the call to generateMaze() to test generateMase()
  • 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.