Java Fun and Games: It’s contest time

news
Feb 6, 200710 mins

Create a tiny game and win an entertaining prize

I recently discovered an interesting game-programming contest with entertaining prizes, which I encourage Java Fun and Games readers to enter.

Java Unlimited is a free service that promotes Java games to players and programmers. Java games are promoted by providing a huge library of free and entertaining Java games, by facilitating game-programming contests, and by giving game authors (regardless of their participation in contests) access to a community that provides valuable feedback for their games.

Java 4K is Java Unlimited’s first contest, described by the company as “the ultimate byte-squeezing Java challenge!” The idea: Build an entire game that fits into 4096 bytes. If your game satisfies the size limit and a few other rules, you could win a free copy of Tribal Trouble or six months of free playtime with Wurm Online.

The Java 4K Programming Contest page presents a complete set of rules and a link to a forum where you can ask questions. Because it is challenging to adapt your game to 4096 or fewer bytes, you will want to visit the 4KGamesDesign TWiki for tips on controlling game size.

Read on for a sample game that, one hopes, inspires you to create and submit your own contest entry.

Battle on the high seas

Sea battles are among my favorite computer games. I especially enjoy the challenge of destroying submarines that are also trying to destroy my ships. For this reason, I created a Sea Battle sample game in which a single ship and a single submarine attempt to destroy each other with depth charges and torpedoes. The figure below reveals this game’s graphics.

The figure reveals Sea Battle’s static background and dynamic game object elements. The background consists of the moon, a black sky, bluish water, and brown undersea hilly terrain. The game object elements include a gray ship, a black submarine, red square torpedoes, and a yellow round depth charge. The only game object element not shown is a circular red explosion.

Although Sea Battle’s graphics are primitive, they have been improved through antialiasing and transparency. I chose to anti-alias the graphic shapes of the moon, depth charge, and submarine to give them a rounded appearance. I also chose to paint the bottom two-thirds of this applet 75% blue and 25% dark gray so that underwater game objects (including the bottom of the ship) have a murky look.

Sea Battle is easy to play. When you start this applet, its title screen tells you to click a mouse button to play the game. During game play, press the left/right arrow keys to move the ship left or right; press the spacebar to fire a depth charge. When the depth charge hits the submarine or a torpedo hits the ship, you see an explosion and the game reverts to the title screen, which presents a win/lose message.

Architecture highlights

Sea Battle is described by a single SeaBattle.java source file—SeaBattle.java and its SeaBattle.html companion HTML file are located in this article’s code archive. This heavily-commented source file (which is more than 900 lines long) organizes the applet’s code into these seven classes:

  • SeaBattle is the main applet class. In addition to an assortment of field variables, this class provides methods for initializing the applet, starting/stopping the game loop, performing the game loop, rendering animation frames, and obtaining randomly selected integers.
  • DepthCharge describes the game object responsible for blowing up the submarine. This class maintains this game object’s location, a bound (how far the depth charge can drop before disappearing), and its rectangular bounding box (for collision detection).
  • Explosion describes an expanding circular explosion. This class maintains the explosion’s location and current circular width, and a flag that differentiates between a ship or submarine explosion (the submarine keeps moving but fires no torpedoes when the ship is hit).
  • Queue describes a queue datastructure for storing java.awt.event.KeyEvent objects. Objects that describe pressed left, right, and spacebar keys are stored (in first-in-first-out order) in the game’s single queue object.
  • Ship describes the game’s protagonist. Along with the ship’s left and right boundaries, position, and bounding box, the class maintains the ship’s velocity. When the ship hits a boundary, its velocity changes.
  • Sub describes the game’s antagonist. It maintains the same information as the ship, but is more flexible. For example, the submarine can move to the left and right of the applet window’s left and right edges. Also, the submarine’s initial velocity is randomly chosen.
  • Torpedo describes the game object responsible for blowing up the ship. As with DepthCharge, this class maintains a torpedo’s location, a bound (how far the torpedo can rise before disappearing), and its rectangular collision-detection bounding box.

The SeaBattle class includes 20 constant and non-constant private field variables that control the game. These variables influence the animation speed, record mouse button clicks and key-presses, buffer the next animation frame, identify the game thread and various game objects, and more.

The complete list of variables appears below:

 // Attempt an animation speed of 50 frames per second.

private final static int FPS = 50;

// Divide 1000 milliseconds by frames-per-second to obtain delay between
// frames.

private final static int DELAY = 1000/FPS;

// The maximum number of torpedoes that can be in motion at any time.

private final static int MAX_TORP = 10;

// There are two game states.

private final static int STATE_PLAY = 0;
private final static int STATE_TITLE = 1;

// Clicking the mouse transitions the game from the title state to the play
// state.

private volatile boolean mouseClicked;

// Applet height and width.

private int height, width;

// The starting game state is always the title state. This state introduces
// the game and tells the user to click the mouse to play.

private int state = STATE_TITLE;

// The y locations of the undersea terrain are stored in the array below.

private int [] hill_tops;

// The ship fires depth charges in hopes of destroying the submarine.

private DepthCharge dc;

// The same explosion object is used for ship and submarine explosions.

private Explosion explosion;

// This graphics context lets us draw into the image buffer.

private Graphics2D grBuffer;

// An image buffer is used to minimize screen flicker.

private Image imBuffer;

// Left arrow/right arrow/spacebar key events are recorded in a queue so
// that none are missed.

private Queue queue = new Queue ();

// You are the ship.

private Ship ship;

// The first line of title text is changed during game play to reflect your
// winning or losing.

private String [] title =
{
   "Welcome to SeaBattle!",
   "Click mouse button to play."
};

// The computer is the submarine.

private Sub sub;

// The game animation is controlled by a runner thread.

private volatile Thread runner;

// The various "in flight" torpedoes are maintained in this array.

private Torpedo [] torp = new Torpedo [MAX_TORP];

You might be curious about my use of the volatile keyword — variables mouseClicked and runner are marked volatile. When a single field variable is shared between threads, at least one thread modifies the variable, and the other threads read the variable’s value; the variable must be, in most cases, marked volatile, or accessed from within a synchronized region.

The event-dispatching and runner-specific threads write to the mouseClicked variable; mouseClicked is read from the runner-specific thread. Similarly, the browser thread writes to runner in public void start() and public void stop(); the runner-specific thread reads this variable in public void run().

The volatile keyword is used to ensure that when one thread writes to a variable, all other threads see this new value when they subsequently read from the variable. This is important in multiprocessor systems, where each thread accesses local memory in addition to shared main memory. A volatile variable’s value is kept in the shared main memory; there are no local copies of its value.

In addition to performing the same task as volatile, the synchronized keyword also takes care of mutual exclusion through lock acquisition and release. Because these tasks add overhead, they are only needed when multiple operations must be performed as a unit; accessing 64-bit longs or doubles on 32-bit JVMs is an example.

Note
I haven’t always declared individual field variables that are written to by one thread and read from by other threads volatile — I haven’t even placed the code that writes to and reads from these variables in a synchronized method or a synchronized code block. Although this hasn’t been a problem on my uniprocessor platforms, not marking these variables volatile could be problematic on both multiprocessor and dual-core processor platforms. Therefore, I think it’s a good idea for all of us to think about consistently using volatile and synchronized (where appropriate) to address the main memory versus local memory issue. For more information on these keywords and the Java memory model, read “Java Specification Request 133 in Public Review.”

Sea Battle requires the hill_tops array of integers field to assist it in generating and rendering the undersea hilly terrain. This array has a length equal to the applet’s width; each entry determines the height of the terrain at a specific horizontal position. The following code excerpt from this applet’s public void init() method populates this array:

 hill_tops = new int [width];
int seed = 5*height/6;
for (int i = 0; i < width; i++)
{
     hill_tops [i] = seed;
     if (rnd (2) == 0)
     {
         if (--seed < 2*height/3)
             seed = 2*height/3;
     }
     else
     {
         if (++seed > height-1)
             seed = height-1;
     }
}

This code fragment creates the terrain near the bottom of the applet’s drawing surface. The leftmost elevation marks the midpoint of the bottom third of the drawing surface; it serves as a starting seed for randomly determining subsequent elevations. Each elevation is randomly chosen to be relative to the previous elevation. The public void paint(Graphics g) excerpt below uses these elevations to render the hills:

 grBuffer.setColor (Color.orange);
for (int i = 0; i < width; i++)
     grBuffer.drawLine (i, hill_tops [i], i, height);

Each hill is rendered as a line that is drawn from the current elevation to the bottom of the image buffer. I chose to color all of these lines orange because paint()‘s use of transparency will ultimately result in a color change to brown. As an exercise, think about how you might modify the terrain-generation and rendering code to make the undersea terrain more interesting.

Size reduction

When you compile SeaBattle.java with the J2SE 1.4 compiler, 10 classfiles with a combined size of 15,096 bytes are generated. If you store these classfiles in a jar file (jar cf sb.jar *.class, for example), this size reduces to 10,562 bytes. Clearly, Sea Battle violates the 4096-byte limit and cannot be entered into the contest without reducing its size.

You can reduce the applet’s size by eliminating all constants. Instead of FPS, DELAY, MAX_TORP, and so on, replace each occurrence of a constant with its value. The code won’t look as nice and will be harder to maintain, but that’s not the point—its size must be reduced. Because eliminating constants only reduces the size by a few hundred bytes, something more radical is necessary.

According to the 4KGamesDesign TWiki, you should limit your classes to a single class because, among other reasons, each class introduces overhead in terms of a constant pool and other data. Because the DepthCharge and Torpedo classes are so similar, eliminating one of these classes by merging them into a single DT class makes sense.

When the constants and one of these classes are eliminated (this article’s code archive includes a SeaBattle2.java source file that implements these changes), the combined size of the classfiles drops to 13,493 bytes. Also, the jar file’s size decreases to 9,459 bytes. If you remove additional classes, the size reduction should become even more pronounced.

March 1 is the deadline for this year’s Java 4K game-programming contest. Hopefully, my Sea Battle game will inspire you to write your own. Remember: The game’s jar file must not exceed 4096 bytes. Best of luck.

Jeff Friesen is a freelance software developer and educator specializing in C, C++, and Java technology.