CIS 211 - Computer Science II
Winter, 2003 - A. Hornof

Project 5

This Project is due on Monday, March 3, at 5 PM.

Purpose

This project will give you practice the following programming techniques:

  • Using encapsulation and information hiding to provide information on a need-to-know basis only.
  • Polymorphism achieved by overriding base methods.
  • Building a modular, decoupled system.
  • Using the Model-View-Controller architecture.
  • Writing a command-loop processor.
  • Reusing and extending existing code.

Overview

In this project, you will give "life" to your maritime world. Islands will produce oil. Ships will sail from island to island, burning fuel as they go. Each ship will also have some unique characteristics. Cruise ships will tour groups of islands. Pirate ships will raid cruise ships and tankers. Tankers will refuel ships that have run out of fuel in the middle of the ocean, and move oil from island to island. Ships will run out of fuel either because they used it all or because pirates took it. Cruise ships will have islands as destinations. Tankers and pirates can have both islands and ships as destinations. Cruiseships can have a series of islands as a chain of destinations on a tour, whereas tankers and pirates can have only one island at a time as a destination. Pirate ships can also set arbitrary screen locations as destinations, so they can sit waiting in a shipping lane for the next ship to come along.

Islands are safe havens for cruise ships and tankers, which cannot get attacked by pirates while at an island. Islands are dangerous to pirates. Pirates get thrown in jail for ten days if they land at an island.

The world description will be loaded from a disk file using the file format and code developed in previous projects. Ships will be commanded to moved using a command language that the user will type at a command prompt. The world will be viewed via a text-based map that you will create.

An Evolving Program and Specification

Project 5 builds on Project 4. You may use your previous code or you may build on the P4 solution provided for previous projects. You may not, however, start with any code written by other students in the class. If you re-use code, you should, in the header comment for each file, give credit to the original author and also note yourself as a subsequent author.

The Project 5 Addendum posted on the course web site is officially part of these project specifications. You should check it at least at the beginning of each work session. Be sure to start the project early enough to have an opportunity to ask for clarification in case it is necessary.

Basic Structure of the Program

The high level structure of the program is based on a simplified Model-View-Controller (MVC) architecture. In this project, the model keeps track of the simulated world of ships and islands, and periodically instructs them to update or describe themselves, the view presents a map of the world, and the controller interacts with the human user, handling all the input, and controlling the rest of the program. The MaritimeModel object keeps track of the simulated "world" of ships and islands; it is the keeper of which Ships and Islands exist, and keeps them all up to date, and instructs them to describe themselves either to the console output or to the View. The View in this case prepares and presents a map-like view of the World. These three objects are very isolated from the details of the ships and the islands. Basically, the Model and Controller do not know about the different kinds of Ships; they only know about the base class Ship, not about Tankers and Cruise_Ships. The View only knows about the kinds of objects in the world, called SimObjects: Ships and Islands are both SimObjects. Because of this structure, in the future new kinds of Ships or Islands can be added with little or no change to the Model, View, or Controller, and vice versa for the Ship and Island classes.

Model

The MaritimeModel class used in Project 4 will be reused and extended. Here are a few modifications that will be needed:

  • The world starts at (0, 0) and continues to (1-X, 1-Y), where the world size (X, Y). So (0,0) is now an acceptable location, and an object could not be at location (10,10) if the world size is (10, 10). Change the error checking in the maritime file format-checking accordingly.
  • The file format-checking should also verify that every island and ship have a unique name, throwing an error for the first line that reuses a name.
  • Give each ship a fuel tank with a maximum capacity of 20 tons of fuel, used to fuel the ship's engine. The engine fuel tank starts full. Give tankers an additional cargo tank for carrying a maximum of 1000 tons of fuel. The cargo tank starts empty. It can be used to move fuel from island to island, or to refuel other ships that ran out of fuel in the middle of the ocean, but it cannot be used to directly refuel itself.
  • Each grid unit is 100 nautical miles (nm). In other words, location (0, 0) is 100 nautical miles from location (0, 1).
  • The ship specification in the data file will be extended to include two more numbers: <maximum speed> and <tons per nautical mile>. The maximum speed is in knots (nautical miles per hour). The second is the tons of fuel burned to travel a nautical mile.
  • All speeds, capacities, and locations should use doubles instead of integers. All locations are now (double, double). All doubles should be rounded to two decimal places before being displayed on the screen or saved to a data file.

View

The View class will have a single public method, show(), that will display the world in the standard output using a square matrix of cells, with each cell containing two characters. The matrix is X cell wide and Y cells high, where X and Y are the world size. Here is a map of a world with a world size of (21, 11).

 10 . . . . . . . . . . . . . . . . . . . . .
    . . . . . . . . . . . . . . . . . . . . .
    . . . . . . . . . . . . . . . . . . . . .
    . . Su. . . . . . . * . Sc. . . . . . . .
    . . . . . . . . . . . . . . . . . . . . .
  5 . . . . . No. . . . . . . . . . . Ce. . .
    . . . . . . . . . . . . . . . Va. . . . .
    . . . . . . . . . . . . . . . . . . . . .
    . . . . . . . . . Ja. . . . . . . . . . .
    . . . . . . . . . . . . . . . . . . . . .
  0 . . . . . . . . . . . . . . . . . . . . .
    0         5        10        15        20

Ships and islands are displayed on the map (on the nearest integer equivalent of their current position) using the first two characters of their name. On this map, you can see the following islands: Sumatra at (2,7), Java at (9,2), and Celebes at (17,5). You can see the cruiseship Noordam at (5,5), currently sailing between Sumatra and Java. You can see the tanker Valdez at (15, 4) and the Pirate ship Scoundrel at (12,7). An asterisk * indicates that more than one island and ship are at the same location. For example, the tanker Prestige is currently docked at Borneo at (10,7). (Note that there is an extra space to the left of the Y-axis labels in the map, to accommodate 3-digit labels.)

Controller

The program will run interactively from a command line, prompting the user with

Day 0. Enter command: _

Day is an integer that increases by 1 every time the "go" command is issued. A sample interaction (for a 11x6 world with this particular set of islands and ships) should look like the following. For illustration purposes, the user's inputs are underlined.

Day 0. Enter command: show 
  5 . . . . . . . . . . . 
    As. . . . . . . . . . 
    . . . Qu. . . . . . . 
    . . . . . . . . Va. No
    . . . . . . * . . . . 
  0 . . . . . . . . . . . 
    0         5        10 
Day 0. Enter command: go
Island Asia (0.00, 4.00) has 1800 tons of fuel. Island Hawaii (6.00, 1.00) has 200 tons of fuel. Island NorthAmerica (10.00, 2.00) has 200 tons of fuel. CruiseShip QueenLiz (3.05, 4.75) is sailing at 10 knots to Asia, Hawaii, NorthAmerica, and Asia with 8 tons of engine fuel. Tanker Valdez (8.65,2.21) is sailing at 5 knots to NorthAmerica with 1000 tons of cargo fuel with 8 tons of engine fuel. PirateShip Rascal is docked at Hawaii with the crew in jail for 6 more days. Day 1. Enter command: _

Command Set

The entire command set is as follows. Each command is followed by a description.

show
Display the map.
status
Outputs a textual description of the current state of the world, including the current status of all ships and islands.
go
Advance the simulation's clock by one day, show all ship and island interactions (messages specified below), and then show the world status.
sail <ship-name> <island-name> <speed>
Tell a ship to set sail to an island at a given speed.
sail <cruise-ship-name> <island-name-1> [island-name-2 ...] <speed>
Tell a cruise ship to set sail for any number of islands, in the order they are listed.
sail <pirate-ship-name> <X> <Y> <speed>
Tell a pirate ship to set sail to destination (X,Y).
attack <pirate-ship-name> <ship-name> <speed>
Tell a pirate ship to sail to ship-name and take all the engine fuel when they get to the ship.
rescue <tanker-name> <ship-name> <speed>
Tell tanker-name to sail to ship-name so it can be refueled.
refuel <ship-name>
Fill the engine tank of the ship using the fuel from the island where it is docked or the tanker that has arrived to rescue it.
load <ship-name>
Fill the cargo fuel tank of a tanker from island where it is docked.
unload <ship-name>
Unload the cargo fuel tank to the island where it is docked.
quit
Quit the simulation. User is subsequently prompted to save the world model.
help
Give the user a list of all commands and arguments that are available, as follows.
Commands available:
show
status
go
sail <ship-name> <island-name> <speed>
sail <cruise-ship-name> <island-name-1> [island-name-2 ...] <speed>
...

You will need to write a command loop processor in the controller module that continually prompts the user for the next command, and parses and executes commands. If an invalid command is entered, an informative error message should be provided whenever possible. Use the following messages, returning the first message in the list that applies to the situation.

Error: No such ship.
Error: Only a tanker can rescue.
Error: Only a pirate ship can attack.
Error: Not a valid destination.
Error: Not a valid speed.
Error: Unrecognized command.

Public interfaces and class responsibilities

These are the new public instance methods associated with each class. Each has a return type of void unless otherwise specified.

SimObject class

  • Abstract class that holds the name and location of an island or ship object.
  • You may add abstract methods as needed.
  • Location getLocation() returns the location.
  • String getName() returns the name.

Island class

  • Maintains the amount of fuel stored on the island. The total amount is unlimited.
  • double giveFuel(double d) responds to a request for d tons of fuel. It returns the requested amount of fuel, or whatever was available, whichever is less. It then substracts that amount of fuel from the fuel stored at that island.
  • takeFuel (double d) adds d tons of fuel to an island.
  • update() is called once with every clock cycle. It adds the amount of oil produced in a day (the production rate) to the fuel stored on the island.
  • describe() sends the following to the standard output:
    Island <name> <location> has <fuel> tons of fuel.

Ship class

  • Maintains the status of the ship, which could be one of the following:
    - Sailing to a destination, in which the destination is an island, ship, or location.
    - Stopped at a location in the middle of the ocean. This could be a ship out of fuel, a tanker during or after a rescue, or a pirate ship waiting for a ship to attack.
    - Docked at an island.
    The status can be further elaborated depending on the specific type of ship. The details are provided in the shiptype descriptions that follow.
  • double getAttacked(double d) receives an attack from a pirate ship that has caught up to this ship, and the amount of fuel that the pirate ship would like to take (based on the pirate ship's current fuel level and capacity). Returns the amount requested, or the full contents of the tank, whichever is less. Both fuel levels are adjusted accordingly. Both ships stay stopped at this location.
  • update() moves the ship towards its destination at its current speed and decrements the amount of fuel needed for the distance traveled. If the ship doesn't have enough fuel for the entire distance, it gets as far as it can on its current fuel and then stops. If the ship can reach its destination in this move, it arrives at its destination.
  • describe() sends the ship's status to the standard output. Some or all of this could be handled by a method in the subclass. The ship's status is a single line put together as follows:
      [CruiseShip | Tanker | PirateShip] <name> is

    plus one of the following

      sailing to [<island> | <island-1>, ..., and <island-n> | <ship-name> | <location>]
      stopped at <location> 
      docked at <island-name

    plus, for tankers

      with <cargo-fuel-amount> tons of cargo fuel and

    plus one of the the following

      with <engine-fuel-amount> tons of engine fuel.
      with the crew in jail for <days-remaining> more days.

CruiseShip class

  • Maintain an itinerary of islands to visit after the current destination is reached. When a destination is reached, dock the ship for a day so that the tourists can see the sights for a day. Then, remove the first destination from the itinerary and make it the ship's new destination, and set sail on the next clock cycle.

Tanker class

  • Maintain an extended status of the ship, including
    - Whether the tanker is currently in the process of rescuing a ship (other than itself).
  • double giveFuel(double d) responds to a request for d tons of fuel. It returns the requested amount of fuel, or whatever is left in the cargo tank, whichever is less. The cargo tank is adjusted accordingly.
  • fillCargoTank() fills the tanker's cargo tank from the oil supply of the island where the ship is docked.

PirateShip class

  • Maintain an extended status of the ship, including
    - Whether the pirate ship is currently in the process of attacking a ship.
    - If the crew is in jail and, if they are, how many days are left in their time in jail. (This should be decremented by one with every clock cycle.)

View class

  • show() displays the current model in the map described above.

Controller class

  • run() starts the command processing loop, in which the user is prompted for the commands listed earlier, and in which the appropriate methods are called to execute the user's commands.

ControllerException class

  • Used to catch user input errors caught in the command processing loop.

Interactions between Ships and Islands

In the simulation, legal fuel-moving activities are initiated as soon as they are issued, but sail, attack, and rescue commands get set up while time is not moving and then get executed all at once as time lurches forward in discrete jumps with each "go" command.

Here are some details for executing movement commands when the user types "go".

  • If a ship is given conflicting commands for the same cycle, the most recent command overrides.
  • Ships get updated in the order that they appeared in the input data file, except that pirate ships are updated before cruise ships and tankers. As a side effect, if two pirate ships try to attack each other, the one that appeared first in the input file wins.
  • If a ship is sailing to a destination and can reach the destination during the move, or will arrive within 0.1 nm, then the ship's location gets set to the destination location, and one of the following two messages is displayed:
    <ship-name> has arrived and docked at <island-name>.
    <tanker-name> has arrived to rescue <ship-name>.
    <pirate-ship-name> has caught <ship-name>.
  • When a pirate ship catches (or can get within 0.1 nm of) a ship it is attacking, the following message is displayed, and both ships stop at the attacked ship's location.
    <pirate-ship-name> has caught <ship-name> and is stealing its engine fuel!

    After the attack, both ships stay at the location until commanded to do otherwise.

  • If a pirate ship docks at an island, the pirates get sent to jail for ten days.
  • If a docked ship is attacked by a pirate ship, the attack is thwarted and the pirates get sent to jail.
  • After serving a jail sentence, the pirate ship gets a full tank of gas, the ship stays docked at the island until commanded to do otherwise, and the following message is displayed:
    The crew of <pirate-ship-name> have been freed!

P5.java

P5.java should be similar to P4.java, but should also include, after the model is created:

View view = new View();
MaritimeModel model = MaritimeModel.getInstance();
Controller controller = new Controller (model, view);
controller.run();

Packages

Continue to use packages as in Project 4. Add View.java, Controller.java, and ControllerException.java to the maritime package. Continue to use the "utils" package from Project 4 for file input and output.

Additional Notes

  • Keyboard input should be done using L&L's Keyboard class.
  • You may create whatever private methods and instances that you like, and you may need to create a few getter and setter methods, but otherwise try to stick to this specification for your public interface.
  • Wherever visibility modifiers are not specified throughout this project specification, you should close the visibility of all classes and methods as much as possible. Classes should become package-only whenever possible, and methods should be, in order of preference: private, package-only, protected, and public.
  • There should not be any calls to System.exit() in any of the code that you submit for this assignment.
  • The program must compile and run correctly using Java 1.4.1.

Some advice on how to do it

Start early so you can ask questions as needed. Once you get started, you will likely need clarification on some details.

If you are not fully confident in your Project 4, consider using the solution provided on the web.

Break the project down into steps, as was done for you in Project 4. For example, you could complete each of the following steps in the following order:

  1. Extend Project 4 as needed, adding the new functionality and changing everything to double.
  2. Build the View class and make sure it works correctly with a number of different input files.
  3. Get update() and describe() working correctly for all SimObjects.
  4. Get the command line processor working, starting with just a few key commands.
  5. Get the rest of the commands working, and get the error messages working.
  6. Fine tune the many possible ship and island interactions.
  7. Thoroughly test the system for all possible input file and command combinations.

After you complete each step, you should thoroughly test and debug your program, and then back up the entire thing in a new folder on a separate disk for when your case your hard drive crashes. You can be absolutely certainly that your hard drive will crash, eventually.

Compile regularly. Bring your code to working stopping points regularly.

Grading Criteria

You will receive credit for programs that compile using the correct package declarations and that solve the problems as specified. You will not receive any credit for code that does not compile from the command line using Java 1.4.1. Any problems with your Project 4 solution must be fixed for this project.