Extending GridWorld

Cay S. Horstmann

DRAFT 2007-02-08

This note gives some hints to advanced users who would like to extend the GridWorld environment for their own class projects. Nothing in this note is required or suggested reading for the AP CS exam.

Other actors

The narrative explains how to design bugs and critters. For more general actors, you can simply subclass Actor, override the act method, and supply a GIF file.

NOTE: Images look best if their size is 48 x 48 pixels. If that is a problem, try 24 x 24 or 12 x 12 for smaller images/96 x 96 pixels for large images. Other image sizes will be scaled.

Example: Game of Life. Note that these actors alternate planning and living. The display changes with every second press of the Step button.

???

No actors

You don't have to use actors. In that case, use a World<Something>, not an ActorWorld. Here is an example of a loop practice lab that uses a simple World<Rock>. Of course, then the Step and Run buttons don't do anything.

???

Rendering occupants

Sometimes, you may want to draw occupants that are not GIF images.

The rendering algorithm is as follows:

  1. Check whether there is a class whose name is the name of the class with the suffix Display added, and that implements the info.gridworld.gui.Display interface. If so, its draw method is called. This is the same mechanism that was used in the MBS case study. (If you decide to supply such a class, you should extend the convenience class info.gridworld.gui.AbstractDisplay that automatically handles scaling and rotation.)
  2. Otherwise, check whether there is an image with the same name as the class and extension gif (in lowercase). If you use packages or JAR files, then the image must be in the same directory as the class file. If a matching image is found, the occupant is rendered by the info.gridworld.gui.ImageDisplay class. If the occupant class has a getImageSuffix method, it is called and the suffix is added to the image name. If the occupant class has a getColor method, it is called and the image is tinted with the result. If the occupant class has a getDirection method, it is called and the image is rotated.
  3. Otherwise, repeat the preceding checks for the superclass.
  4. If no superclass has a display class or GIF image, the image is rendered as a colored cell with text by the info.gridworld.gui.DefaultDisplay class. If the occupant class has a getColor method, it is called to determine the fill color of the cell background. If the method returns null or there is no such method, and the object is an instance of java.awt.Color, it is used as the color. (Note that you can subclass java.awt.Color.) If the occupant class has a getText method, it is called. If getText returns null or there is no such method, toString is called instead. The first 8 characters of the resulting string are called, with ... appended if the string is longer. If there is a textColor property, its value is used to color the text. Otherwise the text is black. However, if the fill color and the text color are the same, the text color is set to the opposite of the fill color.

What does all this mean for you?

The simplest occupants (such as numbers and Boolean values) are displayed correctly without any effort, simply by invoking toString.

Colored and labeled tiles can be done very simply: Provide a getText and, optionally, a getColor method. An example is the TileGame below.

If you have an occupant that needs different images depending on its state, then supply a getImageSuffix method. For example, a bug might be alive or dead. If the getImageSuffix method returns a string "" or "_dead", then you can supply two images MyBug.gif and MyBug_dead.gif. Note: (1) One of the images must have the same name as the class, even if you never use it. (2) The getImageSuffix method must return any separators such as underscores.

If you need to do a fancier drawing, supply a class that extends AbstractDisplay. You can use any of the Java AWT drawing calls in the draw method.

As an example, look at the code for FishDisplay. The result looks eerily familiar.

???

Finally, if you get an image from a superclass but you actually want a colored cell with text, then provide a display class that trivially extends the DefaultDisplay class, e.g.

public class MyTile extends Actor { . . . } 
// don't want to pick up Actor.gif
public class MyTileDisplay extends DefaultDisplay { }

Tweaking a world

You can tweak the behavior of your world as follows:

Other worlds

You can use the GridWorld framework for games and other applications. In this case, you will want to install your own subclass of the info.gridworld.world.World class. There are three main extension points.

Here is an example for the first modification: a tile game. When the user clicks on a tile, it is flipped over to reveal its top side. Click again, and the next tile is flipped. If they both match, they stay up. Otherwise, they are both flipped again.

???

Secret Flags

If you call

System.setProperty("info.gridworld.gui.selection", "hide");

the selection square is hidden. This may be useful for special environments (such as a Quzzle game in which it doesn't make sense to select a fractional tile.)

If you call

System.setProperty("info.gridworld.gui.tooltips", "hide");

the tooltips are hidden.

Call

System.setProperty("info.gridworld.gui.frametitle", titleString);

before showing the world to set a different title.

If you modify the framework to implement other special effects that shouldn't result in API clutter, consider using a similar “secret flag” mechanism.

Advanced Magic

The following is a more sophisticated morphing of the GridWorld environment in which the Step button has been redefined.

Students write a run method in a subclass of StepWorld, placing calls to pause when desired. Here is an example:

public class LoopWorld extends StepWorld<Rock>
{
   public void run()
   {
      int n = 5;
      for (int i = 1; i <= n; i++)
      {
         for (int j = 1; j <= 2 * i - 1; j++)
         {
            add(new Location(i, n - i + j), new Rock());
            pause("i=" + i + ",j=" + j);
         }
      }
   }
}

In GridWorld, press the Step button. The program runs to the next call of the pause method. Keep doing this to see how the rock pile is built up one step at a time.

???

This allows the student to single-step through the loop without having to run a debugger.

Here is the complete project. The StepWorld implementation isn't for the faint of heart, but students don't need to see it.