Lab 2
Lab 2 Description
The goal of this lab is to design a graphics-based interactive game, and along the way to practice test-first design. Additional exercises introduce the handling of special test cases.
You have a choice of either following the design of the ocean game that was presented during the lecture, or start from the scratch a design your own game from the beginning.
Ocean Game
Here is a short description of the Ocean Game:
Fish swim across the screen from right to left. There may be one fish, or a whole school of fish, or none at a time. A hungry shark swims in place at the left side, moving only up and down, controlled by the "up" and "down" keys. It tries to catch and eat the fish. It gets bigger with each fish it eats, it keeps getting hungrier and hungrier as the time goes on between the catches. It dies of starvation, if not fed in time.
The starter files:
The file that contains the images of the shark and the fish used in our game Images.zip.
Code with just the data definitions and examples of data ExamplesOceanData.java.
Code with the data definitions, methods for drawing the images, and examples of data ExamplesOceanImages.java.
Code with the data definitions, methods for drawing the images, methods for a game with just one fish, the complete templates, examples of data, and tests for the defined methods. ExamplesOceanSimple.java.
Solution code ExamplesOceanWorldFun.java.
Here is a picture of the game (once the shark has died):
Design the Ocean Game:
Start a new project and add to it one of the starter files. Add the libraries worldcanvas.jar, worldimages.jar, funworld.jar and colors.jar to the classpath.
In Eclipse, the image files shoud be in the same directory where the src and bin directpories are located.
Design the classes that represent one fish, one shark, and the whole game.
Of course, as the Design Recipe tells you, include the examples of instances of these classes.
Design the images for the game objects and the whole game.
Design the method onTick that moves the fish from right to left until it reaches the end, then starts again on the right. At the same time the shark gets hungrier with each tick.
Design the method onKeyEvent that moves the shark up and down as specified by the pressed key.
Design the method worldEnds that determines whether the game should end. The method produces an instance of the class WorldEnd. The constructor consumes a boolean value that is true if the game should end and is false if the game continues. The second argument to this method is the WorldImage that should be shown in each case.
Design the methods that check whether a shark can eat a fish, produce a fatter shark after it ate the fish, and replace the eaten fish with a new one at the right side of the ocean. Think of which class is responsible for eack task, and what does the onTick method need to register the chomping of the fish by the shark.
Guess what? You have just designed the game - you can run it.
Now replace one fish by a school of fish. A school of fish is either empty, or it consists of the first fish and the rest of the fish-es in the school.
Design all the needed methods for the scool of fish and finish the game.
Apple Orchard
As an alternative you can design a different game. Apples are falling from an apple tree and you are trying to collect them into a basket. The left and right keys move the basket on the ground. The game ends when the basket is full. (You decide how many apples fit into your basket.)
The basket can be just a brown rectangle, the apples can be red, yellow, and green disks. Later, you can replace these with images, and even draw an apple tree in the background.
Special Test Cases
One-of and Range checks
In our solution to the Ocean Game the fish do not just swim in a straight line. They move up or down some small random distance for each move to the left.
The tester library provides two ways for testing this kind of situation. The checkOneOf test checks that the actual value is equal to one of the several expected values. The checkRange method verifies that the actual value is within the range of the lower and upper expected values. For the primitive types it uses the built-in comparison. For Strings it uses the compareTo method defined in the String class (that implements the Comparable interface.
The testcases iin the test method testRandomMethods in the class ExamplesOceanSolution illustrates the design of these test cases.
The tester library web pages describe in detail all variants of this kind of tests.
One thing to keep in mind for these tests is that running just one or two tests that check that the given value is within some bounds (or one-of several options) may not reveal a flaw - either the out-of-bounds value is generated only rarely (and we missed it), or some of the expected values are never produced (and there is no way for us to tell).
Exceptions Thrown by Methods and Constructors
Often the constructors are designed in such a way that they verify the integrity of data, and possibly throw an exception if the provided data does not satisfy the expected restrictions. For example, we may decide that a shark can never swim outside of the bounds. So the constructor would check that the coordinates are within the bounds and throw an exception if that is not the case. (Of course, in real world, we may want to be lenient and just move the shark back within the bounds - but here we want to show how to handle exceptions thrown by the constructors.)
The code in the example ExamplesSharkOutOfBounds.java shows how this is done. It also shows how we can test the helper method that throws the exception.