Java/J2ME/Game

Материал из Java эксперт
Перейти к: навигация, поиск

Checkers game

   <source lang="java">

/* Title: J2ME Games With MIDP2 Authors: Carol Hamer Publisher: Apress ISBN: 1590593820

  • /

import java.util.Vector; import java.io.*; import javax.microedition.io.*; import javax.microedition.rms.*;

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; /**

* This is the main class of the checkers game.
*
* @author Carol Hamer
*/

public class Checkers extends MIDlet implements CommandListener {

 //-----------------------------------------------------
 //    game object fields
 /**
  * The canvas that the checkerboard is drawn on.
  */
 private CheckersCanvas myCanvas;
 /**
  * The class that makes the http connection.
  */
 private Communicator myCommunicator;
 //-----------------------------------------------------
 //    command fields
 /**
  * The button to exit the game.
  */
 private Command myExitCommand = new Command("Exit", Command.EXIT, 99);
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Initialize the canvas and the commands.
  */
 public Checkers() {
   try { 
     //create the canvas and set up the commands:
     myCanvas = new CheckersCanvas(Display.getDisplay(this));
     myCanvas.addCommand(myExitCommand);
     myCanvas.setCommandListener(this);
     CheckersGame game = myCanvas.getGame();
     myCommunicator = new Communicator(this, myCanvas, game);
     game.setCommunicator(myCommunicator);
   } catch(Exception e) {
     // if there"s an error during creation, display it as an alert.
     errorMsg(e);
   }
 }
 //----------------------------------------------------------------
 //  implementation of MIDlet
 // these methods may be called by the application management 
 // software at any time, so we always check fields for null 
 // before calling methods on them.
 /**
  * Start the application.
  */
 public void startApp() throws MIDletStateChangeException {
   // tell the canvas to set up the game data and paint the 
   // checkerboard.
   if(myCanvas != null) {
     myCanvas.start();
   }
   // tell the communicator to start its thread and make a
   // connection.
   if(myCommunicator != null) {
     myCommunicator.start();
   }
 }
 
 /**
  * Throw out the garbage.
  */
 public void destroyApp(boolean unconditional) 
     throws MIDletStateChangeException {
   // tell the communicator to send the end game 
   // message to the other player and then disconnect:
   if(myCommunicator != null) {
     myCommunicator.endGame();
   }
   // throw the larger game objects in the garbage:
   myCommunicator = null;
   myCanvas = null;
   System.gc();
 }
 /**
  * Pause the game.
  * This method merely ends the game because this 
  * version of the Checkers game does not support 
  * re-entering a game that is in play.  A possible 
  * improvement to the game would be to allow 
  * a player to diconeect and leave a game and then 
  * later return to it, using some sort of session
  * token to find the correct game in progress on 
  * the server side.
  */
 public void pauseApp() {
   try {
     destroyApp(false);
     notifyDestroyed();
   } catch (MIDletStateChangeException ex) {
   }
 }
 //----------------------------------------------------------------
 //  implementation of CommandListener
 /*
  * Respond to a command issued on the Canvas.
  */
 public void commandAction(Command c, Displayable s) {
   if(c == myExitCommand) {
     try {
         destroyApp(false);
         notifyDestroyed();
     } catch (MIDletStateChangeException ex) {
     }
   }
 }
 
 //-------------------------------------------------------
 //  error methods
 /**
  * Converts an exception to a message and displays 
  * the message..
  */
 void errorMsg(Exception e) {
   e.printStackTrace();
   if(e.getMessage() == null) {
     errorMsg(e.getClass().getName());
   } else {
     errorMsg(e.getMessage());
   }
 }
 /**
  * Displays an error message alert if something goes wrong.
  */
 void errorMsg(String msg) {
   Alert errorAlert = new Alert("error", 
        msg, null, AlertType.ERROR);
   errorAlert.setCommandListener(this);
   errorAlert.setTimeout(Alert.FOREVER);
   Display.getDisplay(this).setCurrent(errorAlert);
 }

} /**

* This class is the display of the game.
* 
* @author Carol Hamer
*/

class CheckersCanvas extends Canvas {

 //---------------------------------------------------------
 //   static fields
 /**
  * color constant
  */
 public static final int BLACK = 0;
 /**
  * color constant
  */
 public static final int WHITE = 0xffffff;
 /**
  * color constant.
  * (not quite bright red)
  */
 public static final int RED = 0xf96868;
 /**
  * color constant
  */
 public static final int GREY = 0xc6c6c6;
 /**
  * color constant
  */
 public static final int LT_GREY = 0xe5e3e3;
 /**
  * how many rows and columns the display is divided into.
  */
 public static final int GRID_WIDTH = 8;
 //---------------------------------------------------------
 //   instance fields
 /**
  * The black crown to draw on the red pieces..
  */
 private Image myBlackCrown;
 /**
  * The red crown to draw on the black pieces..
  */
 private Image myWhiteCrown;
 /**
  * a handle to the display.
  */
 private Display myDisplay;
 /**
  * a handle to the object that stores the game logic
  * and game data.
  */
 private CheckersGame myGame;
 /**
  * checkers dimension: the width of the squares of the checkerboard.
  */
 private int mySquareSize;
 /**
  * checkers dimension: the minimum width possible for the 
  * checkerboard squares.
  */
 private int myMinSquareSize = 15;
 /**
  * whether or not we"re waiting for another player to join 
  * the game.
  */
 private boolean myIsWaiting;
 //-----------------------------------------------------
 //    gets / sets
 /**
  * @return a handle to the class that holds the logic of the 
  * checkers game.
  */
 CheckersGame getGame() {
   return(myGame);
 }
 /**
  * Display a screen to inform the player that we"re 
  * waiting for another player.
  */
 void setWaitScreen(boolean wait) {
   myIsWaiting = wait;
 }
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Constructor performs size calculations.
  * @throws Exception if the display size is too 
  *         small to make a checkers.
  */
 CheckersCanvas(Display d) throws Exception {
   myDisplay = d;
   myGame = new CheckersGame();
   // a few calculations to make the right checkerboard 
   // for the current display.
   int width = getWidth();
   int height = getHeight();
   // get the smaller dimension fo the two possible 
   // screen dimensions in order to determine how 
   // big to make the checkerboard.
   int screenSquareWidth = height;
   if(width < height) {
     screenSquareWidth = width;
   }
   mySquareSize = screenSquareWidth / GRID_WIDTH;
   // if the display is too small to make a reasonable checkerboard, 
   // then we throw an Exception
   if(mySquareSize < myMinSquareSize) {
     throw(new Exception("Display too small"));
   }
   // initialize the crown images:
   myBlackCrown = Image.createImage("/blackCrown.png");
   myWhiteCrown = Image.createImage("/whiteCrown.png");
 }
 /**
  * This is called as soon as the application begins.
  */
 void start() {
   myDisplay.setCurrent(this);
   // prepare the game data for the first move:
   myGame.start();
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * Repaint the checkerboard..
  */
 protected void paint(Graphics g) {
   int width = getWidth();
   int height = getHeight();
   g.setColor(WHITE);
   // clear the board (including the region around
   // the board, which can get menu stuff and other 
   // garbage painted onto it...)
   g.fillRect(0, 0, width, height);
   // If we need to wait for another player to join the 
   // game before we can start, this displays the appropriate
   // message:
   if(myIsWaiting) {
     // perform some calculations to place the text correctly:
     Font font = g.getFont();
     int fontHeight = font.getHeight();
     int fontWidth = font.stringWidth("waiting for another player");
     g.setColor(WHITE);
     g.fillRect((width - fontWidth)/2, (height - fontHeight)/2,
          fontWidth + 2, fontHeight);
     // write in black
     g.setColor(BLACK);
     g.setFont(font);
     g.drawString("waiting for another player", (width - fontWidth)/2, 
      (height - fontHeight)/2,
      g.TOP|g.LEFT);
     return;
   }
   // now draw the checkerboard:
   // first the dark squares:
   byte offset = 0;
   for(byte i = 0; i < 4; i++) {
     for(byte j = 0; j < 8; j++) {
 // the offset is used to handle the fact that in every 
 // other row the dark squares are shifted one place 
 // to the right.
 if(j % 2 != 0) {
   offset = 1;
 } else {
   offset = 0;
 }
 // now if this is a selected square, we draw it lighter:
 if(myGame.isSelected(i, j)) {
   g.setColor(LT_GREY);
   g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, 
          mySquareSize, mySquareSize);
 } else {
   // if it"s not selected, we draw it dark grey:
   g.setColor(GREY);
   g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, 
        mySquareSize, mySquareSize);
 }
 // now put the pieces in their places:
 g.setColor(RED);
 int piece = myGame.getPiece(i, j);
 int circleOffset = 2;
 int circleSize = mySquareSize - 2*circleOffset;
 if(piece < 0) {
   // color the piece in black
   g.setColor(BLACK);
   g.fillRoundRect((2*i + offset)*mySquareSize + circleOffset, 
       j*mySquareSize + circleOffset, 
      circleSize, circleSize, circleSize, circleSize);
   // if the player is a king, draw a crown on:
   if(piece < -1) {
     g.drawImage(myWhiteCrown, 
         (2*i + offset)*mySquareSize + mySquareSize/2, 
         j*mySquareSize + 1 + mySquareSize/2, 
         Graphics.VCENTER|Graphics.HCENTER);
   }
 } else if(piece > 0) {
   // color the piece in red
   g.fillRoundRect((2*i + offset)*mySquareSize + circleOffset, 
       j*mySquareSize + circleOffset, 
      circleSize, circleSize, circleSize, circleSize);
   // if the player is a king, draw a crown on:
   if(piece > 1) {
     g.drawImage(myBlackCrown, 
         (2*i + offset)*mySquareSize + mySquareSize/2, 
         j*mySquareSize + 1 + mySquareSize/2, 
         Graphics.VCENTER|Graphics.HCENTER);
   }
 }
     }
   }
   // now the blank squares:
   // actually, this part is probably not necessary...
   g.setColor(WHITE);
   for(int i = 0; i < 4; i++) {
     for(int j = 0; j < 8; j++) {
 if(j % 2 == 0) {
   offset = 1;
 } else {
   offset = 0;
 }
 g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, 
      mySquareSize, mySquareSize);
     }
   }
   // if the player has reached the end of the game, 
   // we display the end message.
   if(myGame.getGameOver()) {
     // perform some calculations to place the text correctly:
     Font font = g.getFont();
     int fontHeight = font.getHeight();
     int fontWidth = font.stringWidth("Game Over");
     g.setColor(WHITE);
     g.fillRect((width - fontWidth)/2, (height - fontHeight)/2,
          fontWidth + 2, fontHeight);
     // write in black
     g.setColor(BLACK);
     g.setFont(font);
     g.drawString("Game Over", (width - fontWidth)/2, 
      (height - fontHeight)/2,
      g.TOP|g.LEFT);
   }
 }
 //-------------------------------------------------------
 //  handle keystrokes
 /**
  * Move the player.
  */
 public void keyPressed(int keyCode) {  
   if(myGame.isMyTurn()) {
     int action = getGameAction(keyCode);   
     switch (action) {
     case LEFT:
 myGame.leftPressed();
 break;
     case RIGHT:
 myGame.rightPressed();
 break;
     case UP:
 myGame.upPressed();
 break;
     case DOWN:
 myGame.deselect();
 break;
     }
     repaint();
     serviceRepaints();
   }
 }

} /**

* This class contacts a remote server in order to 
* play a game of checkers against an opponent..
*
* @author Carol Hamer
*/

class Communicator extends Thread {

 //--------------------------------------------------------
 //  static fields
 /**
  * This is the URL to contact.
  * IMPORTANT: before compiling, the following URL
  * must be changed to the correct URL of the 
  * machine running the server code.
  */
 public static final String SERVER_URL 
   = "socket://malbec:8007";
 /**
  * The int to signal that the game is to begin.
  */
 public static final byte START_GAME_FLAG = -4;
 /**
  * The byte to signal that the game is to end.
  */
 public static final byte END_GAME_FLAG = -3;
 /**
  * The byte to signal the end of a turn.
  */
 public static final byte END_TURN_FLAG = -2;
 //--------------------------------------------------------
 //  game instance fields
 /**
  * The MIDlet subclass, used to set the Display 
  * in the case where an error message needs to be sent..
  */
 private Checkers myCheckers;
 /**
  * The Canvas subclass, used to set the Display 
  * in the case where an error message needs to be sent..
  */
 private CheckersCanvas myCanvas;
 /**
  * The game logic class that we send the opponent"s 
  * moves to..
  */
 private CheckersGame myGame;
 /**
  * Whether or not the MIDlet class has requested the 
  * game to end.
  */
 private boolean myShouldStop;
 //--------------------------------------------------------
 //  data exchange instance fields
 /**
  * The data from the local player that is to 
  * be sent to the opponent.
  */
 private byte[] myMove;
 /**
  * Whether or not the current turn is done and 
  * should be sent.
  */
 private boolean myTurnIsDone = true;
 //--------------------------------------------------------
 //  initialization
 /**
  * Constructor is used only when the program wants 
  * to spawn a data-fetching thread, not for merely 
  * reading local data with static methods.
  */
 Communicator(Checkers checkers, CheckersCanvas canvas, 
        CheckersGame game) {
   myCheckers = checkers;
   myCanvas = canvas;
   myGame = game;
 }
 //--------------------------------------------------------
 //  methods called by CheckersGame to send move
 //    information to the opponent.
 /**
  * Stop the game entirely.  Notify the servlet that 
  * the user is exiting the game.
  */
 synchronized void endGame() {
   myShouldStop = true;
   if(myGame != null) {
     myGame.setGameOver();
   }
   notify();
 }
 /**
  * This is called when the player moves a piece.
  */
 synchronized void move(byte sourceX, byte sourceY, byte destinationX, 
       byte destinationY) {
   myMove = new byte[4];
   myMove[0] = sourceX;
   myMove[1] = sourceY;
   myMove[2] = destinationX;
   myMove[3] = destinationY;
   myTurnIsDone = false;
   notify();
 }
 /**
  * This is called when the local player"s turn is over.
  */
 synchronized void endTurn() {
   myTurnIsDone = true;
   notify();
 }
 //--------------------------------------------------------
 //  main communication method
 /**
  * Makes a connection to the server and sends and receives
  * information about moves.
  */
 public void run() {
   DataInputStream dis = null;
   DataOutputStream dos = null;
   SocketConnection conn = null;
   byte[] fourBytes = new byte[4];
   try {
     // tell the user that we"re waiting for the other player to join:
     myCanvas.setWaitScreen(true);
     myCanvas.repaint();
     myCanvas.serviceRepaints();
     // now make the connection:
     conn = (SocketConnection)Connector.open(SERVER_URL);
     conn.setSocketOption(SocketConnection.KEEPALIVE, 1);
     dos = conn.openDataOutputStream();
     dis = conn.openDataInputStream();
     // we read four bytes to make sure the connection works...
     dis.readFully(fourBytes);
     if(fourBytes[0] != START_GAME_FLAG) {
 throw(new Exception("server-side error"));
     }
     // On this line it will block waiting for another 
     // player to join the game or make a move:
     dis.readFully(fourBytes);
     // if the server sends the start game flag again, 
     // that means that we start with the local player"s turn.
     // Otherwise, we read the other player"s first move from the 
     // stream:
     if(fourBytes[0] != START_GAME_FLAG) {
 // verify that the other player sent a move 
 // and not just a message ending the game...
 if(fourBytes[0] == END_GAME_FLAG) {
   throw(new Exception("other player quit"));
 }
 // we move the opponent on the local screen.
 // then we read from the opponent again, 
 // in case there"s a double-jump:
 while(fourBytes[0] != END_TURN_FLAG) {
   myGame.moveOpponent(fourBytes);
   dis.readFully(fourBytes);
 }
     }
     // now signal the local game that the opponent is done
     // so the board must be updated and the local player 
     // prompted to make a move:
     myGame.endOpponentTurn();
     myCanvas.setWaitScreen(false);
     myCanvas.repaint();
     myCanvas.serviceRepaints();
     // begin main game loop:
     while(! myShouldStop) {
 // now it"s the local player"s turn.
 // wait for the player to move a piece:
 synchronized(this) {
   wait();
 }
 // after every wait, we check if the game 
 // ended while we were waiting...
 if(myShouldStop) {
   break;
 }
 while(! myTurnIsDone) {
   // send the current move:
   if(myMove != null) {
     dos.write(myMove, 0, myMove.length);
     myMove = null;
   }
   // If the player can continue the move with a double 
   // jump, we wait for the player to do it:
   synchronized(this) {
     // make sure the turn isn"t done before we start waiting
     // (the end turn notify might accidentally be called 
     // before we start waiting...)
     if(! myTurnIsDone) {
       wait();
     } 
   }
 }
 // after every wait, we check if the game 
 // ended while we were waiting...
 if(myShouldStop) {
   break;
 }
 // now we tell the other player the this player"s 
 // turn is over:
 fourBytes[0] = END_TURN_FLAG;
 dos.write(fourBytes, 0, fourBytes.length);
 // now that we"ve sent the move, we wait for a response:
 dis.readFully(fourBytes);
 while((fourBytes[0] != END_TURN_FLAG) && 
       (fourBytes[0] != END_GAME_FLAG) && (!myShouldStop)) {
   // we move the opponent on the local screen.
   // then we read from the opponent again, 
   // in case there"s a double-jump:
   myGame.moveOpponent(fourBytes);
   dis.readFully(fourBytes);
 }
 // if the other player has left the game, we tell the 
 // local user that the game is over.
 if((fourBytes[0] == END_GAME_FLAG) || (myShouldStop)) {
   endGame();
   break;
 }
 myGame.endOpponentTurn();
 myCanvas.repaint();
 myCanvas.serviceRepaints();
     } // end while loop
   } catch(Exception e) {
     // if there"s an error, we display its messsage and 
     // end the game.
     myCheckers.errorMsg(e.getMessage());
   } finally {
     // now we send the information that we"re leaving the game,
     // then close up and delete everything.
     try {
 if(dos != null) {
   dos.write(END_GAME_FLAG);
   dos.close();
 }
 if(dis != null) {
   dis.close();
 }
 if(conn != null) {
   conn.close();
 }
 dis = null;
 dos = null;
 conn = null;
     } catch(Exception e) {
 // if this throws, at least we made our best effort 
 // to close everything up....
     }
   }
   // one last paint job to display the "Game Over"
   myCanvas.repaint();
   myCanvas.serviceRepaints();
 }
   

}

/**

* This class is a set of simple utility functions that 
* can be used to convert standard data types to bytes 
* and back again.  It is used especially for data storage, 
* but also for sending and receiving data.
* 
* @author Carol Hamer
*/

class DataConverter {

 //--------------------------------------------------------
 //  utilities to encode small, compactly-stored small ints.
 /**
  * Encodes a coordinate pair into a byte.
  * @param coordPair a pair of integers to be compacted into
  * a single byte for storage.
  * WARNING: each of the two values MUST BE 
  * between 0 and 15 (inclusive).  This method does not 
  * verify the length of the array (which must be 2!) 
  * nor does it verify that the ints are of the right size.
  */
 public static byte encodeCoords(int[] coordPair) {
   // get the byte value of the first coordinate:
   byte retVal = (new Integer(coordPair[0])).byteValue();
   // move the first coordinate"s value up to the top 
   // half of the storage byte:
   retVal = (new Integer(retVal << 4)).byteValue();
   // store the second coordinate in the lower half
   // of the byte:
   retVal += (new Integer(coordPair[1])).byteValue();
   return(retVal);
 }
 /**
  * Encodes eight ints into a byte.
  * This could be easily modified to encode eight booleans.
  * @param eight an array of at least eight ints.
  * WARNING: all values must be 0 or 1!  This method does 
  * not verify that the values are in the correct range 
  * nor does it verify that the array is long enough.
  * @param offset the index in the array eight to start
  * reading data from.  (should usually be 0)
  */
 public static byte encode8(int[] eight, int offset) {
   // get the byte value of the first int:
   byte retVal = (new Integer(eight[offset])).byteValue();
   // progressively move the data up one bit in the 
   // storage byte and then record the next int in
   // the lowest spot in the storage byte:
   for(int i = offset + 1; i < 8 + offset; i++) {
     retVal = (new Integer(retVal << 1)).byteValue();
     retVal += (new Integer(eight[i])).byteValue();
   }
   return(retVal);
 }
 //--------------------------------------------------------
 //  utilities to decode small, compactly-stored small ints.
 /**
  * Turns a byte into a pair of coordinates.
  */
 public static int[] decodeCoords(byte coordByte) {
   int[] retArray = new int[2];
   // we perform a bitwise and with the value 15 
   // in order to just get the bits of the lower
   // half of the byte:
   retArray[1] = coordByte & 15;
   // To get the bits of the upper half of the 
   // byte, we perform a shift to move them down:
   retArray[0] = coordByte >> 4;
   // bytes in Java are generally assumed to be 
   // signed, but in this coding algorithm we 
   // would like to treat them as unsigned: 
   if(retArray[0] < 0) {
     retArray[0] += 16;
   }
   return(retArray);
 }
 /**
  * Turns a byte into eight ints.
  */
 public static int[] decode8(byte data) {
   int[] retArray = new int[8];
   // The flag allows us to look at each bit individually
   // to determine if it is 1 or 0.  The number 128 
   // corresponds to the highest bit of a byte, so we 
   // start with that one.
   int flag = 128;
   // We use a loop that checks 
   // the data bit by bit by performing a bitwise 
   // and (&) between the data byte and a flag:
   for(int i = 0; i < 8; i++) {
     if((flag & data) != 0) {
 retArray[i] = 1;
     } else {
 retArray[i] = 0;
     }
     // move the flag down one bit so that we can 
     // check the next bit of data on the next pass
     // through the loop:
     flag = flag >> 1;
   }
   return(retArray);
 }
 //--------------------------------------------------------
 //  standard integer interpretation
 /**
  * Uses an input stream to convert an array of bytes to an int.
  */
 public static int parseInt(byte[] data) throws IOException {
   DataInputStream stream 
     = new DataInputStream(new ByteArrayInputStream(data));
   int retVal = stream.readInt();
   stream.close();
   return(retVal);
 }
 /**
  * Uses an output stream to convert an int to four bytes.
  */
 public static byte[] intToFourBytes(int i) throws IOException {
   ByteArrayOutputStream baos = new ByteArrayOutputStream(4);
   DataOutputStream dos = new DataOutputStream(baos);
   dos.writeInt(i);
   baos.close();
   dos.close();
   byte[] retArray = baos.toByteArray();
   return(retArray);
 }
 //--------------------------------------------------------
 //  integer interpretation illustrated
 /**
  * Java appears to treat a byte as being signed when
  * returning it as an int--this function converts from
  * the signed value to the corresponding unsigned value.
  * This method is used by nostreamParseInt.
  */
 public static int unsign(int signed) {
   int retVal = signed;
   if(retVal < 0) {
     retVal += 256;
   }
   return(retVal);
 }
 /**
  * Takes an array of bytes and returns an int.
  * This version will return the same value as the 
  * method parseInt above.  This version is included 
  * in order to illustrate how Java encodes int values
  * in terms of bytes.
  * @param data an array of 1, 2, or 4 bytes.
  */
 public static int nostreamParseInt(byte[] data) {
   // byte 0 is the high byte which is assumed 
   // to be signed.  As we add the lower bytes 
   // one by one, we unsign them because because 
   // a single byte alone is interpreted as signed, 
   // but in an int only the top byte should be signed.
   // (note that the high byte is the first one in the array)
   int retVal = data[0];
   for(int i = 1; i < data.length; i++) {
     retVal = retVal << 8;
     retVal += unsign(data[i]);
   }
   return(retVal);
 }
 /**
  * Takes an arbitrary int and returns
  * an array of four bytes.
  * This version will return the same byte array 
  * as the method intToFourBytes above.  This version 
  * is included in order to illustrate how Java encodes 
  * int values in terms of bytes.
  */
 public static byte[] nostreamIntToFourBytes(int i) {
   byte[] fourBytes = new byte[4];
   // when you take the byte value of an int, it
   // only gives you the lowest byte.  So we 
   // get all four bytes by taking the lowest 
   // byte four times and moving the whole int 
   // down by one byte between each one.
   // (note that the high byte is the first one in the array)
   fourBytes[3] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[2] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[1] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[0] = (new Integer(i)).byteValue();
   return(fourBytes);
 }
 /**
  * Takes an int between -32768 and 32767 and returns
  * an array of two bytes.  This does not verify that 
  * the argument is of the right size.  If the absolute
  * value of i is too high, it will not be encoded 
  * correctly.
  */
 public static byte[] nostreamIntToTwoBytes(int i) {
   byte[] twoBytes = new byte[2];
   // when you take the byte value of an int, it
   // only gives you the lowest byte.  So we 
   // get the lower two bytes by taking the lowest 
   // byte twice and moving the whole int 
   // down by one byte between each one.
   twoBytes[1] = (new Integer(i)).byteValue();
   i = i >> 8;
   twoBytes[0] = (new Integer(i)).byteValue();
   return(twoBytes);
 }

}

/**

* This class takes care of the underlying logic and data of 
* the checkers game being played.  That includes where 
* all of the pieces are on the board and where it is okay 
* for them to move to.  
*
* @author Carol Hamer
*/

class CheckersGame {

 //-------------------------------------------------------
 //   static fields
 /**
  * The length of the checkerboard in the x-direction.
  */
 public static final byte X_LENGTH = 4;
 /**
  * The length of the checkerboard in the y-direction.
  */
 public static final byte Y_LENGTH = 8;
 //-------------------------------------------------------
 //   instance fields
 /**
  * a handle to the communications class that exchanges
  * data with the server.
  */
 private Communicator myCommunicator;
 /**
  * This array represents the black squares of the 
  * checkerboard.  The two dimensions of the array 
  * represent the two dimensions of the checkerboard.
  * The value represents what type of piece is on 
  * the square.
  * 0 = empty
  * 1 = local player"s piece
  * 2 = local player"s king
  * -1 = remote player"s piece
  * -2 = remote player"s king
  */
 private byte[][] myGrid;
 /**
  * If the user has currently selected a piece to move, 
  * this is its X grid coordinate. (-1 if none selected)
  */
 private byte mySelectedX = -1;
 /**
  * If the user has currently selected a piece to move, 
  * this is its Y grid coordinate.(-1 if none selected)
  */
 private byte mySelectedY = -1;
 /**
  * If the user has currently selected a possible 
  * destination square for a move, this is its X coordinate..
  * (-1 if none selected)
  */
 private byte myDestinationX = -1;
 /**
  * If the user has currently selected a possible 
  * destination square for a move, this is its Y coordinate..
  * (-1 if none selected)
  */
 private byte myDestinationY = -1;
 /**
  * This Vector contains the coordinates of all of the 
  * squares that the player could currently move to.
  */
 private Vector myPossibleMoves = new Vector(4);
 /**
  * Whether or not the currently displayed checkers has 
  * been completed.
  */
 private boolean myGameOver = false;
 /**
  * Whether or not it is currently this player"s turn.
  */
 private boolean myTurn = false;
 /**
  * This is true if the player has just jumped and can 
  * jump again.
  */
 private boolean myIsJumping = false;
 //-------------------------------------------------------
 //   get/set data
 
 /**
  * get the piece on the given grid square.
  */
 byte getPiece(byte x, byte y) {
   return(myGrid[x][y]);
 }
 /**
  * This is callsed by CheckersCanvas to determine if 
  * the square is currently selected (as containing 
  * a piece to move or a destination square).
  */
 boolean isSelected(byte x, byte y) {
   boolean retVal = false;
   if((x == mySelectedX) && (y == mySelectedY)) {
     retVal = true;
   } else if((x == myDestinationX) && (y == myDestinationY)) {
     retVal = true;
   }
   return(retVal);
 }
 /**
  * This tells whether or not the keystrokes should currently
  * be taken into account.
  */
 boolean isMyTurn() {
   boolean retVal = false;
   if((!myGameOver) && ((myTurn) || (myIsJumping))) {
     retVal = true;
   }
   return(retVal);
 }
 /**
  * This tells whether or not the game has ended.
  */
 boolean getGameOver() {
   boolean retVal = false;
   if(myGameOver) {
     retVal = true;
   }
   return(retVal);
 }
 /**
  * tell the CheckersGame that the other player has ended the game.
  */
 void setGameOver() {
   myGameOver = true;
 }
 /**
  * set the communicator object.
  */
 void setCommunicator(Communicator comm) {
   myCommunicator = comm;
 }
 //-------------------------------------------------------
 //   initialization
 /**
  * Constructor puts the pieces in their initial positions:
  */
 CheckersGame() {
   myGrid = new byte[X_LENGTH][];
   for(byte i = 0; i < myGrid.length; i++) {
     myGrid[i] = new byte[Y_LENGTH];
     for(byte j = 0; j < myGrid[i].length; j++) {
 if(j < 3) {
   // fill the top of the board with remote players
   myGrid[i][j] = -1;
 } else if(j > 4) {
   // fill the bottom of the board with local players
   myGrid[i][j] = 1;
 }
     }
   }
 }
 /**
  * This is called just before the player makes the 
  * first move.
  */
 void start() {
   mySelectedX = 0;
   mySelectedY = 5;
   myTurn = true;
   getMoves(mySelectedX, mySelectedY, myPossibleMoves, false);
 }
 //-------------------------------------------------------
 //   move the opponent
 // to be called by Communicator
 /**
  * This is called when the opponent wants to move
  * its piece.
  * @param moveData an array of four bytes:
  * moveData[0] = opponent"s initial X coordinate
  * moveData[1] = opponent"s initial Y coordinate
  * moveData[2] = opponent"s destination X coordinate
  * moveData[3] = opponent"s destination Y coordinate
  */
 void moveOpponent(byte[] moveData) {
   // since both players appear on their own screens 
   // as the red side (bottom of the screen), we need 
   // to invert the opponent"s move:
   moveData[0] = (new Integer(X_LENGTH - moveData[0] - 1)).byteValue();
   moveData[2] = (new Integer(X_LENGTH - moveData[2] - 1)).byteValue();
   moveData[1] = (new Integer(Y_LENGTH - moveData[1] - 1)).byteValue();
   moveData[3] = (new Integer(Y_LENGTH - moveData[3] - 1)).byteValue();
   myGrid[moveData[2]][moveData[3]] 
     = myGrid[moveData[0]][moveData[1]];
   myGrid[moveData[0]][moveData[1]] = 0;
   // deal with an opponent"s jump:
   if((moveData[1] - moveData[3] > 1) || 
      (moveData[3] - moveData[1] > 1)) {
     int jumpedY = (moveData[1] + moveData[3])/2;
     int jumpedX = moveData[0];
     int parity = moveData[1] % 2;
     if((parity > 0) && (moveData[2] > moveData[0])) {
 jumpedX++;
     } else if((parity == 0) && (moveData[0] > moveData[2])) {
 jumpedX--;
     }
     myGrid[jumpedX][jumpedY] = 0;
   }
   // if the opponent reaches the far side, 
   // make him a king:
   if(moveData[3] == Y_LENGTH - 1) {
     myGrid[moveData[2]][moveData[3]] = -2;
   }
 }
 /**
  * This is called when the opponent"s turn is over.
  * Note that the turn doesn"t automatically end after 
  * the opponent moves because the opponent may make 
  * a double or triple jump.
  */
 void endOpponentTurn() {
   myTurn = true;
   // Now begin the local player"s turn: 
   // First select the first local piece that can be 
   // moved. (rightPressed will select an appropriate 
   // piece or end the game if the local player has 
   // no possible moves to make)
   mySelectedX = 0;
   mySelectedY = 0;
   myDestinationX = -1;
   myDestinationY = -1;
   rightPressed();
   // the local player"s thread has been waiting 
   // for the opponent"s turn to end.  
   synchronized(this) {
     notify();
   }
 }
 //-------------------------------------------------------
 //   handle keystrokes
 // to be called by CheckersCanvas
 /**
  * if the left button is pressed, this method takes 
  * the correct course of action depending on the situation.
  */
 void leftPressed() {
   // in the first case the user has not yet selected a 
   // piece to move:
   if(myDestinationX == -1) {
     // find the next possible piece (to the left) 
     // that can move:
     selectPrevious();
     // if selectPrevious fails to fill myPossibleMoves, that 
     // means that the local player cannot move, so the game
     // is over:
     if(myPossibleMoves.size() == 0) {
 myCommunicator.endGame();
     }
   } else {
     // if the user has already selected a piece to move, 
     // we give the options of where the piece can move to:
     for(byte i = 0; i < myPossibleMoves.size(); i++) {
 byte[] coordinates = (byte[])myPossibleMoves.elementAt(i);
 if((coordinates[0] == myDestinationX) && 
    (coordinates[1] == myDestinationY)) {
   i++;
   i = (new Integer(i % myPossibleMoves.size())).byteValue();
   coordinates = (byte[])myPossibleMoves.elementAt(i);
   myDestinationX = coordinates[0];
   myDestinationY = coordinates[1];
   break;
 }
     }
   }
 }
 /**
  * if the left button is pressed, this method takes 
  * the correct course of action depending on the situation.
  */
 void rightPressed() {
   // in the first case the user has not yet selected a 
   // piece to move:
   if(myDestinationX == -1) {
     // find the next possible piece that can 
     // move:
     selectNext();
     // if selectNext fails to fill myPossibleMoves, that 
     // means that the local player cannot move, so the game
     // is over:
     if(myPossibleMoves.size() == 0) {
 myCommunicator.endGame();
     }
   } else {
     // if the user has already selected a piece to move, 
     // we give the options of where the piece can move to:
     for(byte i = 0; i < myPossibleMoves.size(); i++) {
 byte[] coordinates = (byte[])myPossibleMoves.elementAt(i);
 if((coordinates[0] == myDestinationX) && 
    (coordinates[1] == myDestinationY)) {
   i++;
   i = (new Integer(i % myPossibleMoves.size())).byteValue();
   coordinates = (byte[])myPossibleMoves.elementAt(i);
   myDestinationX = coordinates[0];
   myDestinationY = coordinates[1];
   break;
 }
     }
   }
 }
 /**
  * If no piece is selected, we select one.  If a piece 
  * is selected, we move it.
  */
 void upPressed() {
   // in the first case the user has not yet selected a 
   // piece to move:
   if(myDestinationX == -1) {
     fixSelection();
   } else {
     // if the source square and destination square 
     // have been chosen, we move the piece:
     move();
   }
 }
 /**
  * If the user decided not to move the selected piece 
  * (and instead wants to select again), this undoes 
  * the selection. This corresponds to pressing the 
  * DOWN key.
  */
 void deselect() {
   // if the player has just completed a jump and 
   // could possibly jump again but decides not to 
   // (i.e. deselects), then the turn ends:
   if(myIsJumping) {
     mySelectedX = -1;
     mySelectedY = -1;
     myDestinationX = -1;
     myDestinationY = -1;
     myIsJumping = false;
     myTurn = false;
     myCommunicator.endTurn();
   } else {
     // setting the destination coordinates to -1 
     // is the signal that the the choice of which 
     // piece to move can be modified:
     myDestinationX = -1;
     myDestinationY = -1;
   }
 }
 //-------------------------------------------------------
 //   internal square selection methods
 /**
  * When the player has decided that the currently selected
  * square contains the piece he really wants to move, this 
  * is called. This method switches to the mode where 
  * the player selects the destination square of the move.
  */
 private void fixSelection() {
   byte[] destination = (byte[])myPossibleMoves.elementAt(0);
   // setting the destination coordinates to valid 
   // coordinates is the signal that the user is done 
   // selecting the piece to move and now is choosing 
   // the destination square:
   myDestinationX = destination[0];
   myDestinationY = destination[1];
 }
 /**
  * This method starts from the currently selected square 
  * and finds the next square that contains a piece that 
  * the player can move.
  */
 private void selectNext() {
   // Test the squares one by one (starting from the 
   // currently selected square) until we find a square 
   // that contains one of the local player"s pieces 
   // that can move:
   byte testX = mySelectedX;
   byte testY = mySelectedY;
   while(true) {
     testX++;
     if(testX >= X_LENGTH) {
 testX = 0;
 testY++;
 testY = (new Integer(testY % Y_LENGTH)).byteValue();
     }
     getMoves(testX, testY, myPossibleMoves, false);
     if((myPossibleMoves.size() != 0) || 
    ((testX == mySelectedX) && (testY == mySelectedY))) {
 mySelectedX = testX;
 mySelectedY = testY;
 break;
     }
   }
 }
 /**
  * This method starts from the currently selected square 
  * and finds the next square (to the left) that contains 
  * a piece that the player can move.
  */
 private void selectPrevious() {
   // Test the squares one by one (starting from the 
   // currently selected square) until we find a square 
   // that contains one of the local player"s pieces 
   // that can move:
   byte testX = mySelectedX;
   byte testY = mySelectedY;
   while(true) {
     testX--;
     if(testX < 0) {
 testX += X_LENGTH;
 testY--;
 if(testY < 0) {
   testY += Y_LENGTH;
 }
     }
     getMoves(testX, testY, myPossibleMoves, false);
     if((myPossibleMoves.size() != 0) || 
  ((testX == mySelectedX) && (testY == mySelectedY))) {
 mySelectedX = testX;
 mySelectedY = testY;
 break;
     }
   }
 }
 //-------------------------------------------------------
 //   internal utilities
 /**
  * Once the user has selected the move to make, this 
  * updates the data accordingly.
  */
 private void move() {
   // the piece that was on the source square is 
   // now on the destination square:
   myGrid[myDestinationX][myDestinationY] 
     = myGrid[mySelectedX][mySelectedY];
   // the source square is emptied:
   myGrid[mySelectedX][mySelectedY] = 0;
   if(myDestinationY == 0) {
     myGrid[myDestinationX][myDestinationY] = 2;
   }
   // tell the communicator to inform the other player 
   // of this move:
   myCommunicator.move(mySelectedX, mySelectedY, 
     myDestinationX, myDestinationY);
   // deal with the special rules for jumps::
   if((mySelectedY - myDestinationY > 1) || 
      (myDestinationY - mySelectedY > 1)) {
     int jumpedY = (mySelectedY + myDestinationY)/2;
     int jumpedX = mySelectedX;
     int parity = mySelectedY % 2;
     // the coordinates of the jumped square depend on 
     // what row we"re in:
     if((parity > 0) && (myDestinationX > mySelectedX)) {
         jumpedX++;
     } else if((parity == 0) && (mySelectedX > myDestinationX)) {
         jumpedX--;
     }
     // remove the piece that was jumped over:
     myGrid[jumpedX][jumpedY] = 0;
     // now get ready to jump again if possible:
     mySelectedX = myDestinationX;
     mySelectedY = myDestinationY;
     myDestinationX = -1;
     myDestinationY = -1;
     // see if another jump is possible.
     // The "true" argument tells the program to return 
     // only jumps because the player can go again ONLY 
     // if there"s a jump:
     getMoves(mySelectedX, mySelectedY, myPossibleMoves, true);
     // if there"s another jump possible with the same piece, 
     // allow the player to continue jumping:
     if(myPossibleMoves.size() != 0) {
 myIsJumping = true;
 byte[] landing = (byte[])myPossibleMoves.elementAt(0);
 myDestinationX = landing[0];
 myDestinationY = landing[1];
     } else {
 myTurn = false;
 myCommunicator.endTurn();
     }
   } else {
     // since it"s not a jump, we just end the turn 
     // by deselecting everything.
     mySelectedX = -1;
     mySelectedY = -1;
     myDestinationX = -1;
     myDestinationY = -1;
     myPossibleMoves.removeAllElements();
     myTurn = false;
     // tell the other player we"re done:
     myCommunicator.endTurn();
   }
 }
 
 /**
  * Given a square on the grid, get the coordinates 
  * of one of the adjoining (diagonal) squares.
  * 0 = top left
  * 1 = top right
  * 2 = bottom left
  * 3 = bottom right.
  * @return the coordinates or null if the desired corner 
  * is off the board.
  */
 private byte[] getCornerCoordinates(byte x, byte y, byte corner) {
   byte[] retArray = null;
   if(corner < 2) {
     y--;
   } else {
     y++;
   }
   // Where the corner is on the grid depends on 
   // whether this is an odd row or an even row:
   if((corner % 2 == 0) && (y % 2 != 0)) {
     x--;
   } else if((corner % 2 != 0) && (y % 2 == 0)) {
     x++;
   }
   try {
     if(myGrid[x][y] > -15) {
 // we don"t really care about the value, this
 // if statement is just there to get it to 
 // throw if the coordinates aren"t on the board.
 retArray = new byte[2];
 retArray[0] = x;
 retArray[1] = y;
     }
   } catch(ArrayIndexOutOfBoundsException e) {
     // this throws if the coordinates do not correspond 
     // to a square on the board. It"s not a problem, 
     // so we do nothing--we just return null instead 
     // of returning coordinates since no valid 
     // coordinates correspond to the desired corner.
   }
   return(retArray);
 }
 
 /**
  * Determines where the piece in the given 
  * grid location can move.  Clears the Vector
  * and fills it with the locations that 
  * the piece can move to.
  * @param jumpsOnly if we should return only moves that 
  *        are jumps.
  */
 private void getMoves(byte x, byte y, Vector toFill, boolean jumpsOnly) {
   toFill.removeAllElements();
   // if the square does not contain one of the local player"s 
   // pieces, then there are no corresponding moves and we just
   // return an empty vector.
   if(myGrid[x][y] <= 0) {
     return;
   }
   // check each of the four corners to see if the 
   // piece can move there:
   for(byte i = 0; i < 4; i++) {
     byte[] coordinates = getCornerCoordinates(x, y, i);
     // if the coordinate array is null, then the corresponding 
     // corner is off the board and we don"t deal with it.
     // The later two conditions in the following if statement
     // ensure that either the move is a forward move or the 
     // current piece is a king:
     if((coordinates != null) &&
  ((myGrid[x][y] > 1) || (i < 2))) {
 // if the corner is empty (and we"re not looking 
 // for just jumps), then this is a possible move
 // so we add it to the vector of moves:
 if((myGrid[coordinates[0]][coordinates[1]] == 0) && (! jumpsOnly)) {
   toFill.addElement(coordinates);
   // if the space is occupied by an opponent, see if we can jump it:
 } else if(myGrid[coordinates[0]][coordinates[1]] < 0) {
   byte[] jumpLanding = getCornerCoordinates(coordinates[0], 
            coordinates[1], i);
   // if the space on the far side of the opponent"s piece
   // is on the board and is unoccupied, then a jump 
   // is possible, so we add it to the vector of moves:
   if((jumpLanding != null) && 
      (myGrid[jumpLanding[0]][jumpLanding[1]] == 0)) {
     toFill.addElement(jumpLanding);
   }
 }
     }
   } // end for loop
 }
 

}


      </source>
   
  
 
  



Dungeon game

   <source lang="java">

/*

Title:  J2ME Games With MIDP2
Authors:  Carol Hamer
Publisher:  Apress
ISBN:   1590593820
*/

import java.io.*; import javax.microedition.lcdui.*; import javax.microedition.lcdui.game.*; import javax.microedition.midlet.*; import javax.microedition.rms.*; /**

* This is the main class of the dungeon game.
* 
* @author Carol Hamer
*/

public class Dungeon extends MIDlet implements CommandListener {

 //-----------------------------------------------------
 //    game object fields
 /**
  * The canvas that the dungeon is drawn on.
  */
 private DungeonCanvas myCanvas;
 /**
  * the thread that advances the game clock.
  */
 private GameThread myGameThread;
 //-----------------------------------------------------
 //    command fields
 /**
  * The button to exit the game.
  */
 private Command myExitCommand = new Command("Exit", Command.EXIT, 99);
 /**
  * The command to save the game in progress.
  */
 private Command mySaveCommand = new Command("Save Game", Command.SCREEN, 2);
 /**
  * The command to restore a previously saved game.
  */
 private Command myRestoreCommand = new Command("Restore Game",
     Command.SCREEN, 2);
 /**
  * the command to start moving when the game is paused.
  */
 private Command myGoCommand = new Command("Go", Command.SCREEN, 1);
 /**
  * the command to pause the game.
  */
 private Command myPauseCommand = new Command("Pause", Command.SCREEN, 1);
 /**
  * the command to start a new game.
  */
 private Command myNewCommand = new Command("Next Board", Command.SCREEN, 1);
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Initialize the canvas and the commands.
  */
 public Dungeon() {
   try {
     // create the canvas and set up the commands:
     myCanvas = new DungeonCanvas(this);
     myCanvas.addCommand(myExitCommand);
     myCanvas.addCommand(mySaveCommand);
     myCanvas.addCommand(myRestoreCommand);
     myCanvas.addCommand(myPauseCommand);
     myCanvas.setCommandListener(this);
   } catch (Exception e) {
     // if there"s an error during creation, display it as an alert.
     errorMsg(e);
   }
 }
 /**
  * Switch the command to the play again command. (removing other commands
  * that are no longer relevant)
  */
 void setNewCommand() {
   myCanvas.removeCommand(myPauseCommand);
   myCanvas.removeCommand(myGoCommand);
   myCanvas.addCommand(myNewCommand);
 }
 /**
  * Switch the command to the go command. (removing other commands that are
  * no longer relevant)
  */
 void setGoCommand() {
   myCanvas.removeCommand(myPauseCommand);
   myCanvas.removeCommand(myNewCommand);
   myCanvas.addCommand(myGoCommand);
 }
 /**
  * Switch the command to the pause command. (removing other commands that
  * are no longer relevant)
  */
 void setPauseCommand() {
   myCanvas.removeCommand(myNewCommand);
   myCanvas.removeCommand(myGoCommand);
   myCanvas.addCommand(myPauseCommand);
 }
 //----------------------------------------------------------------
 //  implementation of MIDlet
 // these methods may be called by the application management
 // software at any time, so we always check fields for null
 // before calling methods on them.
 /**
  * Start the application.
  */
 public void startApp() throws MIDletStateChangeException {
   if (myCanvas != null) {
     if (myGameThread == null) {
       // create the thread and start the game:
       myGameThread = new GameThread(myCanvas);
       myCanvas.start();
       myGameThread.start();
     } else {
       // in case this gets called again after
       // the application has been started once:
       myCanvas.removeCommand(myGoCommand);
       myCanvas.addCommand(myPauseCommand);
       myCanvas.flushKeys();
       myGameThread.resumeGame();
     }
   }
 }
 /**
  * Stop the threads and throw out the garbage.
  */
 public void destroyApp(boolean unconditional)
     throws MIDletStateChangeException {
   myCanvas = null;
   if (myGameThread != null) {
     myGameThread.requestStop();
   }
   myGameThread = null;
   System.gc();
 }
 /**
  * Pause the game.
  */
 public void pauseApp() {
   if (myCanvas != null) {
     setGoCommand();
   }
   if (myGameThread != null) {
     myGameThread.pause();
   }
 }
 //----------------------------------------------------------------
 //  implementation of CommandListener
 /*
  * Respond to a command issued on the Canvas. (reset, exit, or change size
  * prefs).
  */
 public void commandAction(Command c, Displayable s) {
   try {
     if (c == myGoCommand) {
       myCanvas.setNeedsRepaint();
       myCanvas.removeCommand(myGoCommand);
       myCanvas.addCommand(myPauseCommand);
       myCanvas.flushKeys();
       myGameThread.resumeGame();
     } else if (c == myPauseCommand) {
       myCanvas.setNeedsRepaint();
       myCanvas.removeCommand(myPauseCommand);
       myCanvas.addCommand(myGoCommand);
       myGameThread.pause();
     } else if (c == myNewCommand) {
       myCanvas.setNeedsRepaint();
       // go to the next board and restart the game
       myCanvas.removeCommand(myNewCommand);
       myCanvas.addCommand(myPauseCommand);
       myCanvas.reset();
       myGameThread.resumeGame();
     /*} else if (c == Alert.DISMISS_COMMAND) {
       // if there was a serious enough error to
       // cause an alert, then we end the game
       // when the user is done reading the alert:
       // (Alert.DISMISS_COMMAND is the default
       // command that is placed on an Alert
       // whose timeout is FOREVER)
       destroyApp(false);
       notifyDestroyed();*/
     } else if (c == mySaveCommand) {
       myCanvas.setNeedsRepaint();
       myCanvas.saveGame();
     } else if (c == myRestoreCommand) {
       myCanvas.setNeedsRepaint();
       myCanvas.removeCommand(myNewCommand);
       myCanvas.removeCommand(myGoCommand);
       myCanvas.addCommand(myPauseCommand);
       myCanvas.revertToSaved();
     } else if (c == myExitCommand) {
       destroyApp(false);
       notifyDestroyed();
     }
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 //-------------------------------------------------------
 //  error methods
 /**
  * Converts an exception to a message and displays the message..
  */
 void errorMsg(Exception e) {
   if (e.getMessage() == null) {
     errorMsg(e.getClass().getName());
   } else {
     errorMsg(e.getClass().getName() + ":" + e.getMessage());
   }
 }
 /**
  * Displays an error message alert if something goes wrong.
  */
 void errorMsg(String msg) {
   Alert errorAlert = new Alert("error", msg, null, AlertType.ERROR);
   errorAlert.setCommandListener(this);
   errorAlert.setTimeout(Alert.FOREVER);
   Display.getDisplay(this).setCurrent(errorAlert);
 }

} /**

* This class represents doors and keys.
* 
* @author Carol Hamer
*/

class DoorKey extends Sprite {

 //---------------------------------------------------------
 //    fields
 /**
  * The image file shared by all doors and keys.
  */
 public static Image myImage;
 /**
  * A code int that indicates the door or key"s color.
  */
 private int myColor;
 //---------------------------------------------------------
 //    get/set data
 /**
  * @return the door or key"s color.
  */
 public int getColor() {
   return (myColor);
 }
 //---------------------------------------------------------
 //    constructor and initializer
 static {
   try {
     myImage = Image.createImage("/images/keys.png");
   } catch (Exception e) {
     throw (new RuntimeException(
         "DoorKey.<init>-->failed to load image, caught "
             + e.getClass() + ": " + e.getMessage()));
   }
 }
 /**
  * Standard constructor sets the image to the correct frame (according to
  * whether this is a door or a key and what color it should be) and then
  * puts it in the correct location.
  */
 public DoorKey(int color, boolean isKey, int[] gridCoordinates) {
   super(myImage, DungeonManager.SQUARE_WIDTH, DungeonManager.SQUARE_WIDTH);
   myColor = color;
   int imageIndex = color * 2;
   if (isKey) {
     imageIndex++;
   }
   setFrame(imageIndex);
   setPosition(gridCoordinates[0] * DungeonManager.SQUARE_WIDTH,
       gridCoordinates[1] * DungeonManager.SQUARE_WIDTH);
 }

} /**

* This class is a set of simple utility functions that can be used to convert
* standard data types to bytes and back again. It is used especially for data
* storage, but also for sending and receiving data.
* 
* @author Carol Hamer
*/

class DataConverter {

 //--------------------------------------------------------
 //  utilities to encode small, compactly-stored small ints.
 /**
  * Encodes a coordinate pair into a byte.
  * 
  * @param coordPair
  *            a pair of integers to be compacted into a single byte for
  *            storage. WARNING: each of the two values MUST BE between 0 and
  *            15 (inclusive). This method does not verify the length of the
  *            array (which must be 2!) nor does it verify that the ints are
  *            of the right size.
  */
 public static byte encodeCoords(int[] coordPair) {
   // get the byte value of the first coordinate:
   byte retVal = (new Integer(coordPair[0])).byteValue();
   // move the first coordinate"s value up to the top
   // half of the storage byte:
   retVal = (new Integer(retVal << 4)).byteValue();
   // store the second coordinate in the lower half
   // of the byte:
   retVal += (new Integer(coordPair[1])).byteValue();
   return (retVal);
 }
 /**
  * Encodes eight ints into a byte. This could be easily modified to encode
  * eight booleans.
  * 
  * @param eight
  *            an array of at least eight ints. WARNING: all values must be 0
  *            or 1! This method does not verify that the values are in the
  *            correct range nor does it verify that the array is long
  *            enough.
  * @param offset
  *            the index in the array eight to start reading data from.
  *            (should usually be 0)
  */
 public static byte encode8(int[] eight, int offset) {
   // get the byte value of the first int:
   byte retVal = (new Integer(eight[offset])).byteValue();
   // progressively move the data up one bit in the
   // storage byte and then record the next int in
   // the lowest spot in the storage byte:
   for (int i = offset + 1; i < 8 + offset; i++) {
     retVal = (new Integer(retVal << 1)).byteValue();
     retVal += (new Integer(eight[i])).byteValue();
   }
   return (retVal);
 }
 //--------------------------------------------------------
 //  utilities to decode small, compactly-stored small ints.
 /**
  * Turns a byte into a pair of coordinates.
  */
 public static int[] decodeCoords(byte coordByte) {
   int[] retArray = new int[2];
   // we perform a bitwise and with the value 15
   // in order to just get the bits of the lower
   // half of the byte:
   retArray[1] = coordByte & 15;
   // To get the bits of the upper half of the
   // byte, we perform a shift to move them down:
   retArray[0] = coordByte >> 4;
   // bytes in Java are generally assumed to be
   // signed, but in this coding algorithm we
   // would like to treat them as unsigned:
   if (retArray[0] < 0) {
     retArray[0] += 16;
   }
   return (retArray);
 }
 /**
  * Turns a byte into eight ints.
  */
 public static int[] decode8(byte data) {
   int[] retArray = new int[8];
   // The flag allows us to look at each bit individually
   // to determine if it is 1 or 0. The number 128
   // corresponds to the highest bit of a byte, so we
   // start with that one.
   int flag = 128;
   // We use a loop that checks
   // the data bit by bit by performing a bitwise
   // and (&) between the data byte and a flag:
   for (int i = 0; i < 8; i++) {
     if ((flag & data) != 0) {
       retArray[i] = 1;
     } else {
       retArray[i] = 0;
     }
     // move the flag down one bit so that we can
     // check the next bit of data on the next pass
     // through the loop:
     flag = flag >> 1;
   }
   return (retArray);
 }
 //--------------------------------------------------------
 //  standard integer interpretation
 /**
  * Uses an input stream to convert an array of bytes to an int.
  */
 public static int parseInt(byte[] data) throws IOException {
   DataInputStream stream = new DataInputStream(new ByteArrayInputStream(
       data));
   int retVal = stream.readInt();
   stream.close();
   return (retVal);
 }
 /**
  * Uses an output stream to convert an int to four bytes.
  */
 public static byte[] intToFourBytes(int i) throws IOException {
   ByteArrayOutputStream baos = new ByteArrayOutputStream(4);
   DataOutputStream dos = new DataOutputStream(baos);
   dos.writeInt(i);
   baos.close();
   dos.close();
   byte[] retArray = baos.toByteArray();
   return (retArray);
 }
 //--------------------------------------------------------
 //  integer interpretation illustrated
 /**
  * Java appears to treat a byte as being signed when returning it as an
  * int--this function converts from the signed value to the corresponding
  * unsigned value. This method is used by nostreamParseInt.
  */
 public static int unsign(int signed) {
   int retVal = signed;
   if (retVal < 0) {
     retVal += 256;
   }
   return (retVal);
 }
 /**
  * Takes an array of bytes and returns an int. This version will return the
  * same value as the method parseInt above. This version is included in
  * order to illustrate how Java encodes int values in terms of bytes.
  * 
  * @param data
  *            an array of 1, 2, or 4 bytes.
  */
 public static int nostreamParseInt(byte[] data) {
   // byte 0 is the high byte which is assumed
   // to be signed. As we add the lower bytes
   // one by one, we unsign them because because
   // a single byte alone is interpreted as signed,
   // but in an int only the top byte should be signed.
   // (note that the high byte is the first one in the array)
   int retVal = data[0];
   for (int i = 1; i < data.length; i++) {
     retVal = retVal << 8;
     retVal += unsign(data[i]);
   }
   return (retVal);
 }
 /**
  * Takes an arbitrary int and returns an array of four bytes. This version
  * will return the same byte array as the method intToFourBytes above. This
  * version is included in order to illustrate how Java encodes int values in
  * terms of bytes.
  */
 public static byte[] nostreamIntToFourBytes(int i) {
   byte[] fourBytes = new byte[4];
   // when you take the byte value of an int, it
   // only gives you the lowest byte. So we
   // get all four bytes by taking the lowest
   // byte four times and moving the whole int
   // down by one byte between each one.
   // (note that the high byte is the first one in the array)
   fourBytes[3] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[2] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[1] = (new Integer(i)).byteValue();
   i = i >> 8;
   fourBytes[0] = (new Integer(i)).byteValue();
   return (fourBytes);
 }
 /**
  * Takes an int between -32768 and 32767 and returns an array of two bytes.
  * This does not verify that the argument is of the right size. If the
  * absolute value of i is too high, it will not be encoded correctly.
  */
 public static byte[] nostreamIntToTwoBytes(int i) {
   byte[] twoBytes = new byte[2];
   // when you take the byte value of an int, it
   // only gives you the lowest byte. So we
   // get the lower two bytes by taking the lowest
   // byte twice and moving the whole int
   // down by one byte between each one.
   twoBytes[1] = (new Integer(i)).byteValue();
   i = i >> 8;
   twoBytes[0] = (new Integer(i)).byteValue();
   return (twoBytes);
 }

} /**

* This class contains the data for the map of the dungeon..
* 
* @author Carol Hamer
*/

class BoardDecoder {

 //--------------------------------------------------------
 //  fields
 /**
  * The coordinates of where the player starts on the map in terms of the
  * array indices.
  */
 private int[] myPlayerSquare;
 /**
  * The coordinates of the goal (crown).
  */
 private int[] myGoalSquare;
 /**
  * The coordinates of the doors. the there should be two in a row of each
  * color, following the same sequence as the keys.
  */
 private int[][] myDoors;
 /**
  * The coordinates of the Keys. the there should be of each color, following
  * the same sequence as the doors.
  */
 private int[][] myKeys;
 /**
  * The coordinates of the stone walls of the maze, encoded bit by bit.
  */
 private TiledLayer myLayer;
 /**
  * The data in bytes that gives the various boards. This was created using
  * EncodingUtils... This is a two-dimensional array: Each of the four main
  * sections corresponds to one of the four possible boards.
  */
 private static byte[][] myData = {
     { 0, 0, -108, -100, -24, 65, 21, 58, 53, -54, -116, -58, -56, -84,
         115, -118, -1, -1, -128, 1, -103, -15, -128, 25, -97, -127,
         -128, 79, -14, 1, -126, 121, -122, 1, -113, -49, -116, 1,
         -100, -3, -124, 5, -25, -27, -128, 1, -1, -1 },
     { 0, 1, 122, 90, -62, 34, -43, 72, -59, -29, 56, -55, 98, 126, -79,
         61, -1, -1, -125, 1, -128, 17, -26, 29, -31, 57, -72, 1,
         -128, -51, -100, 65, -124, 57, -2, 1, -126, 13, -113, 1,
         -97, 25, -127, -99, -8, 1, -1, -1 },
     { 0, 2, 108, -24, 18, -26, 102, 30, -58, 46, -28, -88, 34, -98, 97,
         -41, -1, -1, -96, 1, -126, 57, -9, 97, -127, 69, -119, 73,
         -127, 1, -109, 59, -126, 1, -26, 103, -127, 65, -103, 115,
         -127, 65, -25, 73, -128, 1, -1, -1 },
     { 0, 3, -114, 18, -34, 27, -39, -60, -76, -50, 118, 90, 82, -88,
         34, -74, -1, -1, -66, 1, -128, 121, -26, 125, -128, -123,
         -103, 29, -112, 1, -109, 49, -112, 1, -116, -31, -128, 5,
         -122, 5, -32, 13, -127, -51, -125, 1, -1, -1 }, };
 //--------------------------------------------------------
 //  initialization
 /**
  * Constructor fills data fields by interpreting the data bytes.
  */
 public BoardDecoder(int boardNum) throws Exception {
   // we start by selecting the two dimensional
   // array corresponding to the desired board:
   byte[] data = myData[boardNum];
   // The first two bytes give the version number and
   // the board number, but we ignore them because
   // they are assumed to be correct.
   // The third byte of the first array is the first one
   // we read: it gives the player"s starting coordinates:
   myPlayerSquare = DataConverter.decodeCoords(data[2]);
   // the next byte gives the coordinates of the crown:
   myGoalSquare = DataConverter.decodeCoords(data[3]);
   // the next four bytes give the coordinates of the keys:
   myKeys = new int[4][];
   for (int i = 0; i < myKeys.length; i++) {
     myKeys[i] = DataConverter.decodeCoords(data[i + 4]);
   }
   // the next eight bytes give the coordinates of the doors:
   myDoors = new int[8][];
   for (int i = 0; i < myDoors.length; i++) {
     myDoors[i] = DataConverter.decodeCoords(data[i + 8]);
   }
   // now we create the TiledLayer object that is the
   // background dungeon map:
   myLayer = new TiledLayer(16, 16,
       Image.createImage("/images/stone.png"),
       DungeonManager.SQUARE_WIDTH, DungeonManager.SQUARE_WIDTH);
   // now we call an internal utility that reads the array
   // of data that gives the positions of the blocks in the
   // walls of this dungeon:
   decodeDungeon(data, myLayer, 16);
 }
 //--------------------------------------------------------
 //  get/set data
 /**
  * @return the number of boards currently stored in this class.
  */
 public static int getNumBoards() {
   return (myData.length);
 }
 /**
  * get the coordinates of where the player starts on the map in terms of the
  * array indices.
  */
 public int[] getPlayerSquare() {
   return (myPlayerSquare);
 }
 /**
  * get the coordinates of the goal crown in terms of the array indices.
  */
 public int[] getGoalSquare() {
   return (myGoalSquare);
 }
 /**
  * get the tiled layer that gives the map of the dungeon.
  */
 public TiledLayer getLayer() {
   return (myLayer);
 }
 /**
  * Creates the array of door sprites. (call this only once to avoid creating
  * redundant sprites).
  */
 DoorKey[] createDoors() {
   DoorKey[] retArray = new DoorKey[8];
   for (int i = 0; i < 4; i++) {
     retArray[2 * i] = new DoorKey(i, false, myDoors[2 * i]);
     retArray[2 * i + 1] = new DoorKey(i, false, myDoors[2 * i + 1]);
   }
   return (retArray);
 }
 /**
  * Creates the array of key sprites. (call this only once to avoid creating
  * redundant sprites.)
  */
 DoorKey[] createKeys() {
   DoorKey[] retArray = new DoorKey[4];
   for (int i = 0; i < 4; i++) {
     retArray[i] = new DoorKey(i, true, myKeys[i]);
   }
   return (retArray);
 }
 //--------------------------------------------------------
 //  decoding utilities
 /**
  * Takes a dungeon given as a byte array and uses it to set the tiles of a
  * tiled layer.
  * 
  * The TiledLayer in this case is a 16 x 16 grid in which each square can be
  * either blank (value of 0) or can be filled with a stone block (value of
  * 1). Therefore each square requires only one bit of information. Each byte
  * of data in the array called "data" records the frame indices of eight
  * squares in the grid.
  */
 private static void decodeDungeon(byte[] data, TiledLayer dungeon,
     int offset) throws Exception {
   if (data.length + offset < 32) {
     throw (new Exception(
         "BoardDecoder.decodeDungeon-->not enough data!!!"));
   }
   // a frame index of zero indicates a blank square
   // (this is always true in a TiledLayer).
   // This TiledLayer has only one possible (non-blank)
   // frame, so a frame index of 1 indicates a stone block
   int frame = 0;
   // Each of the 32 bytes in the data array records
   // the frame indices of eight block in the 16 x 16
   // grid. Two bytes give one row of the dungeon,
   // so we have the array index go from zero to 16
   // to set the frame indices fro each of the 16 rows.
   for (int i = 0; i < 16; i++) {
     // The flag allows us to look at each bit individually
     // to determine if it is 1 or 0. The number 128
     // corresponds to the highest bit of a byte, so we
     // start with that one.
     int flag = 128;
     // Here we check two bytes at the same time
     // (the two bytes together correspond to one row
     // of the dungeon). We use a loop that checks
     // the bytes bit by bit by performing a bitwise
     // and (&) between the data byte and a flag:
     for (int j = 0; j < 8; j++) {
       if ((data[offset + 2 * i] & flag) != 0) {
         frame = 1;
       } else {
         frame = 0;
       }
       dungeon.setCell(j, i, frame);
       if ((data[offset + 2 * i + 1] & flag) != 0) {
         frame = 1;
       } else {
         frame = 0;
       }
       dungeon.setCell(j + 8, i, frame);
       // move the flag down one bit so that we can
       // check the next bit of data on the next pass
       // through the loop:
       flag = flag >> 1;
     }
   }
 }

} /**

* This class contains the loop that keeps the game running.
* 
* @author Carol Hamer
*/

class GameThread extends Thread {

 //---------------------------------------------------------
 //   fields
 /**
  * Whether or not the main thread would like this thread to pause.
  */
 private boolean myShouldPause;
 /**
  * Whether or not the main thread would like this thread to stop.
  */
 private static boolean myShouldStop;
 /**
  * A handle back to the graphical components.
  */
 private DungeonCanvas myDungeonCanvas;
 /**
  * The System.time of the last screen refresh, used to regulate refresh
  * speed.
  */
 private long myLastRefreshTime;
 //----------------------------------------------------------
 //   initialization
 /**
  * standard constructor.
  */
 GameThread(DungeonCanvas canvas) {
   myDungeonCanvas = canvas;
 }
 //----------------------------------------------------------
 //   utilities
 /**
  * Get the amount of time to wait between screen refreshes. Normally we wait
  * only a single millisecond just to give the main thread a chance to update
  * the keystroke info, but this method ensures that the game will not
  * attempt to show too many frames per second.
  */
 private long getWaitTime() {
   long retVal = 1;
   long difference = System.currentTimeMillis() - myLastRefreshTime;
   if (difference < 75) {
     retVal = 75 - difference;
   }
   return (retVal);
 }
 //----------------------------------------------------------
 //   actions
 /**
  * pause the game.
  */
 void pause() {
   myShouldPause = true;
 }
 /**
  * restart the game after a pause.
  */
 synchronized void resumeGame() {
   myShouldPause = false;
   notify();
 }
 /**
  * stops the game.
  */
 synchronized void requestStop() {
   myShouldStop = true;
   this.notify();
 }
 /**
  * start the game..
  */
 public void run() {
   // flush any keystrokes that occurred before the
   // game started:
   myDungeonCanvas.flushKeys();
   myShouldStop = false;
   myShouldPause = false;
   while (true) {
     myLastRefreshTime = System.currentTimeMillis();
     if (myShouldStop) {
       break;
     }
     myDungeonCanvas.checkKeys();
     myDungeonCanvas.updateScreen();
     // we do a very short pause to allow the other thread
     // to update the information about which keys are pressed:
     synchronized (this) {
       try {
         wait(getWaitTime());
       } catch (Exception e) {
       }
     }
     if (myShouldPause) {
       synchronized (this) {
         try {
           wait();
         } catch (Exception e) {
         }
       }
     }
   }
 }

} /**

* This class contains the data for a game currently in progress. used to store
* a game and to resume a stored game.
* 
* @author Carol Hamer
*/

class GameInfo {

 //--------------------------------------------------------
 //  fields
 /**
  * The name of the datastore.
  */
 public static final String STORE = "GameInfo";
 /**
  * This is set to true if an attempt is made to read a game when no game has
  * been saved.
  */
 private boolean myNoDataSaved;
 /**
  * The number that indicates which board the player is currently on.
  */
 private int myBoardNum;
 /**
  * The amount of time that has passed.
  */
 private int myTime;
 /**
  * The coordinates of where the player is on the board. coordinate values
  * must be between 0 and 15.
  */
 private int[] myPlayerSquare;
 /**
  * The coordinates of where the keys are currently found. MUST BE four sets
  * of two integer coordinates. coordinate values must be between 0 and 15.
  */
 private int[][] myKeyCoords;
 /**
  * The list of which doors are currently open. 0 = open 1 = closed WARNING:
  * this array MUST have length 8.
  */
 private int[] myDoorsOpen;
 /**
  * The number of the key that is currently being held by the player. if no
  * key is held, then the value is -1.
  */
 private int myHeldKey;
 //--------------------------------------------------------
 //  data gets/sets
 /**
  * @return true if no saved game records were found.
  */
 boolean getIsEmpty() {
   return (myNoDataSaved);
 }
 /**
  * @return The number that indicates which board the player is currently on.
  */
 int getBoardNum() {
   return (myBoardNum);
 }
 /**
  * @return The number of the key that is currently being held by the player.
  *         if no key is held, then the value is -1.
  */
 int getHeldKey() {
   return (myHeldKey);
 }
 /**
  * @return The amount of time that has passed.
  */
 int getTime() {
   return (myTime);
 }
 /**
  * @return The coordinates of where the player is on the board. coordinate
  *         values must be between 0 and 15.
  */
 int[] getPlayerSquare() {
   return (myPlayerSquare);
 }
 /**
  * @return The coordinates of where the keys are currently found. MUST BE
  *         four sets of two integer coordinates. coordinate values must be
  *         between 0 and 15.
  */
 int[][] getKeyCoords() {
   return (myKeyCoords);
 }
 /**
  * @return The list of which doors are currently open. 0 = open 1 = closed
  *         WARNING: this array MUST have length 8.
  */
 int[] getDoorsOpen() {
   return (myDoorsOpen);
 }
 //--------------------------------------------------------
 //  constructors
 /**
  * This constructor records the game info of a game currently in progress.
  */
 GameInfo(int boardNum, int time, int[] playerSquare, int[][] keyCoords,
     int[] doorsOpen, int heldKey) throws Exception {
   myBoardNum = boardNum;
   myTime = time;
   myPlayerSquare = playerSquare;
   myKeyCoords = keyCoords;
   myDoorsOpen = doorsOpen;
   myHeldKey = heldKey;
   encodeInfo();
 }
 /**
  * This constructor reads the game configuration from memory. This is used
  * to reconstruct a saved game.
  */
 GameInfo() {
   RecordStore store = null;
   try {
     // if the record store does not yet exist, don"t
     // create it
     store = RecordStore.openRecordStore(STORE, false);
     if ((store != null) && (store.getNumRecords() > 0)) {
       // the first record has id number 1
       // it should also be the only record since this
       // particular game stores only one game.
       byte[] data = store.getRecord(1);
       myBoardNum = data[0];
       myPlayerSquare = DataConverter.decodeCoords(data[1]);
       myKeyCoords = new int[4][];
       myKeyCoords[0] = DataConverter.decodeCoords(data[2]);
       myKeyCoords[1] = DataConverter.decodeCoords(data[3]);
       myKeyCoords[2] = DataConverter.decodeCoords(data[4]);
       myKeyCoords[3] = DataConverter.decodeCoords(data[5]);
       myDoorsOpen = DataConverter.decode8(data[6]);
       myHeldKey = data[7];
       byte[] fourBytes = new byte[4];
       System.arraycopy(data, 8, fourBytes, 0, 4);
       myTime = DataConverter.parseInt(fourBytes);
     } else {
       myNoDataSaved = true;
     }
   } catch (Exception e) {
     // this throws when the record store doesn"t exist.
     // for that or any error, we assume no data is saved:
     myNoDataSaved = true;
   } finally {
     try {
       if (store != null) {
         store.closeRecordStore();
       }
     } catch (Exception e) {
       // if the record store is open this shouldn"t throw.
     }
   }
 }
 //--------------------------------------------------------
 //  encoding method
 /**
  * Turn the data into a byte array and save it.
  */
 private void encodeInfo() throws Exception {
   RecordStore store = null;
   try {
     byte[] data = new byte[12];
     data[0] = (new Integer(myBoardNum)).byteValue();
     data[1] = DataConverter.encodeCoords(myPlayerSquare);
     data[2] = DataConverter.encodeCoords(myKeyCoords[0]);
     data[3] = DataConverter.encodeCoords(myKeyCoords[1]);
     data[4] = DataConverter.encodeCoords(myKeyCoords[2]);
     data[5] = DataConverter.encodeCoords(myKeyCoords[3]);
     data[6] = DataConverter.encode8(myDoorsOpen, 0);
     data[7] = (new Integer(myHeldKey)).byteValue();
     byte[] timeBytes = DataConverter.intToFourBytes(myTime);
     System.arraycopy(timeBytes, 0, data, 8, 4);
     // if the record store does not yet exist, the second
     // arg "true" tells it to create.
     store = RecordStore.openRecordStore(STORE, true);
     int numRecords = store.getNumRecords();
     if (numRecords > 0) {
       store.setRecord(1, data, 0, data.length);
     } else {
       store.addRecord(data, 0, data.length);
     }
   } catch (Exception e) {
     throw (e);
   } finally {
     try {
       if (store != null) {
         store.closeRecordStore();
       }
     } catch (Exception e) {
       // if the record store is open this shouldn"t throw.
     }
   }
 }

} /**

* This class contains the data for the map of the dungeon. This is a utility
* class that allows a developer to write the data for a board in a simple
* format, then this class encodes the data in a format that the game can use.
* 
* note that the data that this class encodes is hard-coded. that is because
* this class is intended to be used only a few times to encode the data. Once
* the board data has been encoded, it never needs to be encoded again. The
* encoding methods used in this class could be generalized to be used to create
* a board editor which would allow a user to easily create new boards, but that
* is an exercise for another day...
* 
* @author Carol Hamer
*/

class EncodingUtils {

 //--------------------------------------------------------
 //  fields
 /**
  * data for which squares are filled and which are blank. 0 = empty 1 =
  * filled
  */
 private int[][] mySquares = {
     { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
     { 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
     { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 },
     { 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1 },
     { 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1 },
     { 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
     { 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1 },
     { 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 },
     { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
     { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
     { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1 },
     { 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 },
     { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1 },
     { 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1 },
     { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
     { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, };
 /**
  * The coordinates of where the player starts on the map in terms of the
  * array indices.
  */
 private int[] myPlayerSquare = { 7, 10 };
 /**
  * The coordinates of the goal (crown).
  */
 private int[] myGoalSquare = { 5, 10 };
 //--------------------------------------------------------
 //  get/set data
 /**
  * Creates the array of door sprites. (call this only once to avoid creating
  * redundant sprites).
  */
 int[][] getDoorCoords() {
   int[][] retArray = new int[8][];
   for (int i = 0; i < retArray.length; i++) {
     retArray[i] = new int[2];
   }
   // red
   retArray[0][0] = 12;
   retArray[0][1] = 5;
   retArray[1][0] = 14;
   retArray[1][1] = 3;
   // green
   retArray[2][0] = 3;
   retArray[2][1] = 8;
   retArray[3][0] = 12;
   retArray[3][1] = 9;
   // blue
   retArray[4][0] = 6;
   retArray[4][1] = 2;
   retArray[5][0] = 7;
   retArray[5][1] = 14;
   // yellow
   retArray[6][0] = 11;
   retArray[6][1] = 1;
   retArray[7][0] = 3;
   retArray[7][1] = 13;
   return (retArray);
 }
 /**
  * Creates the array of key sprites. (call this only once to avoid creating
  * redundant sprites.)
  */
 int[][] getKeyCoords() {
   int[][] retArray = new int[4][];
   for (int i = 0; i < retArray.length; i++) {
     retArray[i] = new int[2];
   }
   // red
   retArray[0][0] = 12;
   retArray[0][1] = 2;
   // green
   retArray[1][0] = 2;
   retArray[1][1] = 2;
   // blue
   retArray[2][0] = 13;
   retArray[2][1] = 5;
   // yellow
   retArray[3][0] = 4;
   retArray[3][1] = 8;
   return (retArray);
 }
 //--------------------------------------------------------
 //  encoding / decoding utilities
 /**
  * Encodes the entire dungeon.
  */
 byte[][] encodeDungeon() {
   byte[][] retArray = new byte[2][];
   retArray[0] = new byte[16];
   // the first byte is the version number:
   retArray[0][0] = 0;
   // the second byte is the board number:
   retArray[0][1] = 0;
   // the player"s start square:
   retArray[0][2] = DataConverter.encodeCoords(myPlayerSquare);
   // the goal (crown) square:
   retArray[0][3] = DataConverter.encodeCoords(myGoalSquare);
   //encode the keys:
   int[][] keyCoords = getKeyCoords();
   for (int i = 0; i < keyCoords.length; i++) {
     retArray[0][i + 4] = DataConverter.encodeCoords(keyCoords[i]);
   }
   //encode the doors:
   int[][] doorCoords = getDoorCoords();
   for (int i = 0; i < doorCoords.length; i++) {
     retArray[0][i + 8] = DataConverter.encodeCoords(doorCoords[i]);
   }
   //encode the maze:
   try {
     retArray[1] = encodeDungeon(mySquares);
   } catch (Exception e) {
     e.printStackTrace();
   }
   return (retArray);
 }
 /**
  * Takes a dungeon given in terms of an array of 1s and 0s and turns it into
  * an array of bytes. WARNING: the array MUST BE 16 X 16.
  */
 static byte[] encodeDungeon(int[][] dungeonMap) throws Exception {
   if ((dungeonMap.length != 16) || (dungeonMap[0].length != 16)) {
     throw (new Exception(
         "EncodingUtils.encodeDungeon-->must be 16x16!!!"));
   }
   byte[] retArray = new byte[32];
   for (int i = 0; i < 16; i++) {
     retArray[2 * i] = DataConverter.encode8(dungeonMap[i], 0);
     retArray[2 * i + 1] = DataConverter.encode8(dungeonMap[i], 8);
   }
   return (retArray);
 }
 //--------------------------------------------------------
 //  main prints the bytes to standard out.
 // (note that this class is not intended to be run as a MIDlet)
 /**
  * Prints the byte version of the board to standard out.
  */
 public static void main(String[] args) {
   try {
     EncodingUtils map = new EncodingUtils();
     byte[][] data = map.encodeDungeon();
     System.out.println("EncodingUtils.main-->dungeon encoded");
     System.out.print("{\n   " + data[0][0]);
     for (int i = 1; i < data[0].length; i++) {
       System.out.print(", " + data[0][i]);
     }
     for (int i = 1; i < data[1].length; i++) {
       System.out.print(", " + data[1][i]);
     }
     System.out.println("\n};");
   } catch (Exception e) {
     e.printStackTrace();
   }
 }

} /**

* This class handles the graphics objects.
* 
* @author Carol Hamer
*/

class DungeonManager extends LayerManager {

 //---------------------------------------------------------
 //   dimension fields
 //  (constant after initialization)
 /**
  * The x-coordinate of the place on the game canvas where the LayerManager
  * window should appear, in terms of the coordiantes of the game canvas.
  */
 static int CANVAS_X;
 /**
  * The y-coordinate of the place on the game canvas where the LayerManager
  * window should appear, in terms of the coordiantes of the game canvas.
  */
 static int CANVAS_Y;
 /**
  * The width of the display window.
  */
 static int DISP_WIDTH;
 /**
  * The height of this object"s visible region.
  */
 static int DISP_HEIGHT;
 /**
  * the (right or left) distance the player goes in a single keystroke.
  */
 static final int MOVE_LENGTH = 8;
 /**
  * The width of the square tiles that this game is divided into. This is the
  * width of the stone walls as well as the princess and the ghost.
  */
 static final int SQUARE_WIDTH = 24;
 /**
  * The jump index that indicates that no jump is currently in progress..
  */
 static final int NO_JUMP = -6;
 /**
  * The maximum speed for the player"s fall..
  */
 static final int MAX_FREE_FALL = 3;
 //---------------------------------------------------------
 //   game object fields
 /**
  * the handle back to the canvas.
  */
 private DungeonCanvas myCanvas;
 /**
  * the background dungeon.
  */
 private TiledLayer myBackground;
 /**
  * the player.
  */
 private Sprite myPrincess;
 /**
  * the goal.
  */
 private Sprite myCrown;
 /**
  * the doors.
  */
 private DoorKey[] myDoors;
 /**
  * the keys.
  */
 private DoorKey[] myKeys;
 /**
  * the key currently held by the player.
  */
 private DoorKey myHeldKey;
 /**
  * The leftmost x-coordinate that should be visible on the screen in terms
  * of this objects internal coordinates.
  */
 private int myViewWindowX;
 /**
  * The top y-coordinate that should be visible on the screen in terms of
  * this objects internal coordinates.
  */
 private int myViewWindowY;
 /**
  * Where the princess is in the jump sequence.
  */
 private int myIsJumping = NO_JUMP;
 /**
  * Whether or not the screen needs to be repainted.
  */
 private boolean myModifiedSinceLastPaint = true;
 /**
  * Which board we"re playing on.
  */
 private int myCurrentBoardNum = 0;
 //-----------------------------------------------------
 //    gets/sets
 /**
  * Tell the layer manager that it needs to repaint.
  */
 public void setNeedsRepaint() {
   myModifiedSinceLastPaint = true;
 }
 //-----------------------------------------------------
 //    initialization
 //    set up or save game data.
 /**
  * Constructor merely sets the data.
  * 
  * @param x
  *            The x-coordinate of the place on the game canvas where the
  *            LayerManager window should appear, in terms of the coordiantes
  *            of the game canvas.
  * @param y
  *            The y-coordinate of the place on the game canvas where the
  *            LayerManager window should appear, in terms of the coordiantes
  *            of the game canvas.
  * @param width
  *            the width of the region that is to be occupied by the
  *            LayoutManager.
  * @param height
  *            the height of the region that is to be occupied by the
  *            LayoutManager.
  * @param canvas
  *            the DungeonCanvas that this LayerManager should appear on.
  */
 public DungeonManager(int x, int y, int width, int height,
     DungeonCanvas canvas) throws Exception {
   myCanvas = canvas;
   CANVAS_X = x;
   CANVAS_Y = y;
   DISP_WIDTH = width;
   DISP_HEIGHT = height;
   // create a decoder object that creates the dungeon and
   // its associated Sprites from data.
   BoardDecoder decoder = new BoardDecoder(myCurrentBoardNum);
   // get the background TiledLayer
   myBackground = decoder.getLayer();
   // get the coordinates of the square that the princess
   // starts on.
   int[] playerCoords = decoder.getPlayerSquare();
   // create the player sprite
   myPrincess = new Sprite(Image.createImage("/images/princess.png"),
       SQUARE_WIDTH, SQUARE_WIDTH);
   myPrincess.setFrame(1);
   // we define the reference pixel to be in the middle
   // of the princess image so that when the princess turns
   // from right to left (and vice versa) she does not
   // appear to move to a different location.
   myPrincess.defineReferencePixel(SQUARE_WIDTH / 2, 0);
   // the dungeon is a 16x16 grid, so the array playerCoords
   // gives the player"s location in terms of the grid, and
   // then we multiply those coordinates by the SQUARE_WIDTH
   // to get the precise pixel where the player should be
   // placed (in terms of the LayerManager"s coordinate system)
   myPrincess.setPosition(SQUARE_WIDTH * playerCoords[0], SQUARE_WIDTH
       * playerCoords[1]);
   // we append all of the Layers (TiledLayer and Sprite)
   // so that this LayerManager will paint them when
   // flushGraphics is called.
   append(myPrincess);
   // get the coordinates of the square where the crown
   // should be placed.
   int[] goalCoords = decoder.getGoalSquare();
   myCrown = new Sprite(Image.createImage("/images/crown.png"));
   myCrown.setPosition(
       (SQUARE_WIDTH * goalCoords[0]) + (SQUARE_WIDTH / 4),
       (SQUARE_WIDTH * goalCoords[1]) + (SQUARE_WIDTH / 2));
   append(myCrown);
   // The decoder creates the door and key sprites and places
   // them in the correct locations in terms of the LayerManager"s
   // coordinate system.
   myDoors = decoder.createDoors();
   myKeys = decoder.createKeys();
   for (int i = 0; i < myDoors.length; i++) {
     append(myDoors[i]);
   }
   for (int i = 0; i < myKeys.length; i++) {
     append(myKeys[i]);
   }
   // append the background last so it will be painted first.
   append(myBackground);
   // this sets the view screen so that the player is
   // in the center.
   myViewWindowX = SQUARE_WIDTH * playerCoords[0]
       - ((DISP_WIDTH - SQUARE_WIDTH) / 2);
   myViewWindowY = SQUARE_WIDTH * playerCoords[1]
       - ((DISP_HEIGHT - SQUARE_WIDTH) / 2);
   // a number of objects are created in order to set up the game,
   // but they should be eliminated to free up memory:
   decoder = null;
   System.gc();
 }
 /**
  * sets all variables back to their initial positions.
  */
 void reset() throws Exception {
   // first get rid of the old board:
   for (int i = 0; i < myDoors.length; i++) {
     remove(myDoors[i]);
   }
   myHeldKey = null;
   for (int i = 0; i < myKeys.length; i++) {
     remove(myKeys[i]);
   }
   remove(myBackground);
   // now create the new board:
   myCurrentBoardNum++;
   // in this version we go back to the beginning if
   // all boards have been completed.
   if (myCurrentBoardNum == BoardDecoder.getNumBoards()) {
     myCurrentBoardNum = 0;
   }
   // we create a new decoder object to read and interpret
   // all of the data for the current board.
   BoardDecoder decoder = new BoardDecoder(myCurrentBoardNum);
   // get the background TiledLayer
   myBackground = decoder.getLayer();
   // get the coordinates of the square that the princess
   // starts on.
   int[] playerCoords = decoder.getPlayerSquare();
   // the dungeon is a 16x16 grid, so the array playerCoords
   // gives the player"s location in terms of the grid, and
   // then we multiply those coordinates by the SQUARE_WIDTH
   // to get the precise pixel where the player should be
   // placed (in terms of the LayerManager"s coordinate system)
   myPrincess.setPosition(SQUARE_WIDTH * playerCoords[0], SQUARE_WIDTH
       * playerCoords[1]);
   myPrincess.setFrame(1);
   // get the coordinates of the square where the crown
   // should be placed.
   int[] goalCoords = decoder.getGoalSquare();
   myCrown.setPosition(
       (SQUARE_WIDTH * goalCoords[0]) + (SQUARE_WIDTH / 4),
       (SQUARE_WIDTH * goalCoords[1]) + (SQUARE_WIDTH / 2));
   // The decoder creates the door and key sprites and places
   // them in the correct locations in terms of the LayerManager"s
   // coordinate system.
   myDoors = decoder.createDoors();
   myKeys = decoder.createKeys();
   for (int i = 0; i < myDoors.length; i++) {
     append(myDoors[i]);
   }
   for (int i = 0; i < myKeys.length; i++) {
     append(myKeys[i]);
   }
   // append the background last so it will be painted first.
   append(myBackground);
   // this sets the view screen so that the player is
   // in the center.
   myViewWindowX = SQUARE_WIDTH * playerCoords[0]
       - ((DISP_WIDTH - SQUARE_WIDTH) / 2);
   myViewWindowY = SQUARE_WIDTH * playerCoords[1]
       - ((DISP_HEIGHT - SQUARE_WIDTH) / 2);
   // a number of objects are created in order to set up the game,
   // but they should be eliminated to free up memory:
   decoder = null;
   System.gc();
 }
 /**
  * sets all variables back to the position in the saved game.
  * 
  * @return the time on the clock of the saved game.
  */
 int revertToSaved() throws Exception {
   int retVal = 0;
   // first get rid of the old board:
   for (int i = 0; i < myDoors.length; i++) {
     remove(myDoors[i]);
   }
   myHeldKey = null;
   for (int i = 0; i < myKeys.length; i++) {
     remove(myKeys[i]);
   }
   remove(myBackground);
   // now get the info of the saved game
   // only one game is saved at a time, and the GameInfo object
   // will read the saved game"s data from memory.
   GameInfo info = new GameInfo();
   if (info.getIsEmpty()) {
     // if no game has been saved, we start from the beginning.
     myCurrentBoardNum = 0;
     reset();
   } else {
     // get the time on the clock of the saved game.
     retVal = info.getTime();
     // get the number of the board the saved game was on.
     myCurrentBoardNum = info.getBoardNum();
     // create the BoradDecoder that gives the data for the
     // desired board.
     BoardDecoder decoder = new BoardDecoder(myCurrentBoardNum);
     // get the background TiledLayer
     myBackground = decoder.getLayer();
     // get the coordinates of the square that the princess
     // was on in the saved game.
     int[] playerCoords = info.getPlayerSquare();
     myPrincess.setPosition(SQUARE_WIDTH * playerCoords[0], SQUARE_WIDTH
         * playerCoords[1]);
     myPrincess.setFrame(1);
     // get the coordinates of the square where the crown
     // should be placed (this is given by the BoardDecoder
     // and not from the data of the saved game because the
     // crown does not move during the game.
     int[] goalCoords = decoder.getGoalSquare();
     myCrown.setPosition((SQUARE_WIDTH * goalCoords[0])
         + (SQUARE_WIDTH / 4), (SQUARE_WIDTH * goalCoords[1])
         + (SQUARE_WIDTH / 2));
     // The decoder creates the door and key sprites and places
     // them in the correct locations in terms of the LayerManager"s
     // coordinate system.
     myDoors = decoder.createDoors();
     myKeys = decoder.createKeys();
     // get an array of ints that lists whether each door is
     // open or closed in the saved game
     int[] openDoors = info.getDoorsOpen();
     for (int i = 0; i < myDoors.length; i++) {
       append(myDoors[i]);
       if (openDoors[i] == 0) {
         // if the door was open, make it invisible
         myDoors[i].setVisible(false);
       }
     }
     // the keys can be moved by the player, so we get their
     // coordinates from the GameInfo saved data.
     int[][] keyCoords = info.getKeyCoords();
     for (int i = 0; i < myKeys.length; i++) {
       append(myKeys[i]);
       myKeys[i].setPosition(SQUARE_WIDTH * keyCoords[i][0],
           SQUARE_WIDTH * keyCoords[i][1]);
     }
     // if the player was holding a key in the saved game,
     // we have the player hold that key and set it to invisible.
     int heldKey = info.getHeldKey();
     if (heldKey != -1) {
       myHeldKey = myKeys[heldKey];
       myHeldKey.setVisible(false);
     }
     // append the background last so it will be painted first.
     append(myBackground);
     // this sets the view screen so that the player is
     // in the center.
     myViewWindowX = SQUARE_WIDTH * playerCoords[0]
         - ((DISP_WIDTH - SQUARE_WIDTH) / 2);
     myViewWindowY = SQUARE_WIDTH * playerCoords[1]
         - ((DISP_HEIGHT - SQUARE_WIDTH) / 2);
     // a number of objects are created in order to set up the game,
     // but they should be eliminated to free up memory:
     decoder = null;
     System.gc();
   }
   return (retVal);
 }
 /**
  * save the current game in progress.
  */
 void saveGame(int gameTicks) throws Exception {
   int[] playerSquare = new int[2];
   // the coordinates of the player are given in terms of
   // the 16 x 16 dungeon grid. We divide the player"s
   // pixel coordinates to ge the right grid square.
   // If the player was not precisely alligned with a
   // grid square when the game was saved, the difference
   // will be shaved off.
   playerSquare[0] = myPrincess.getX() / SQUARE_WIDTH;
   playerSquare[1] = myPrincess.getY() / SQUARE_WIDTH;
   // save the coordinates of the current locations of
   // the keys, and if a key is currently held by the
   // player, we save the info of which one it was.
   int[][] keyCoords = new int[4][];
   int heldKey = -1;
   for (int i = 0; i < myKeys.length; i++) {
     keyCoords[i] = new int[2];
     keyCoords[i][0] = myKeys[i].getX() / SQUARE_WIDTH;
     keyCoords[i][1] = myKeys[i].getY() / SQUARE_WIDTH;
     if ((myHeldKey != null) && (myKeys[i] == myHeldKey)) {
       heldKey = i;
     }
   }
   // save the information of which doors were open.
   int[] doorsOpen = new int[8];
   for (int i = 0; i < myDoors.length; i++) {
     if (myDoors[i].isVisible()) {
       doorsOpen[i] = 1;
     }
   }
   // take all of the information we"ve gathered and
   // create a GameInfo object that will save the info
   // in the device"s memory.
   GameInfo info = new GameInfo(myCurrentBoardNum, gameTicks,
       playerSquare, keyCoords, doorsOpen, heldKey);
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * paint the game graphic on the screen.
  */
 public void paint(Graphics g) throws Exception {
   // only repaint if something has changed:
   if (myModifiedSinceLastPaint) {
     g.setColor(DungeonCanvas.WHITE);
     // paint the background white to cover old game objects
     // that have changed position since last paint.
     // here coordinates are given
     // with respect to the graphics (canvas) origin:
     g.fillRect(0, 0, DISP_WIDTH, DISP_HEIGHT);
     // here coordinates are given
     // with respect to the LayerManager origin:
     setViewWindow(myViewWindowX, myViewWindowY, DISP_WIDTH, DISP_HEIGHT);
     // call the paint funstion of the superclass LayerManager
     // to paint all of the Layers
     paint(g, CANVAS_X, CANVAS_Y);
     // don"t paint again until something changes:
     myModifiedSinceLastPaint = false;
   }
 }
 //-------------------------------------------------------
 //  game movements
 /**
  * respond to keystrokes by deciding where to move and then moving the
  * pieces and the view window correspondingly.
  */
 void requestMove(int horizontal, int vertical) {
   if (horizontal != 0) {
     // see how far the princess can move in the desired
     // horizontal direction (if not blocked by a wall
     // or closed door)
     horizontal = requestHorizontal(horizontal);
   }
   // vertical < 0 indicates that the user has
   // pressed the UP button and would like to jump.
   // therefore, if we"re not currently jumping,
   // we begin the jump.
   if ((myIsJumping == NO_JUMP) && (vertical < 0)) {
     myIsJumping++;
   } else if (myIsJumping == NO_JUMP) {
     // if we"re not jumping at all, we need to check
     // if the princess should be falling:
     // we (temporarily) move the princess down and see if that
     // causes a collision with the floor:
     myPrincess.move(0, MOVE_LENGTH);
     // if the princess can move down without colliding
     // with the floor, then we set the princess to
     // be falling. The variable myIsJumping starts
     // negative while the princess is jumping up and
     // is zero or positive when the princess is coming
     // back down. We therefore set myIsJumping to
     // zero to indicate that the princess should start
     // falling.
     if (!checkCollision()) {
       myIsJumping = 0;
     }
     // we move the princess Sprite back to the correct
     // position she was at before we (temporarily) moved
     // her down to see if she would fall.
     myPrincess.move(0, -MOVE_LENGTH);
   }
   // if the princess is currently jumping or falling,
   // we calculate the vertical distance she should move
   // (taking into account the horizontal distance that
   // she is also moving).
   if (myIsJumping != NO_JUMP) {
     vertical = jumpOrFall(horizontal);
   }
   // now that we"ve calculated how far the princess
   // should move, we move her. (this is a call to
   // another internal method of this method
   // suite, it is not a built-in LayerManager method):
   move(horizontal, vertical);
 }
 /**
  * Internal to requestMove. Calculates what the real horizontal distance
  * moved should be after taking obstacles into account.
  * 
  * @return the horizontal distance that the player can move.
  */
 private int requestHorizontal(int horizontal) {
   // we (temporarily) move her to the right or left
   // and see if she hits a wall or a door:
   myPrincess.move(horizontal * MOVE_LENGTH, 0);
   if (checkCollision()) {
     // if she hits something, then she"s not allowed
     // to go in that direction, so we set the horizontal
     // move distance to zero and then move the princess
     // back to where she was.
     myPrincess.move(-horizontal * MOVE_LENGTH, 0);
     horizontal = 0;
   } else {
     // if she doesn"t hit anything then the move request
     // succeeds, but we still move her back to the
     // earlier position because this was just the checking
     // phase.
     myPrincess.move(-horizontal * MOVE_LENGTH, 0);
     horizontal *= MOVE_LENGTH;
   }
   return (horizontal);
 }
 /**
  * Internal to requestMove. Calculates the vertical change in the player"s
  * position if jumping or falling. this method should only be called if the
  * player is currently jumping or falling.
  * 
  * @return the vertical distance that the player should move this turn.
  *         (negative moves up, positive moves down)
  */
 private int jumpOrFall(int horizontal) {
   // by default we do not move vertically
   int vertical = 0;
   // The speed of rise or descent is computed using
   // the int myIsJumping. Since we are in a jump or
   // fall, we advance the jump by one (which simulates
   // the downward pull of gravity by slowing the rise
   // or accellerating the fall) unless the player is
   // already falling at maximum speed. (a maximum
   // free fall speed is necessary because otherwise
   // it is possible for the player to fall right through
   // the bottom of the maze...)
   if (myIsJumping <= MAX_FREE_FALL) {
     myIsJumping++;
   }
   if (myIsJumping < 0) {
     // if myIsJumping is negative, that means that
     // the princess is rising. We calculate the
     // number of pixels to go up by raising 2 to
     // the power myIsJumping (absolute value).
     // note that we make the result negative because
     // the up and down coordinates in Java are the
     // reverse of the vertical coordinates we learned
     // in math class: as you go up, the coordinate
     // values go down, and as you go down the screen,
     // the coordinate numbers go up.
     vertical = -(2 << (-myIsJumping));
   } else {
     // if myIsJumping is positive, the princess is falling.
     // we calculate the distance to fall by raising two
     // to the power of the absolute value of myIsJumping.
     vertical = (2 << (myIsJumping));
   }
   // now we temporarily move the princess the desired
   // vertical distance (with the corresponding horizontal
   // distance also thrown in), and see if she hits anything:
   myPrincess.move(horizontal, vertical);
   if (checkCollision()) {
     // here we"re in the case where she did hit something.
     // we move her back into position and then see what
     // to do about it.
     myPrincess.move(-horizontal, -vertical);
     if (vertical > 0) {
       // in this case the player is falling.
       // so we need to determine precisely how
       // far she can fall before she hit the bottom
       vertical = 0;
       // we temporarily move her the desired horizontal
       // distance while calculating the corresponding
       // vertical distance.
       myPrincess.move(horizontal, 0);
       while (!checkCollision()) {
         vertical++;
         myPrincess.move(0, 1);
       }
       // now that we"ve calculated how far she can fall,
       // we move her back to her earlier position
       myPrincess.move(-horizontal, -vertical);
       // we subtract 1 pixel from the distance calculated
       // because once she has actually collided with the
       // floor, she"s gone one pixel too far...
       vertical--;
       // now that she"s hit the floor, she"s not jumping
       // anymore.
       myIsJumping = NO_JUMP;
     } else {
       // in this case we"re going up, so she
       // must have hit her head.
       // This next if is checking for a special
       // case where there"s room to jump up exactly
       // one square. In that case we increase the
       // value of myIsJumping in order to make the
       // princess not rise as high. The details
       // of the calculation in this case were found
       // through trial and error:
       if (myIsJumping == NO_JUMP + 2) {
         myIsJumping++;
         vertical = -(2 << (-myIsJumping));
         // now we see if the special shortened jump
         // still makes her hit her head:
         // (as usual, temporarily move her to test
         // for collisions)
         myPrincess.move(horizontal, vertical);
         if (checkCollision()) {
           // if she still hits her head even
           // with this special shortened jump,
           // then she was not meant to jump...
           myPrincess.move(-horizontal, -vertical);
           vertical = 0;
           myIsJumping = NO_JUMP;
         } else {
           // now that we"ve chhecked for collisions,
           // we move the player back to her earlier
           // position:
           myPrincess.move(-horizontal, -vertical);
         }
       } else {
         // if she hit her head, then she should not
         // jump up.
         vertical = 0;
         myIsJumping = NO_JUMP;
       }
     }
   } else {
     // since she didn"t hit anything when we moved
     // her, then all we have to do is move her back.
     myPrincess.move(-horizontal, -vertical);
   }
   return (vertical);
 }
 /**
  * Internal to requestMove. Once the moves have been determined, actually
  * perform the move.
  */
 private void move(int horizontal, int vertical) {
   // repaint only if we actually change something:
   if ((horizontal != 0) || (vertical != 0)) {
     myModifiedSinceLastPaint = true;
   }
   // if the princess is moving left or right, we set
   // her image to be facing the right direction:
   if (horizontal > 0) {
     myPrincess.setTransform(Sprite.TRANS_NONE);
   } else if (horizontal < 0) {
     myPrincess.setTransform(Sprite.TRANS_MIRROR);
   }
   // if she"s jumping or falling, we set the image to
   // the frame where the skirt is inflated:
   if (vertical != 0) {
     myPrincess.setFrame(0);
     // if she"s just running, we alternate between the
     // two frames:
   } else if (horizontal != 0) {
     if (myPrincess.getFrame() == 1) {
       myPrincess.setFrame(0);
     } else {
       myPrincess.setFrame(1);
     }
   }
   // move the position of the view window so that
   // the player stays in the center:
   myViewWindowX += horizontal;
   myViewWindowY += vertical;
   // after all that work, we finally move the
   // princess for real!!!
   myPrincess.move(horizontal, vertical);
 }
 //-------------------------------------------------------
 //  sprite interactions
 /**
  * Drops the currently held key and picks up another.
  */
 void putDownPickUp() {
   // we do not want to allow the player to put
   // down the key in the air, so we verify that
   // we"re not jumping or falling first:
   if ((myIsJumping == NO_JUMP) && (myPrincess.getY() % SQUARE_WIDTH == 0)) {
     // since we"re picking something up or putting
     // something down, the display changes and needs
     // to be repainted:
     setNeedsRepaint();
     // if the thing we"re picking up is the crown,
     // we"re done, the player has won:
     if (myPrincess.collidesWith(myCrown, true)) {
       myCanvas.setGameOver();
       return;
     }
     // keep track of the key we"re putting down in
     // order to place it correctly:
     DoorKey oldHeld = myHeldKey;
     myHeldKey = null;
     // if the princess is on top of another key,
     // that one becomes the held key and is hence
     // made invisible:
     for (int i = 0; i < myKeys.length; i++) {
       // we check myHeldKey for null because we don"t
       // want to accidentally pick up two keys.
       if ((myPrincess.collidesWith(myKeys[i], true))
           && (myHeldKey == null)) {
         myHeldKey = myKeys[i];
         myHeldKey.setVisible(false);
       }
     }
     if (oldHeld != null) {
       // place the key we"re putting down in the Princess"s
       // current position and make it visible:
       oldHeld.setPosition(myPrincess.getX(), myPrincess.getY());
       oldHeld.setVisible(true);
     }
   }
 }
 /**
  * Checks of the player hits a stone wall or a door.
  */
 boolean checkCollision() {
   boolean retVal = false;
   // the "true" arg meand to check for a pixel-level
   // collision (so merely an overlap in image
   // squares does not register as a collision)
   if (myPrincess.collidesWith(myBackground, true)) {
     retVal = true;
   } else {
     // Note: it is not necessary to synchronize
     // this block because the thread that calls this
     // method is the same as the one that puts down the
     // keys, so there"s no danger of the key being put down
     // between the moment we check for the key and
     // the moment we open the door:
     for (int i = 0; i < myDoors.length; i++) {
       // if she"s holding the right key, then open the door
       // otherwise bounce off
       if (myPrincess.collidesWith(myDoors[i], true)) {
         if ((myHeldKey != null)
             && (myDoors[i].getColor() == myHeldKey.getColor())) {
           setNeedsRepaint();
           myDoors[i].setVisible(false);
         } else {
           // if she"s not holding the right key, then
           // she has collided with the door just the same
           // as if she had collided with a wall:
           retVal = true;
         }
       }
     }
   }
   return (retVal);
 }

} /**

* This class is the display of the game.
* 
* @author Carol Hamer
*/

class DungeonCanvas extends GameCanvas {

 //---------------------------------------------------------
 //   dimension fields
 //  (constant after initialization)
 /**
  * the height of the black region below the play area.
  */
 static int TIMER_HEIGHT = 32;
 /**
  * the top corner x coordinate according to this object"s coordinate
  * system:.
  */
 static final int CORNER_X = 0;
 /**
  * the top corner y coordinate according to this object"s coordinate
  * system:.
  */
 static final int CORNER_Y = 0;
 /**
  * the width of the portion of the screen that this canvas can use.
  */
 static int DISP_WIDTH;
 /**
  * the height of the portion of the screen that this canvas can use.
  */
 static int DISP_HEIGHT;
 /**
  * the height of the font used for this game.
  */
 static int FONT_HEIGHT;
 /**
  * the font used for this game.
  */
 static Font FONT;
 /**
  * color constant
  */
 public static final int BLACK = 0;
 /**
  * color constant
  */
 public static final int WHITE = 0xffffff;
 //---------------------------------------------------------
 //   game object fields
 /**
  * a handle to the display.
  */
 private Display myDisplay;
 /**
  * a handle to the MIDlet object (to keep track of buttons).
  */
 private Dungeon myDungeon;
 /**
  * the LayerManager that handles the game graphics.
  */
 private DungeonManager myManager;
 /**
  * whether or not the game has ended.
  */
 private static boolean myGameOver;
 /**
  * The number of ticks on the clock the last time the time display was
  * updated. This is saved to determine if the time string needs to be
  * recomputed.
  */
 private int myOldGameTicks = 0;
 /**
  * the number of game ticks that have passed since the beginning of the
  * game.
  */
 private int myGameTicks = myOldGameTicks;
 /**
  * we save the time string to avoid recreating it unnecessarily.
  */
 private static String myInitialString = "0:00";
 /**
  * we save the time string to avoid recreating it unnecessarily.
  */
 private String myTimeString = myInitialString;
 //-----------------------------------------------------
 //    gets/sets
 /**
  * This is called when the game ends.
  */
 void setGameOver() {
   myGameOver = true;
   myDungeon.pauseApp();
 }
 /**
  * Find out if the game has ended.
  */
 static boolean getGameOver() {
   return (myGameOver);
 }
 /**
  * Tell the layer manager that it needs to repaint.
  */
 public void setNeedsRepaint() {
   myManager.setNeedsRepaint();
 }
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Constructor sets the data, performs dimension calculations, and creates
  * the graphical objects.
  */
 public DungeonCanvas(Dungeon midlet) throws Exception {
   super(false);
   myDisplay = Display.getDisplay(midlet);
   myDungeon = midlet;
   // calculate the dimensions
   DISP_WIDTH = getWidth();
   DISP_HEIGHT = getHeight();
   if ((!myDisplay.isColor()) || (myDisplay.numColors() < 256)) {
     throw (new Exception("game requires full-color screen"));
   }
   if ((DISP_WIDTH < 150) || (DISP_HEIGHT < 170)) {
     throw (new Exception("Screen too small"));
   }
   if ((DISP_WIDTH > 250) || (DISP_HEIGHT > 250)) {
     throw (new Exception("Screen too large"));
   }
   // since the time is painted in white on black,
   // it shows up better if the font is bold:
   FONT = Font
       .getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
   // calculate the height of the black region that the
   // timer is painted on:
   FONT_HEIGHT = FONT.getHeight();
   TIMER_HEIGHT = FONT_HEIGHT + 8;
   // create the LayerManager (where all of the interesting
   // graphics go!) and give it the dimensions of the
   // region it is supposed to paint:
   if (myManager == null) {
     myManager = new DungeonManager(CORNER_X, CORNER_Y, DISP_WIDTH,
         DISP_HEIGHT - TIMER_HEIGHT, this);
   }
 }
 /**
  * This is called as soon as the application begins.
  */
 void start() {
   myGameOver = false;
   myDisplay.setCurrent(this);
   setNeedsRepaint();
 }
 /**
  * sets all variables back to their initial positions.
  */
 void reset() throws Exception {
   // most of the variables that need to be reset
   // are held by the LayerManager:
   myManager.reset();
   myGameOver = false;
   setNeedsRepaint();
 }
 /**
  * sets all variables back to the positions from a previously saved game.
  */
 void revertToSaved() throws Exception {
   // most of the variables that need to be reset
   // are held by the LayerManager, so we
   // prompt the LayerManager to get the
   // saved data:
   myGameTicks = myManager.revertToSaved();
   myGameOver = false;
   myOldGameTicks = myGameTicks;
   myTimeString = formatTime();
   setNeedsRepaint();
 }
 /**
  * save the current game in progress.
  */
 void saveGame() throws Exception {
   myManager.saveGame(myGameTicks);
 }
 /**
  * clears the key states.
  */
 void flushKeys() {
   getKeyStates();
 }
 /**
  * If the game is hidden by another app (or a menu) ignore it since not much
  * happens in this game when the user is not actively interacting with it.
  * (we could pause the timer, but it"s not important enough to bother with
  * when the user is just pulling up a menu for a few seconds)
  */
 protected void hideNotify() {
 }
 /**
  * When it comes back into view, just make sure the manager knows that it
  * needs to repaint.
  */
 protected void showNotify() {
   setNeedsRepaint();
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * paint the game graphics on the screen.
  */
 public void paint(Graphics g) {
   // color the bottom segment of the screen black
   g.setColor(BLACK);
   g.fillRect(CORNER_X, CORNER_Y + DISP_HEIGHT - TIMER_HEIGHT, DISP_WIDTH,
       TIMER_HEIGHT);
   // paint the LayerManager (which paints
   // all of the interesting graphics):
   try {
     myManager.paint(g);
   } catch (Exception e) {
     myDungeon.errorMsg(e);
   }
   // draw the time
   g.setColor(WHITE);
   g.setFont(FONT);
   g.drawString("Time: " + formatTime(), DISP_WIDTH / 2, CORNER_Y
       + DISP_HEIGHT - 4, g.BOTTOM | g.HCENTER);
   // write "Dungeon Completed" when the user finishes a board:
   if (myGameOver) {
     myDungeon.setNewCommand();
     // clear the top region:
     g.setColor(WHITE);
     g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, FONT_HEIGHT * 2 + 1);
     int goWidth = FONT.stringWidth("Dungeon Completed");
     g.setColor(BLACK);
     g.setFont(FONT);
     g.drawString("Dungeon Completed", (DISP_WIDTH - goWidth) / 2,
         CORNER_Y + FONT_HEIGHT, g.TOP | g.LEFT);
   }
 }
 /**
  * a simple utility to make the number of ticks look like a time...
  */
 public String formatTime() {
   if ((myGameTicks / 16) != myOldGameTicks) {
     myTimeString = "";
     myOldGameTicks = (myGameTicks / 16) + 1;
     int smallPart = myOldGameTicks % 60;
     int bigPart = myOldGameTicks / 60;
     myTimeString += bigPart + ":";
     if (smallPart / 10 < 1) {
       myTimeString += "0";
     }
     myTimeString += smallPart;
   }
   return (myTimeString);
 }
 //-------------------------------------------------------
 //  game movements
 /**
  * update the display.
  */
 void updateScreen() {
   myGameTicks++;
   // paint the display
   try {
     paint(getGraphics());
     flushGraphics(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
   } catch (Exception e) {
     myDungeon.errorMsg(e);
   }
 }
 /**
  * Respond to keystrokes.
  */
 public void checkKeys() {
   if (!myGameOver) {
     int vertical = 0;
     int horizontal = 0;
     // determine which moves the user would like to make:
     int keyState = getKeyStates();
     if ((keyState & LEFT_PRESSED) != 0) {
       horizontal = -1;
     }
     if ((keyState & RIGHT_PRESSED) != 0) {
       horizontal = 1;
     }
     if ((keyState & UP_PRESSED) != 0) {
       vertical = -1;
     }
     if ((keyState & DOWN_PRESSED) != 0) {
       // if the user presses the down key,
       // we put down or pick up a key object
       // or pick up the crown:
       myManager.putDownPickUp();
     }
     // tell the manager to move the player
     // accordingly if possible:
     myManager.requestMove(horizontal, vertical);
   }
 }

}

      </source>
   
  
 
  



Game Action Example

   <source lang="java">

/* J2ME: The Complete Reference James Keogh Publisher: McGraw-Hill ISBN 0072227109

  • /

//jad file (please verify the jar size) /* MIDlet-Name: GameActionExample MIDlet-Version: 1.0 MIDlet-Vendor: MyCompany MIDlet-Jar-URL: GameActionExample.jar MIDlet-1: GameActionExample, , GameActionExample MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-1.0 MIDlet-JAR-SIZE: 100

  • /

import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class GameActionExample extends MIDlet {

 private Display  display;       
 private MyCanvas canvas;   
 public GameActionExample()
 {
   display = Display.getDisplay(this);
   canvas  = new MyCanvas (this);
 }
 protected void startApp()
 {
   display.setCurrent(canvas);
 }
 protected void pauseApp()
 { 
 }
 protected void destroyApp( boolean unconditional )
 { 
 }
 public void exitMIDlet()
 {
   destroyApp(true);
   notifyDestroyed();
 }

} class MyCanvas extends Canvas implements CommandListener {

 private Command exit;          
 private String message;
 private GameActionExample gameActionExample;
 private int x, y;
 public MyCanvas (GameActionExample gameActionExample)
 {
   x = 5;
   y = 5;
   message = "Use Game Keys";    
   this.gameActionExample = gameActionExample;
   exit = new Command("Exit", Command.EXIT, 1);
   addCommand(exit);
   setCommandListener(this);
 } 
 protected void paint(Graphics graphics)
 {
   graphics.setColor(255,255,255);
   graphics.fillRect(0, 0, getWidth(), getHeight());
   graphics.setColor(255, 0, 0);
   graphics.drawString(message, x, y, Graphics.TOP | Graphics.LEFT);
 }
 public void commandAction(Command command, Displayable displayable)
 {
   if (command == exit)
   {
     gameActionExample.exitMIDlet();
    }
 }
 protected void keyPressed(int key)
 {
   switch ( getGameAction(key) ){
    case Canvas.UP:
      message = "up";
      y--;
      break;
    case Canvas.DOWN:
      message = "down";
      y++;
      break;
    case Canvas.LEFT:
      message = "left";
      x--;
      break;
    case Canvas.RIGHT:
      message = "right";
      x++;
      break;
    case Canvas.FIRE:
      message = "FIRE";
      break;
   }
   repaint();
 }

}


      </source>
   
  
 
  



Maze game

   <source lang="java">

/* Title: J2ME Games With MIDP2 Authors: Carol Hamer Publisher: Apress ISBN: 1590593820

  • /

import java.util.Random; import java.util.Vector; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; /**

* This is the main class of the maze game.
*
* @author Carol Hamer
*/

public class Maze extends MIDlet implements CommandListener {

 //----------------------------------------------------------------
 //  game object fields
 /**
  * The canvas that the maze is drawn on.
  */
 private MazeCanvas myCanvas;
 /**
  * The screen that allows the user to alter the size parameters 
  * of the maze.
  */
 private SelectScreen mySelectScreen;
 //----------------------------------------------------------------
 //  command fields
 /**
  * The button to exit the game.
  */
 private Command myExitCommand = new Command("Exit", Command.EXIT, 99);
 /**
  * The command to create a new maze.  (This command may appear in a menu)
  */
 private Command myNewCommand = new Command("New Maze", Command.SCREEN, 1);
 /**
  * The command to dismiss an alert error message.  In MIDP 2.0
  * an Alert set to Alert.FOREVER automatically has a default 
  * dismiss command.  This program does not use it in order to 
  * allow backwards com
  */
 private Command myAlertDoneCommand = new Command("Done", Command.EXIT, 1);
 /**
  * The command to go to the screen that allows the user 
  * to alter the size parameters.  (This command may appear in a menu)
  */
 private Command myPrefsCommand 
   = new Command("Size Preferences", Command.SCREEN, 1);
 //----------------------------------------------------------------
 //  initialization
 /**
  * Initialize the canvas and the commands.
  */
 public Maze() {
   try { 
     myCanvas = new MazeCanvas(Display.getDisplay(this));
     myCanvas.addCommand(myExitCommand);
     myCanvas.addCommand(myNewCommand);
     myCanvas.addCommand(myPrefsCommand);
     myCanvas.setCommandListener(this);
   } catch(Exception e) {
     // if there"s an error during creation, display it as an alert.
     Alert errorAlert = new Alert("error", 
          e.getMessage(), null, AlertType.ERROR);
     errorAlert.setCommandListener(this);
     errorAlert.setTimeout(Alert.FOREVER);
     errorAlert.addCommand(myAlertDoneCommand);
     Display.getDisplay(this).setCurrent(errorAlert);
   }
 }
 //----------------------------------------------------------------
 //  implementation of MIDlet
 /**
  * Start the application.
  */
 public void startApp() throws MIDletStateChangeException {
   if(myCanvas != null) {
     myCanvas.start();
   }
 }
 
 /**
  * Clean up.
  */
 public void destroyApp(boolean unconditional) 
     throws MIDletStateChangeException {
   myCanvas = null;
   System.gc();
 }
 /**
  * Does nothing since this program occupies no shared resources 
  * and little memory.
  */
 public void pauseApp() {
 }
 //----------------------------------------------------------------
 //  implementation of CommandListener
 /*
  * Respond to a command issued on the Canvas.
  * (reset, exit, or change size prefs).
  */
 public void commandAction(Command c, Displayable s) {
   if(c == myNewCommand) {
     myCanvas.newMaze();
   } else if(c == myAlertDoneCommand) {
     try {
         destroyApp(false);
         notifyDestroyed();
     } catch (MIDletStateChangeException ex) {
     }
   } else if(c == myPrefsCommand) {
     if(mySelectScreen == null) {
         mySelectScreen = new SelectScreen(myCanvas);
     }
     Display.getDisplay(this).setCurrent(mySelectScreen);
   } else if(c == myExitCommand) {
     try {
        destroyApp(false);
        notifyDestroyed();
     } catch (MIDletStateChangeException ex) {
     }
   }
 }
 

}

/**

* This class is the display of the game.
* 
* @author Carol Hamer
*/

class MazeCanvas extends javax.microedition.lcdui.Canvas {

 //---------------------------------------------------------
 //   static fields
 /**
  * color constant
  */
 public static final int BLACK = 0;
 /**
  * color constant
  */
 public static final int WHITE = 0xffffff;
 //---------------------------------------------------------
 //   instance fields
 /**
  * a handle to the display.
  */
 private Display myDisplay;
 /**
  * The data object that describes the maze configuration.
  */
 private Grid myGrid;
 /**
  * Whether or not the currently displayed maze has 
  * been completed.
  */
 private boolean myGameOver = false;
 /**
  * maze dimension: the width of the maze walls.
  */
 private int mySquareSize;
 /**
  * maze dimension: the maximum width possible for the maze walls.
  */
 private int myMaxSquareSize;
 /**
  * maze dimension: the minimum width possible for the maze walls.
  */
 private int myMinSquareSize;
 /**
  * top corner of the display: x-coordiate
  */
 private int myStartX = 0;
 /**
  * top corner of the display: y-coordinate
  */
 private int myStartY = 0;
 /**
  * how many rows the display is divided into.
  */
 private int myGridHeight;
 /**
  * how many columns the display is divided into.
  */
 private int myGridWidth;
 /**
  * the maximum number columns the display can be divided into.
  */
 private int myMaxGridWidth;
 /**
  * the minimum number columns the display can be divided into.
  */
 private int myMinGridWidth;
 /**
  * previous location of the player in the maze: x-coordiate
  * (in terms of the coordinates of the maze grid, NOT in terms 
  * of the coordinate system of the Canvas.)
  */
 private int myOldX = 1;
 /**
  * previous location of the player in the maze: y-coordinate
  * (in terms of the coordinates of the maze grid, NOT in terms 
  * of the coordinate system of the Canvas.)
  */
 private int myOldY = 1;
 /**
  * current location of the player in the maze: x-coordiate
  * (in terms of the coordinates of the maze grid, NOT in terms 
  * of the coordinate system of the Canvas.)
  */
 private int myPlayerX = 1;
 /**
  * current location of the player in the maze: y-coordinate
  * (in terms of the coordinates of the maze grid, NOT in terms 
  * of the coordinate system of the Canvas.)
  */
 private int myPlayerY = 1;
 //-----------------------------------------------------
 //    gets / sets
 /**
  * Changes the width of the maze walls and calculates how 
  * this change affects the number of rows and columns 
  * the maze can have.
  * @return the number of columns now that the the 
  *         width of the columns has been updated.
  */
 int setColWidth(int colWidth) {
   if(colWidth < 2) {
     mySquareSize = 2;
   } else {
     mySquareSize = colWidth;
   }
   myGridWidth = getWidth() / mySquareSize;
   if(myGridWidth % 2 == 0) {
     myGridWidth -= 1;
   }
   myGridHeight = getHeight() / mySquareSize;
   if(myGridHeight % 2 == 0) {
     myGridHeight -= 1;
   }
   myGrid = null;
   return(myGridWidth);
 }
 /**
  * @return the minimum width possible for the maze walls.
  */
 int getMinColWidth() {
   return(myMinSquareSize);
 }
 /**
  * @return the maximum width possible for the maze walls.
  */
 int getMaxColWidth() {
   return(myMaxSquareSize);
 }
 /**
  * @return the maximum number of columns the display can be divided into.
  */
 int getMaxNumCols() {
   return(myMaxGridWidth);
 }
 /**
  * @return the width of the maze walls.
  */
 int getColWidth() {
   return(mySquareSize);
 }
 /**
  * @return the number of maze columns the display is divided into.
  */
 int getNumCols() {
   return(myGridWidth);
 }
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Constructor performs size calculations.
  * @throws Exception if the display size is too 
  *         small to make a maze.
  */
 public MazeCanvas(Display d) throws Exception {
   myDisplay = d;
   // a few calculations to make the right maze 
   // for the current display.
   int width = getWidth();
   int height = getHeight();
   // tests indicate that 5 is a good default square size, 
   // but the user can change it...
   mySquareSize = 5;
   myMinSquareSize = 3;
   myMaxGridWidth = width / myMinSquareSize;
   if(myMaxGridWidth % 2 == 0) {
     myMaxGridWidth -= 1;
   }
   myGridWidth = width / mySquareSize;
   if(myGridWidth % 2 == 0) {
     myGridWidth -= 1;
   }
   myGridHeight = height / mySquareSize;
   if(myGridHeight % 2 == 0) {
     myGridHeight -= 1;
   }
   myMinGridWidth = 15;
   myMaxSquareSize = width / myMinGridWidth;
   if(myMaxSquareSize > height / myMinGridWidth) {
     myMaxSquareSize = height / myMinGridWidth;
   }
   // if the display is too small to make a reasonable maze, 
   // then we throw an Exception
   if(myMaxSquareSize < mySquareSize) {
     throw(new Exception("Display too small"));
   }
 }
 /**
  * This is called as soon as the application begins.
  */
 void start() {
   myDisplay.setCurrent(this);
   repaint();
 }
 /**
  * discard the current maze and draw a new one.
  */
 void newMaze() {
   myGameOver = false;
   // throw away the current maze.
   myGrid = null;
   // set the player back to the beginning of the maze.
   myPlayerX = 1;
   myPlayerY = 1;
   myOldX = 1;
   myOldY = 1;
   myDisplay.setCurrent(this);
   // paint the new maze
   repaint();
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * Create and display a maze if necessary, otherwise just 
  * move the player.  Since the motion in this game is 
  * very simple, it is not necessary to repaint the whole 
  * maze each time, just the player + erase the square 
  * that the player just left..
  */
 protected void paint(Graphics g) {
   // If there is no current maze, create one and draw it.
   if(myGrid == null) {
     int width = getWidth();
     int height = getHeight();
     // create the underlying data of the maze.
     myGrid = new Grid(myGridWidth, myGridHeight);
     // draw the maze:
     // loop through the grid data and color each square the 
     // right color
     for(int i = 0; i < myGridWidth; i++) {
 for(int j = 0; j < myGridHeight; j++) {
   if(myGrid.mySquares[i][j] == 0) {
     g.setColor(BLACK);
   } else {
     g.setColor(WHITE);
   }
   // fill the square with the appropriate color
   g.fillRect(myStartX + (i*mySquareSize), 
        myStartY + (j*mySquareSize), 
        mySquareSize, mySquareSize);
 }
     }
     // fill the extra space outside of the maze
     g.setColor(BLACK);
     g.fillRect(myStartX + ((myGridWidth-1) * mySquareSize), 
    myStartY, width, height);
     // erase the exit path: 
     g.setColor(WHITE);
     g.fillRect(myStartX + ((myGridWidth-1) * mySquareSize), 
    myStartY + ((myGridHeight-2) * mySquareSize), width, height);
     // fill the extra space outside of the maze
     g.setColor(BLACK);
     g.fillRect(myStartX, 
    myStartY + ((myGridHeight-1) * mySquareSize), width, height);
   }
   // draw the player (red): 
   g.setColor(255, 0, 0);
   g.fillRoundRect(myStartX + (mySquareSize)*myPlayerX, 
       myStartY + (mySquareSize)*myPlayerY, 
       mySquareSize, mySquareSize, 
       mySquareSize, mySquareSize);
   // erase the previous location
   if((myOldX != myPlayerX) || (myOldY != myPlayerY)) {
     g.setColor(WHITE);
     g.fillRect(myStartX + (mySquareSize)*myOldX, 
       myStartY + (mySquareSize)*myOldY, 
       mySquareSize, mySquareSize);
   }
   // if the player has reached the end of the maze, 
   // we display the end message.
   if(myGameOver) {
     // perform some calculations to place the text correctly:
     int width = getWidth();
     int height = getHeight();
     Font font = g.getFont();
     int fontHeight = font.getHeight();
     int fontWidth = font.stringWidth("Maze Completed");
     g.setColor(WHITE);
     g.fillRect((width - fontWidth)/2, (height - fontHeight)/2,
          fontWidth + 2, fontHeight);
     // write in red
     g.setColor(255, 0, 0);
     g.setFont(font);
     g.drawString("Maze Completed", (width - fontWidth)/2, 
      (height - fontHeight)/2,
      g.TOP|g.LEFT);
   }
 }
 /**
  * Move the player.
  */
 public void keyPressed(int keyCode) {  
   if(! myGameOver) {
     int action = getGameAction(keyCode);   
     switch (action) {
     case LEFT:
 if((myGrid.mySquares[myPlayerX-1][myPlayerY] == 1) && 
    (myPlayerX != 1)) {
   myOldX = myPlayerX;
   myOldY = myPlayerY;
   myPlayerX -= 2;
   repaint();
 }
 break;
     case RIGHT:
 if(myGrid.mySquares[myPlayerX+1][myPlayerY] == 1) {
   myOldX = myPlayerX;
   myOldY = myPlayerY;
   myPlayerX += 2;
   repaint();
 } else if((myPlayerX == myGrid.mySquares.length - 2) && 
     (myPlayerY == myGrid.mySquares[0].length - 2)) {
   myOldX = myPlayerX;
   myOldY = myPlayerY;
   myPlayerX += 2;
   myGameOver = true;
   repaint();
 }
 break;
     case UP:
 if(myGrid.mySquares[myPlayerX][myPlayerY-1] == 1) {
   myOldX = myPlayerX;
   myOldY = myPlayerY;
   myPlayerY -= 2;
   repaint();
 }
 break;
     case DOWN:
 if(myGrid.mySquares[myPlayerX][myPlayerY+1] == 1) {
   myOldX = myPlayerX;
   myOldY = myPlayerY;
   myPlayerY += 2;
   repaint();
 }
 break;
     }
   }
 }

} /**

* This is the screen that allows the user to modify the 
* width of the maze walls..
*
* @author Carol Hamer
*/

class SelectScreen extends Form

 implements ItemStateListener, CommandListener  {
 //----------------------------------------------------------------
 //  fields
 /**
  * The "Done" button to exit this screen and return to the maze.
  */
 private Command myExitCommand = new Command("Done", Command.EXIT, 1);
 /**
  * The gague that modifies the width of the maze walls.
  */
 private Gauge myWidthGauge;
 /**
  * The gague that displays the number of columns of the maze.
  */
 private Gauge myColumnsGauge;
 /**
  * A handle to the main game canvas.
  */
 private MazeCanvas myCanvas;
 //----------------------------------------------------------------
 //  initialization
 /**
  * Create the gagues and place them on the screen.
  */
 public SelectScreen(MazeCanvas canvas) {
   super("Size Preferences");
   addCommand(myExitCommand);
   setCommandListener(this);
   myCanvas = canvas;
   setItemStateListener(this);
   myWidthGauge = new Gauge("Column Width", true, 
          myCanvas.getMaxColWidth(), 
          myCanvas.getColWidth());
   myColumnsGauge = new Gauge("Number of Columns", false,  
            myCanvas.getMaxNumCols(), 
            myCanvas.getNumCols());
   // Warning: the setLayout method does not exist in 
   // MIDP 1.4.  If there is any chance that a target 
   // device will be using MIDP 1.4, comment out the 
   // following two lines:
   //myWidthGauge.setLayout(Item.LAYOUT_CENTER);
   //myColumnsGauge.setLayout(Item.LAYOUT_CENTER);
   append(myWidthGauge);
   append(myColumnsGauge);
 }
 //----------------------------------------------------------------
 //  implementation of ItemStateListener
 /**
  * Respond to the user changing the width.
  */
 public void itemStateChanged(Item item) {
   if(item == myWidthGauge) {
     int val = myWidthGauge.getValue();
     if(val < myCanvas.getMinColWidth()) {
 myWidthGauge.setValue(myCanvas.getMinColWidth());
     } else {
 int numCols = myCanvas.setColWidth(val);
 myColumnsGauge.setValue(numCols);
     }
   }
 }
 //----------------------------------------------------------------
 //  implementation of CommandListener
 /*
  * Respond to a command issued on this screen.
  * (either reset or exit).
  */
 public void commandAction(Command c, Displayable s) {
   if(c == myExitCommand) {
     myCanvas.newMaze();
   }
 }
 

}

/**

* This class contains the data necessary to draw the maze.
*
* @author Carol Hamer
*/

class Grid {

 /**
  * Random number generator to create a random maze.
  */
 private Random myRandom = new Random();
 /**
  * data for which squares are filled and which are blank.
  * 0 = black
  * 1 = white
  * values higher than 1 are used during the maze creation 
  * algorithm.
  * 2 = the square could possibly be appended to the maze this round.
  * 3 = the square"s color is not yet decided, and the square is 
  * not close enough to be appended to the maze this round.
  */
 int[][] mySquares;
 //--------------------------------------------------------
 //  maze generation methods
 /**
  * Create a new maze.
  */
 public Grid(int width, int height) {
   mySquares = new int[width][height];
   // initialize all of the squares to white except a lattice 
   // framework of black squares.
   for(int i = 1; i < width - 1; i++) {
     for(int j = 1; j < height - 1; j++) {
 if((i % 2 == 1) || (j % 2 == 1)) {
   mySquares[i][j] = 1;
 }
     }
   }
   // the entrance to the maze is at (0,1).
   mySquares[0][1] = 1;
   createMaze();
 }
 /**
  * This method randomly generates the maze.
  */
 private void createMaze() {
   // create an initial framework of black squares.
   for(int i = 1; i < mySquares.length - 1; i++) {
     for(int j = 1; j < mySquares[i].length - 1; j++) {
 if((i + j) % 2 == 1) {
   mySquares[i][j] = 0;
 }
     }
   }
   // initialize the squares that can be either black or white 
   // depending on the maze.
   // first we set the value to 3 which means undecided.
   for(int i = 1; i < mySquares.length - 1; i+=2) {
     for(int j = 1; j < mySquares[i].length - 1; j+=2) {
 mySquares[i][j] = 3;
     }
   }
   // Then those squares that can be selected to be open 
   // (white) paths are given the value of 2.  
   // We randomly select the square where the tree of maze 
   // paths will begin.  The maze is generated starting from 
   // this initial square and branches out from here in all 
   // directions to fill the maze grid.  
   Vector possibleSquares = new Vector(mySquares.length 
         * mySquares[0].length);
   int[] startSquare = new int[2];
   startSquare[0] = getRandomInt(mySquares.length / 2)*2 + 1;
   startSquare[1] = getRandomInt(mySquares[0].length / 2)*2 + 1;
   mySquares[startSquare[0]][startSquare[1]] = 2;
   possibleSquares.addElement(startSquare);
   // Here we loop to select squares one by one to append to 
   // the maze pathway tree.
   while(possibleSquares.size() > 0) {
     // the next square to be joined on is selected randomly.
     int chosenIndex = getRandomInt(possibleSquares.size());
     int[] chosenSquare = (int[])possibleSquares.elementAt(chosenIndex);
     // we set the chosen square to white and then 
     // remove it from the list of possibleSquares (i.e. squares 
     // that can possibly be added to the maze), and we link 
     // the new square to the maze.
     mySquares[chosenSquare[0]][chosenSquare[1]] = 1;
     possibleSquares.removeElementAt(chosenIndex);
     link(chosenSquare, possibleSquares);
   }
   // now that the maze has been completely generated, we 
   // throw away the objects that were created during the 
   // maze creation algorithm and reclaim the memory.
   possibleSquares = null;
   System.gc();
 }
 /**
  * internal to createMaze.  Checks the four squares surrounding 
  * the chosen square.  Of those that are already connected to 
  * the maze, one is randomly selected to be joined to the 
  * current square (to attach the current square to the 
  * growing maze).  Those squares that were not previously in 
  * a position to be joined to the maze are added to the list 
  * of "possible" squares (that could be chosen to be attached 
  * to the maze in the next round).
  */
 private void link(int[] chosenSquare, Vector possibleSquares) {
   int linkCount = 0;
   int i = chosenSquare[0];
   int j = chosenSquare[1];
   int[] links = new int[8];
   if(i >= 3) {
     if(mySquares[i - 2][j] == 1) {
 links[2*linkCount] = i - 1;
 links[2*linkCount + 1] = j;
 linkCount++;
     } else if(mySquares[i - 2][j] == 3) {
 mySquares[i - 2][j] = 2;
 int[] newSquare = new int[2];
 newSquare[0] = i - 2;
 newSquare[1] = j;
 possibleSquares.addElement(newSquare);
     }
   }
   if(j + 3 <= mySquares[i].length) {
     if(mySquares[i][j + 2] == 3) {
 mySquares[i][j + 2] = 2;
 int[] newSquare = new int[2];
 newSquare[0] = i;
 newSquare[1] = j + 2;
 possibleSquares.addElement(newSquare);
     } else if(mySquares[i][j + 2] == 1) {
 links[2*linkCount] = i;
 links[2*linkCount + 1] = j + 1;
 linkCount++;
     }
   } 
   if(j >= 3) {
     if(mySquares[i][j - 2] == 3) {
 mySquares[i][j - 2] = 2;
 int[] newSquare = new int[2];
 newSquare[0] = i;
 newSquare[1] = j - 2;
 possibleSquares.addElement(newSquare);
     } else if(mySquares[i][j - 2] == 1) {
 links[2*linkCount] = i;
 links[2*linkCount + 1] = j - 1;
 linkCount++;
     }
   } 
   if(i + 3 <= mySquares.length) {
     if(mySquares[i + 2][j] == 3) {
 mySquares[i + 2][j] = 2;
 int[] newSquare = new int[2];
 newSquare[0] = i + 2;
 newSquare[1] = j;
 possibleSquares.addElement(newSquare);
     } else if(mySquares[i + 2][j] == 1) {
 links[2*linkCount] = i + 1;
 links[2*linkCount + 1] = j;
 linkCount++;
     }
   } 
   if(linkCount > 0) {
     int linkChoice = getRandomInt(linkCount);
     int linkX = links[2*linkChoice];
     int linkY = links[2*linkChoice + 1];
     mySquares[linkX][linkY] = 1;
     int[] removeSquare = new int[2];
     removeSquare[0] = linkX;
     removeSquare[1] = linkY;
     possibleSquares.removeElement(removeSquare);
   }
 }
 /**
  * a randomization utility. 
  * @param upper the upper bound for the random int.
  * @return a random non-negative int less than the bound upper.
  */
 public int getRandomInt(int upper) {
   int retVal = myRandom.nextInt() % upper;
   if(retVal < 0) {
     retVal += upper;
   }
   return(retVal);
 }

}


      </source>
   
  
 
  



Sweep

   <source lang="java">

/* Wireless Java 2nd edition Jonathan Knudsen Publisher: Apress ISBN: 1590590775

  • /

import javax.microedition.lcdui.*; import javax.microedition.midlet.*; public class Sweep extends MIDlet {

 public void startApp() {
   final SweepCanvas sweeper = new SweepCanvas();
   sweeper.start();
   
   sweeper.addCommand(new Command("Exit", Command.EXIT, 0));
   sweeper.setCommandListener(new CommandListener() {
     public void commandAction(Command c, Displayable s) {
       sweeper.stop();
       notifyDestroyed();
     }
   });
   
   Display.getDisplay(this).setCurrent(sweeper);
 }
 
 public void pauseApp() {}
 public void destroyApp(boolean unconditional) {}

} class SweepCanvas extends Canvas implements Runnable {

 private boolean mTrucking;
 private int mTheta;
 private int mBorder;
 private int mDelay;
 
 public SweepCanvas() {
   mTheta = 0;
   mBorder = 10;
   mDelay = 50;
 }
 
 public void start() {
   mTrucking = true;
   Thread t = new Thread(this);
   t.start();
 }
 
 public void stop() {
   mTrucking = false;
 }
   
 public void paint(Graphics g) {
   int width = getWidth();
   int height = getHeight();
   
   // Clear the Canvas.
   g.setGrayScale(255);
   g.fillRect(0, 0, width - 1, height - 1);
   
   int x = mBorder;
   int y = mBorder;
   int w = width - mBorder * 2;
   int h = height - mBorder * 2;
   for (int i = 0; i < 8; i++) {
     g.setGrayScale((8 - i) * 32 - 16);
     g.fillArc(x, y, w, h, mTheta + i * 10, 10);
     g.fillArc(x, y, w, h, (mTheta + 180) % 360 + i * 10, 10);
   }
 }
 
 public void run() {
   while (mTrucking) {
     mTheta = (mTheta + 1) % 360;
     repaint();
     try { Thread.sleep(mDelay); }
     catch (InterruptedException ie) {}
   }
 }

}


      </source>
   
  
 
  



Sweep Game

   <source lang="java">

/* Wireless Java 2nd edition Jonathan Knudsen Publisher: Apress ISBN: 1590590775

  • /

import javax.microedition.lcdui.*; import javax.microedition.lcdui.game.*; import javax.microedition.midlet.*; public class SweepGame extends MIDlet {

 public void startApp() {
   final SweepGameCanvas sweeper = new SweepGameCanvas();
   sweeper.start();
   
   sweeper.addCommand(new Command("Exit", Command.EXIT, 0));
   sweeper.setCommandListener(new CommandListener() {
     public void commandAction(Command c, Displayable s) {
       sweeper.stop();
       notifyDestroyed();
     }
   });
   
   Display.getDisplay(this).setCurrent(sweeper);
 }
 
 public void pauseApp() {}
 public void destroyApp(boolean unconditional) {}

} class SweepGameCanvas extends GameCanvas implements Runnable {

 private boolean mTrucking;
 private int mTheta;
 private int mBorder;
 private int mDelay;
 
 public SweepGameCanvas() {
   super(true);
   mTheta = 0;
   mBorder = 10;
   mDelay = 50;
 }
 
 public void start() {
   mTrucking = true;
   Thread t = new Thread(this);
   t.start();
 }
 
 public void stop() {
   mTrucking = false;
 }
   
 public void render(Graphics g) {
   int width = getWidth();
   int height = getHeight();
   
   // Clear the Canvas.
   g.setGrayScale(255);
   g.fillRect(0, 0, width - 1, height - 1);
   
   int x = mBorder;
   int y = mBorder;
   int w = width - mBorder * 2;
   int h = height - mBorder * 2;
   for (int i = 0; i < 8; i++) {
     g.setGrayScale((8 - i) * 32 - 16);
     g.fillArc(x, y, w, h, mTheta + i * 10, 10);
     g.fillArc(x, y, w, h, (mTheta + 180) % 360 + i * 10, 10);
   }
 }
 
 public void run() {
   Graphics g = getGraphics();
   while (mTrucking) {
     mTheta = (mTheta + 1) % 360;
     render(g);
     flushGraphics();
     try { Thread.sleep(mDelay); }
     catch (InterruptedException ie) {}
   }
 }

}


      </source>
   
  
 
  



Tumbleweed game

   <source lang="java">

/*

Title:  J2ME Games With MIDP2
Authors:  Carol Hamer
Publisher:  Apress
ISBN:   1590593820
*/

import javax.microedition.media.*; import javax.microedition.media.control.*; import java.util.Random; import javax.microedition.lcdui.game.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; /**

* This is the main class of the tumbleweed game.
* 
* @author Carol Hamer
*/

public class Jump extends MIDlet implements CommandListener {

 //---------------------------------------------------------
 //   commands
 /**
  * the command to end the game.
  */
 private Command myExitCommand = new Command("Exit", Command.EXIT, 99);
 /**
  * the command to start moving when the game is paused.
  */
 private Command myGoCommand = new Command("Go", Command.SCREEN, 1);
 /**
  * the command to pause the game.
  */
 private Command myPauseCommand = new Command("Pause", Command.SCREEN, 1);
 /**
  * the command to start a new game.
  */
 private Command myNewCommand = new Command("Play Again", Command.SCREEN, 1);
 /**
  * The command to start/pause the music. (This command may appear in a menu)
  */
 private Command myMusicCommand = new Command("Music", Command.SCREEN, 2);
 //---------------------------------------------------------
 //   game object fields
 /**
  * the the canvas that all of the game will be drawn on.
  */
 private JumpCanvas myCanvas;
 //---------------------------------------------------------
 //   thread fields
 /**
  * the thread that advances the cowboy.
  */
 private GameThread myGameThread;
 /**
  * The class that plays music if the user wants.
  */
 //private MusicMaker myMusicMaker;
 private ToneControlMusicMaker myMusicMaker;
 /**
  * The thread tha sets tumbleweeds in motion at random intervals.
  */
 private TumbleweedThread myTumbleweedThread;
 /**
  * if the user has paused the game.
  */
 private boolean myGamePause;
 /**
  * if the game is paused because it is hidden.
  */
 private boolean myHiddenPause;
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Initialize the canvas and the commands.
  */
 public Jump() {
   try {
     myCanvas = new JumpCanvas(this);
     myCanvas.addCommand(myExitCommand);
     myCanvas.addCommand(myMusicCommand);
     myCanvas.addCommand(myPauseCommand);
     myCanvas.setCommandListener(this);
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 /**
  * Switch the command to the play again command.
  */
 void setNewCommand() {
   myCanvas.removeCommand(myPauseCommand);
   myCanvas.removeCommand(myGoCommand);
   myCanvas.addCommand(myNewCommand);
 }
 /**
  * Switch the command to the go command.
  */
 private void setGoCommand() {
   myCanvas.removeCommand(myPauseCommand);
   myCanvas.removeCommand(myNewCommand);
   myCanvas.addCommand(myGoCommand);
 }
 /**
  * Switch the command to the pause command.
  */
 private void setPauseCommand() {
   myCanvas.removeCommand(myNewCommand);
   myCanvas.removeCommand(myGoCommand);
   myCanvas.addCommand(myPauseCommand);
 }
 //----------------------------------------------------------------
 //  implementation of MIDlet
 // these methods may be called by the application management
 // software at any time, so we always check fields for null
 // before calling methods on them.
 /**
  * Start the application.
  */
 public void startApp() throws MIDletStateChangeException {
   try {
     if (myCanvas != null) {
       myCanvas.start();
       myCanvas.flushKeys();
       systemStartThreads();
     }
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 /**
  * stop and throw out the garbage.
  */
 public void destroyApp(boolean unconditional)
     throws MIDletStateChangeException {
   try {
     stopThreads();
     myCanvas = null;
     System.gc();
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 /**
  * request the game to pause. This method is called by the application
  * management software, not in response to a user pausing the game.
  */
 public void pauseApp() {
   try {
     if (myCanvas != null) {
       setGoCommand();
       systemPauseThreads();
     }
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 //----------------------------------------------------------------
 //  implementation of CommandListener
 /*
  * Respond to a command issued on the Canvas. (either reset or exit).
  */
 public void commandAction(Command c, Displayable s) {
   try {
     if (c == myGoCommand) {
       myCanvas.removeCommand(myGoCommand);
       myCanvas.addCommand(myPauseCommand);
       myCanvas.flushKeys();
       userStartThreads();
     } else if (c == myPauseCommand) {
       myCanvas.removeCommand(myPauseCommand);
       myCanvas.addCommand(myGoCommand);
       userPauseThreads();
     } else if (c == myNewCommand) {
       myCanvas.removeCommand(myNewCommand);
       myCanvas.addCommand(myPauseCommand);
       System.gc();
       myCanvas.reset();
       myCanvas.flushKeys();
       myHiddenPause = false;
       myGamePause = false;
       startThreads();
     } else if (c == myMusicCommand) {
       if (myMusicMaker != null) {
         myMusicMaker.toggle();
         myCanvas.repaint();
         myCanvas.serviceRepaints();
       }
     } else if ((c == myExitCommand)/* || (c == Alert.DISMISS_COMMAND)*/) {
       try {
         destroyApp(false);
         notifyDestroyed();
       } catch (MIDletStateChangeException ex) {
       }
     }
   } catch (Exception e) {
     errorMsg(e);
   }
 }
 //-------------------------------------------------------
 //  thread methods
 /**
  * start up all of the game"s threads. Creates them if necessary. to be
  * called when the user hits the go command.
  */
 private synchronized void userStartThreads() throws Exception {
   myGamePause = false;
   if (!myHiddenPause) {
     startThreads();
   }
 }
 /**
  * start up all of the game"s threads. Creates them if necessary. used by
  * showNotify
  */
 synchronized void systemStartThreads() throws Exception {
   myHiddenPause = false;
   if (!myGamePause) {
     startThreads();
   }
 }
 /**
  * start up all of the game"s threads. Creates them if necessary. internal
  * version. note: if this were synchronized, whould it cause deadlock?
  */
 private void startThreads() throws Exception {
   if (myGameThread == null) {
     myGameThread = new GameThread(myCanvas);
     myGameThread.start();
   } else {
     myGameThread.resumeGame();
   }
   if (myTumbleweedThread == null) {
     myTumbleweedThread = new TumbleweedThread(myCanvas);
     myTumbleweedThread.start();
   } else {
     myTumbleweedThread.resumeGame();
   }
   if (myMusicMaker == null) {
     myMusicMaker = new ToneControlMusicMaker();
     //myMusicMaker = new MusicMaker();
     myMusicMaker.start();
   } else {
     myMusicMaker.resumeGame();
   }
 }
 /**
  * Pause all of the threads started by this game. to be called when the user
  * hits the pause command.
  */
 synchronized void userPauseThreads() {
   myGamePause = true;
   pauseThreads();
 }
 /**
  * Pause all of the threads started by this game. used by hideNotify
  */
 void systemPauseThreads() {
   myHiddenPause = true;
   pauseThreads();
 }
 /**
  * start up all of the game"s threads. Creates them if necessary. internal
  * version. note: if this were synchronized, whould it cause deadlock?
  */
 private void pauseThreads() {
   if (myGameThread != null) {
     myGameThread.pauseGame();
   }
   if (myTumbleweedThread != null) {
     myTumbleweedThread.pauseGame();
   }
   if (myMusicMaker != null) {
     myMusicMaker.pauseGame();
   }
 }
 /**
  * Stop all of the threads started by this game and delete them as they are
  * no longer usable.
  */
 private synchronized void stopThreads() {
   if (myGameThread != null) {
     myGameThread.requestStop();
   }
   if (myTumbleweedThread != null) {
     myTumbleweedThread.requestStop();
   }
   if (myMusicMaker != null) {
     myMusicMaker.requestStop();
   }
   myGameThread = null;
   myTumbleweedThread = null;
   myMusicMaker = null;
 }
 //-------------------------------------------------------
 //  error methods
 /**
  * Converts an exception to a message and displays the message..
  */
 void errorMsg(Exception e) {
   if (e.getMessage() == null) {
     errorMsg(e.getClass().getName());
   } else {
     errorMsg(e.getClass().getName() + ":" + e.getMessage());
   }
 }
 /**
  * Displays an error message alert if something goes wrong.
  */
 void errorMsg(String msg) {
   Alert errorAlert = new Alert("error", msg, null, AlertType.ERROR);
   errorAlert.setCommandListener(this);
   errorAlert.setTimeout(Alert.FOREVER);
   Display.getDisplay(this).setCurrent(errorAlert);
 }

} /**

* This class is the display of the game.
* 
* @author Carol Hamer
*/

class JumpCanvas extends javax.microedition.lcdui.game.GameCanvas {

 //---------------------------------------------------------
 //   dimension fields
 //  (constant after initialization)
 /**
  * the height of the green region below the ground.
  */
 static final int GROUND_HEIGHT = 32;
 /**
  * a screen dimension.
  */
 static final int CORNER_X = 0;
 /**
  * a screen dimension.
  */
 static final int CORNER_Y = 0;
 /**
  * a screen dimension.
  */
 static int DISP_WIDTH;
 /**
  * a screen dimension.
  */
 static int DISP_HEIGHT;
 /**
  * a font dimension.
  */
 static int FONT_HEIGHT;
 /**
  * the default font.
  */
 static Font FONT;
 /**
  * a font dimension.
  */
 static int SCORE_WIDTH;
 /**
  * The width of the string that displays the time, saved for placement of
  * time display.
  */
 static int TIME_WIDTH;
 /**
  * color constant
  */
 public static final int BLACK = 0;
 /**
  * color constant
  */
 public static final int WHITE = 0xffffff;
 //---------------------------------------------------------
 //   game object fields
 /**
  * a handle to the display.
  */
 private Display myDisplay;
 /**
  * a handle to the MIDlet object (to keep track of buttons).
  */
 private Jump myJump;
 /**
  * the LayerManager that handles the game graphics.
  */
 private JumpManager myManager;
 /**
  * whether or not the game has ended.
  */
 private boolean myGameOver;
 /**
  * the player"s score.
  */
 private int myScore = 0;
 /**
  * How many ticks we start with.
  */
 private int myInitialGameTicks = 950;
 /**
  * this is saved to determine if the time string needs to be recomputed.
  */
 private int myOldGameTicks = myInitialGameTicks;
 /**
  * the number of game ticks that have passed.
  */
 private int myGameTicks = myOldGameTicks;
 /**
  * we save the time string to avoid recreating it unnecessarily.
  */
 private static String myInitialString = "1:00";
 /**
  * we save the time string to avoid recreating it unnecessarily.
  */
 private String myTimeString = myInitialString;
 //-----------------------------------------------------
 //    gets/sets
 /**
  * This is called when the game ends.
  */
 void setGameOver() {
   myGameOver = true;
   myJump.userPauseThreads();
 }
 /**
  * @return a handle to the tumbleweed objects.
  */
 Tumbleweed[] getTumbleweeds() {
   return (myManager.getTumbleweeds());
 }
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Constructor sets the data, performs dimension calculations, and creates
  * the graphical objects.
  */
 public JumpCanvas(Jump midlet) throws Exception {
   super(false);
   myDisplay = Display.getDisplay(midlet);
   myJump = midlet;
   // calculate the dimensions
   DISP_WIDTH = getWidth();
   DISP_HEIGHT = getHeight();
   Display disp = Display.getDisplay(myJump);
   if (disp.numColors() < 256) {
     throw (new Exception("game requires 256 shades"));
   }
   if ((DISP_WIDTH < 150) || (DISP_HEIGHT < 170)) {
     throw (new Exception("Screen too small"));
   }
   if ((DISP_WIDTH > 250) || (DISP_HEIGHT > 250)) {
     throw (new Exception("Screen too large"));
   }
   FONT = getGraphics().getFont();
   FONT_HEIGHT = FONT.getHeight();
   SCORE_WIDTH = FONT.stringWidth("Score: 000");
   TIME_WIDTH = FONT.stringWidth("Time: " + myInitialString);
   if (myManager == null) {
     myManager = new JumpManager(CORNER_X, CORNER_Y + FONT_HEIGHT * 2,
         DISP_WIDTH, DISP_HEIGHT - FONT_HEIGHT * 2 - GROUND_HEIGHT);
   }
 }
 /**
  * This is called as soon as the application begins.
  */
 void start() {
   myGameOver = false;
   myDisplay.setCurrent(this);
   repaint();
 }
 /**
  * sets all variables back to their initial positions.
  */
 void reset() {
   myManager.reset();
   myScore = 0;
   myGameOver = false;
   myGameTicks = myInitialGameTicks;
   myOldGameTicks = myInitialGameTicks;
   repaint();
 }
 /**
  * clears the key states.
  */
 void flushKeys() {
   getKeyStates();
 }
 /**
  * pause the game when it"s hidden.
  */
 protected void hideNotify() {
   try {
     myJump.systemPauseThreads();
   } catch (Exception oe) {
     myJump.errorMsg(oe);
   }
 }
 /**
  * When it comes back into view, unpause it.
  */
 protected void showNotify() {
   try {
     myJump.systemStartThreads();
   } catch (Exception oe) {
     myJump.errorMsg(oe);
   }
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * paint the game graphic on the screen.
  */
 public void paint(Graphics g) {
   // clear the screen:
   g.setColor(WHITE);
   g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, DISP_HEIGHT);
   // color the grass green
   g.setColor(0, 255, 0);
   g.fillRect(CORNER_X, CORNER_Y + DISP_HEIGHT - GROUND_HEIGHT,
       DISP_WIDTH, DISP_HEIGHT);
   // paint the layer manager:
   try {
     myManager.paint(g);
   } catch (Exception e) {
     myJump.errorMsg(e);
   }
   // draw the time and score
   g.setColor(BLACK);
   g.setFont(FONT);
   g.drawString("Score: " + myScore, (DISP_WIDTH - SCORE_WIDTH) / 2,
       DISP_HEIGHT + 5 - GROUND_HEIGHT, g.TOP | g.LEFT);
   g.drawString("Time: " + formatTime(), (DISP_WIDTH - TIME_WIDTH) / 2,
       CORNER_Y + FONT_HEIGHT, g.TOP | g.LEFT);
   // write game over if the game is over
   if (myGameOver) {
     myJump.setNewCommand();
     // clear the top region:
     g.setColor(WHITE);
     g.fillRect(CORNER_X, CORNER_Y, DISP_WIDTH, FONT_HEIGHT * 2 + 1);
     int goWidth = FONT.stringWidth("Game Over");
     g.setColor(BLACK);
     g.setFont(FONT);
     g.drawString("Game Over", (DISP_WIDTH - goWidth) / 2, CORNER_Y
         + FONT_HEIGHT, g.TOP | g.LEFT);
   }
 }
 /**
  * a simple utility to make the number of ticks look like a time...
  */
 public String formatTime() {
   if ((myGameTicks / 16) + 1 != myOldGameTicks) {
     myTimeString = "";
     myOldGameTicks = (myGameTicks / 16) + 1;
     int smallPart = myOldGameTicks % 60;
     int bigPart = myOldGameTicks / 60;
     myTimeString += bigPart + ":";
     if (smallPart / 10 < 1) {
       myTimeString += "0";
     }
     myTimeString += smallPart;
   }
   return (myTimeString);
 }
 //-------------------------------------------------------
 //  game movements
 /**
  * Tell the layer manager to advance the layers and then update the display.
  */
 void advance() {
   myGameTicks--;
   myScore += myManager.advance(myGameTicks);
   if (myGameTicks == 0) {
     setGameOver();
   }
   // paint the display
   try {
     paint(getGraphics());
     flushGraphics();
   } catch (Exception e) {
     myJump.errorMsg(e);
   }
 }
 /**
  * Respond to keystrokes.
  */
 public void checkKeys() {
   if (!myGameOver) {
     int keyState = getKeyStates();
     if ((keyState & LEFT_PRESSED) != 0) {
       myManager.setLeft(true);
     }
     if ((keyState & RIGHT_PRESSED) != 0) {
       myManager.setLeft(false);
     }
     if ((keyState & UP_PRESSED) != 0) {
       myManager.jump();
     }
   }
 }

} /**

* This class draws the background grass.
* 
* @author Carol Hamer
*/

class Grass extends TiledLayer {

 //---------------------------------------------------------
 //    dimension fields
 //  (constant after initialization)
 /**
  * The width of the square tiles that make up this layer..
  */
 static final int TILE_WIDTH = 20;
 /**
  * This is the order that the frames should be displayed for the animation.
  */
 static final int[] FRAME_SEQUENCE = { 2, 3, 2, 4 };
 /**
  * This gives the number of squares of grass to put along the bottom of the
  * screen.
  */
 static int COLUMNS;
 /**
  * After how many tiles does the background repeat.
  */
 static final int CYCLE = 5;
 /**
  * the fixed Y coordinate of the strip of grass.
  */
 static int TOP_Y;
 //---------------------------------------------------------
 //    instance fields
 /**
  * Which tile we are currently on in the frame sequence.
  */
 private int mySequenceIndex = 0;
 /**
  * The index to use in the static tiles array to get the animated tile..
  */
 private int myAnimatedTileIndex;
 //---------------------------------------------------------
 //   gets / sets
 /**
  * Takes the width of the screen and sets my columns to the correct
  * corresponding number
  */
 static int setColumns(int screenWidth) {
   COLUMNS = ((screenWidth / 20) + 1) * 3;
   return (COLUMNS);
 }
 //---------------------------------------------------------
 //   initialization
 /**
  * constructor initializes the image and animation.
  */
 public Grass() throws Exception {
   super(setColumns(JumpCanvas.DISP_WIDTH), 1, Image
       .createImage("/images/grass.png"), TILE_WIDTH, TILE_WIDTH);
   TOP_Y = JumpManager.DISP_HEIGHT - TILE_WIDTH;
   setPosition(0, TOP_Y);
   myAnimatedTileIndex = createAnimatedTile(2);
   for (int i = 0; i < COLUMNS; i++) {
     if ((i % CYCLE == 0) || (i % CYCLE == 2)) {
       setCell(i, 0, myAnimatedTileIndex);
     } else {
       setCell(i, 0, 1);
     }
   }
 }
 //---------------------------------------------------------
 //   graphics
 /**
  * sets the grass back to its initial position..
  */
 void reset() {
   setPosition(-(TILE_WIDTH * CYCLE), TOP_Y);
   mySequenceIndex = 0;
   setAnimatedTile(myAnimatedTileIndex, FRAME_SEQUENCE[mySequenceIndex]);
 }
 /**
  * alter the background image appropriately for this frame..
  * 
  * @param left
  *            whether or not the player is moving left
  */
 void advance(int tickCount) {
   if (tickCount % 2 == 0) { // slow the animation down a little
     mySequenceIndex++;
     mySequenceIndex %= 4;
     setAnimatedTile(myAnimatedTileIndex,
         FRAME_SEQUENCE[mySequenceIndex]);
   }
 }

} /**

* This class contains the loop that keeps the game running.
* 
* @author Carol Hamer
*/

class GameThread extends Thread {

 //---------------------------------------------------------
 //   fields
 /**
  * Whether or not the main thread would like this thread to pause.
  */
 private boolean myShouldPause;
 /**
  * Whether or not the main thread would like this thread to stop.
  */
 private boolean myShouldStop;
 /**
  * A handle back to the graphical components.
  */
 private JumpCanvas myJumpCanvas;
 /**
  * The System.time of the last screen refresh, used to regulate refresh
  * speed.
  */
 private long myLastRefreshTime;
 //----------------------------------------------------------
 //   initialization
 /**
  * standard constructor.
  */
 GameThread(JumpCanvas canvas) {
   myJumpCanvas = canvas;
 }
 //----------------------------------------------------------
 //   utilities
 /**
  * Get the amount of time to wait between screen refreshes. Normally we wait
  * only a single millisecond just to give the main thread a chance to update
  * the keystroke info, but this method ensures that the game will not
  * attempt to show too many frames per second.
  */
 private long getWaitTime() {
   long retVal = 1;
   long difference = System.currentTimeMillis() - myLastRefreshTime;
   if (difference < 75) {
     retVal = 75 - difference;
   }
   return (retVal);
 }
 //----------------------------------------------------------
 //   actions
 /**
  * pause the game.
  */
 void pauseGame() {
   myShouldPause = true;
 }
 /**
  * restart the game after a pause.
  */
 synchronized void resumeGame() {
   myShouldPause = false;
   notify();
 }
 /**
  * stops the game.
  */
 synchronized void requestStop() {
   myShouldStop = true;
   notify();
 }
 /**
  * start the game..
  */
 public void run() {
   // flush any keystrokes that occurred before the
   // game started:
   myJumpCanvas.flushKeys();
   myShouldStop = false;
   myShouldPause = false;
   while (true) {
     myLastRefreshTime = System.currentTimeMillis();
     if (myShouldStop) {
       break;
     }
     synchronized (this) {
       while (myShouldPause) {
         try {
           wait();
         } catch (Exception e) {
         }
       }
     }
     myJumpCanvas.checkKeys();
     myJumpCanvas.advance();
     // we do a very short pause to allow the other thread
     // to update the information about which keys are pressed:
     synchronized (this) {
       try {
         wait(getWaitTime());
       } catch (Exception e) {
       }
     }
   }
 }

} /**

* This class represents the player.
* 
* @author Carol Hamer
*/

class Cowboy extends Sprite {

 //---------------------------------------------------------
 //    dimension fields
 /**
  * The width of the cowboy"s bounding rectangle.
  */
 static final int WIDTH = 32;
 /**
  * The height of the cowboy"s bounding rectangle.
  */
 static final int HEIGHT = 48;
 /**
  * This is the order that the frames should be displayed for the animation.
  */
 static final int[] FRAME_SEQUENCE = { 3, 2, 1, 2 };
 //---------------------------------------------------------
 //    instance fields
 /**
  * the X coordinate of the cowboy where the cowboy starts the game.
  */
 private int myInitialX;
 /**
  * the Y coordinate of the cowboy when not jumping.
  */
 private int myInitialY;
 /**
  * The jump index that indicates that no jump is currently in progress..
  */
 private int myNoJumpInt = -6;
 /**
  * Where the cowboy is in the jump sequence.
  */
 private int myIsJumping = myNoJumpInt;
 /**
  * If the cowboy is currently jumping, this keeps track of how many points
  * have been scored so far during the jump. This helps the calculation of
  * bonus points since the points being scored depend on how many tumbleweeds
  * are jumped in a single jump.
  */
 private int myScoreThisJump = 0;
 //---------------------------------------------------------
 //   initialization
 /**
  * constructor initializes the image and animation.
  */
 public Cowboy(int initialX, int initialY) throws Exception {
   super(Image.createImage("/images/cowboy.png"), WIDTH, HEIGHT);
   myInitialX = initialX;
   myInitialY = initialY;
   // we define the reference pixel to be in the middle
   // of the cowboy image so that when the cowboy turns
   // from right to left (and vice versa) he does not
   // appear to move to a different location.
   defineReferencePixel(WIDTH / 2, 0);
   setRefPixelPosition(myInitialX, myInitialY);
   setFrameSequence(FRAME_SEQUENCE);
 }
 //---------------------------------------------------------
 //   game methods
 /**
  * If the cowboy has landed on a tumbleweed, we decrease the score.
  */
 int checkCollision(Tumbleweed tumbleweed) {
   int retVal = 0;
   if (collidesWith(tumbleweed, true)) {
     retVal = 1;
     // once the cowboy has collided with the tumbleweed,
     // that tumbleweed is done for now, so we call reset
     // which makes it invisible and ready to be reused.
     tumbleweed.reset();
   }
   return (retVal);
 }
 /**
  * set the cowboy back to its initial position.
  */
 void reset() {
   myIsJumping = myNoJumpInt;
   setRefPixelPosition(myInitialX, myInitialY);
   setFrameSequence(FRAME_SEQUENCE);
   myScoreThisJump = 0;
   // at first the cowboy faces right:
   setTransform(TRANS_NONE);
 }
 //---------------------------------------------------------
 //   graphics
 /**
  * alter the cowboy image appropriately for this frame..
  */
 void advance(int tickCount, boolean left) {
   if (left) {
     // use the mirror image of the cowboy graphic when
     // the cowboy is going towards the left.
     setTransform(TRANS_MIRROR);
     move(-1, 0);
   } else {
     // use the (normal, untransformed) image of the cowboy
     // graphic when the cowboy is going towards the right.
     setTransform(TRANS_NONE);
     move(1, 0);
   }
   // this section advances the animation:
   // every third time through the loop, the cowboy
   // image is changed to the next image in the walking
   // animation sequence:
   if (tickCount % 3 == 0) { // slow the animation down a little
     if (myIsJumping == myNoJumpInt) {
       // if he"s not jumping, set the image to the next
       // frame in the walking animation:
       nextFrame();
     } else {
       // if he"s jumping, advance the jump:
       // the jump continues for several passes through
       // the main game loop, and myIsJumping keeps track
       // of where we are in the jump:
       myIsJumping++;
       if (myIsJumping < 0) {
         // myIsJumping starts negative, and while it"s
         // still negative, the cowboy is going up.
         // here we use a shift to make the cowboy go up a
         // lot in the beginning of the jump, and ascend
         // more and more slowly as he reaches his highest
         // position:
         setRefPixelPosition(getRefPixelX(), getRefPixelY()
             - (2 << (-myIsJumping)));
       } else {
         // once myIsJumping is negative, the cowboy starts
         // going back down until he reaches the end of the
         // jump sequence:
         if (myIsJumping != -myNoJumpInt - 1) {
           setRefPixelPosition(getRefPixelX(), getRefPixelY()
               + (2 << myIsJumping));
         } else {
           // once the jump is done, we reset the cowboy to
           // his non-jumping position:
           myIsJumping = myNoJumpInt;
           setRefPixelPosition(getRefPixelX(), myInitialY);
           // we set the image back to being the walking
           // animation sequence rather than the jumping image:
           setFrameSequence(FRAME_SEQUENCE);
           // myScoreThisJump keeps track of how many points
           // were scored during the current jump (to keep
           // track of the bonus points earned for jumping
           // multiple tumbleweeds). Once the current jump is done,
           // we set it back to zero.
           myScoreThisJump = 0;
         }
       }
     }
   }
 }
 /**
  * makes the cowboy jump.
  */
 void jump() {
   if (myIsJumping == myNoJumpInt) {
     myIsJumping++;
     // switch the cowboy to use the jumping image
     // rather than the walking animation images:
     setFrameSequence(null);
     setFrame(0);
   }
 }
 /**
  * This is called whenever the cowboy clears a tumbleweed so that more
  * points are scored when more tumbleweeds are cleared in a single jump.
  */
 int increaseScoreThisJump() {
   if (myScoreThisJump == 0) {
     myScoreThisJump++;
   } else {
     myScoreThisJump *= 2;
   }
   return (myScoreThisJump);
 }

} /**

* This class contains the loop that keeps the game running.
* 
* @author Carol Hamer
*/

class TumbleweedThread extends Thread {

 //---------------------------------------------------------
 //   fields
 /**
  * Whether or not the main thread would like this thread to pause.
  */
 private boolean myShouldPause;
 /**
  * Whether or not the main thread would like this thread to stop.
  */
 private boolean myShouldStop;
 /**
  * A handle back to the graphical components.
  */
 private Tumbleweed[] myTumbleweeds;
 /**
  * Random number generator to randomly decide when to appear.
  */
 private Random myRandom = new Random();
 //----------------------------------------------------------
 //   initialization
 /**
  * standard constructor, sets data.
  */
 TumbleweedThread(JumpCanvas canvas) throws Exception {
   myTumbleweeds = canvas.getTumbleweeds();
 }
 //----------------------------------------------------------
 //   actions
 /**
  * pause the thread.
  */
 void pauseGame() {
   myShouldPause = true;
 }
 /**
  * restart the thread after a pause.
  */
 synchronized void resumeGame() {
   myShouldPause = false;
   notify();
 }
 /**
  * stops the thread.
  */
 synchronized void requestStop() {
   myShouldStop = true;
   notify();
 }
 /**
  * start the thread..
  */
 public void run() {
   myShouldStop = false;
   myShouldPause = false;
   while (true) {
     if (myShouldStop) {
       break;
     }
     synchronized (this) {
       while (myShouldPause) {
         try {
           wait();
         } catch (Exception e) {
         }
       }
     }
     // wait a random length of time:
     int waitTime = (1 + getRandomInt(10)) * 100;
     synchronized (this) {
       try {
         wait(waitTime);
       } catch (Exception e) {
       }
     }
     if (!myShouldPause) {
       // randomly select which one to set in motion and
       // tell it to go. If the chosen tumbleweed is
       // currently visible, it will not be affected
       int whichWeed = getRandomInt(myTumbleweeds.length);
       myTumbleweeds[whichWeed].go();
     }
   }
 }
 //----------------------------------------------------------
 //   randomization utilities
 /**
  * Gets a random int between zero and the param upper (exclusive).
  */
 public int getRandomInt(int upper) {
   int retVal = myRandom.nextInt() % upper;
   if (retVal < 0) {
     retVal += upper;
   }
   return (retVal);
 }

} /**

* This class represents the tumbleweeds that the player must jump over.
* 
* @author Carol Hamer
*/

class Tumbleweed extends Sprite {

 //---------------------------------------------------------
 //   dimension fields
 /**
  * The width of the tumbleweed"s bounding square.
  */
 static final int WIDTH = 16;
 //---------------------------------------------------------
 //    instance fields
 /**
  * whether or not this tumbleweed has been jumped over. This is used to
  * calculate the score.
  */
 private boolean myJumpedOver;
 /**
  * whether or not this tumbleweed enters from the left.
  */
 private boolean myLeft;
 /**
  * the Y coordinate of the tumbleweed.
  */
 private int myY;
 /**
  * the leftmost visible pixel.
  */
 private int myCurrentLeftBound;
 /**
  * the rightmost visible pixel.
  */
 private int myCurrentRightBound;
 //---------------------------------------------------------
 //   initialization
 /**
  * constructor initializes the image and animation.
  * 
  * @param left
  *            whether or not this tumbleweed enters from the left.
  */
 public Tumbleweed(boolean left) throws Exception {
   super(Image.createImage("/images/tumbleweed.png"), WIDTH, WIDTH);
   myY = JumpManager.DISP_HEIGHT - WIDTH - 2;
   myLeft = left;
   if (!myLeft) {
     setTransform(TRANS_MIRROR);
   }
   myJumpedOver = false;
   setVisible(false);
 }
 //---------------------------------------------------------
 //   game actions
 /**
  * Set the tumbleweed in motion if it is not currently visible.
  */
 synchronized boolean go() {
   boolean retVal = false;
   if (!isVisible()) {
     retVal = true;
     //System.out.println("Tumbleweed.go-->not visible");
     myJumpedOver = false;
     setVisible(true);
     // set the tumbleweed"s position to the point
     // where it just barely appears on the screen
     // to that it can start approaching the cowboy:
     if (myLeft) {
       setRefPixelPosition(myCurrentRightBound, myY);
       move(-1, 0);
     } else {
       setRefPixelPosition(myCurrentLeftBound, myY);
       move(1, 0);
     }
   } else {
     //System.out.println("Tumbleweed.go-->visible");
   }
   return (retVal);
 }
 //---------------------------------------------------------
 //   graphics
 /**
  * move the tumbleweed back to its initial (inactive) state.
  */
 void reset() {
   setVisible(false);
   myJumpedOver = false;
 }
 /**
  * alter the tumbleweed image appropriately for this frame..
  * 
  * @param left
  *            whether or not the player is moving left
  * @return how much the score should change by after this advance.
  */
 synchronized int advance(Cowboy cowboy, int tickCount, boolean left,
     int currentLeftBound, int currentRightBound) {
   int retVal = 0;
   myCurrentLeftBound = currentLeftBound;
   myCurrentRightBound = currentRightBound;
   // if the tumbleweed goes outside of the display
   // region, set it to invisible since it is
   // no longer in use.
   if ((getRefPixelX() - WIDTH >= currentRightBound) && (!myLeft)) {
     setVisible(false);
   }
   if ((getRefPixelX() + WIDTH <= currentLeftBound) && myLeft) {
     setVisible(false);
   }
   if (isVisible()) {
     // when the tumbleweed is active, we advance the
     // rolling animation to the next frame and then
     // move the tumbleweed in the right direction across
     // the screen.
     if (tickCount % 2 == 0) { // slow the animation down a little
       nextFrame();
     }
     if (myLeft) {
       move(-3, 0);
       // if the cowboy just passed the tumbleweed
       // (without colliding with it) we increase the
       // cowboy"s score and set myJumpedOver to true
       // so that no further points will be awarded
       // for this tumbleweed until it goes offscreen
       // and then is later reactivated:
       if ((!myJumpedOver) && (getRefPixelX() < cowboy.getRefPixelX())) {
         myJumpedOver = true;
         retVal = cowboy.increaseScoreThisJump();
       }
     } else {
       move(3, 0);
       if ((!myJumpedOver)
           && (getRefPixelX() > cowboy.getRefPixelX()
               + Cowboy.WIDTH)) {
         myJumpedOver = true;
         retVal = cowboy.increaseScoreThisJump();
       }
     }
   }
   return (retVal);
 }

} /**

* This is the class that plays a little tune while you play the game. This
* version uses the Player and Control interfaces.
* 
* @author Carol Hamer
*/

class ToneControlMusicMaker implements PlayerListener {

 //---------------------------------------------------------
 //   fields
 /**
  * The player object that plays the tune.
  */
 private Player myPlayer;
 /**
  * Whether or not the player wants to pause the music.
  */
 private boolean myShouldPause;
 /**
  * Whether or not the system wants to pause the music.
  */
 private boolean myGamePause;
 /**
  * The tune played by the game, stored as an array of bytes in BNF notation.
  */
 private byte[] myTune = {
 // first set the version
     ToneControl.VERSION, 1,
     // set the tempo
     ToneControl.TEMPO, 30,
     // define the first line of the song
     ToneControl.BLOCK_START, 0, 69, 8, 69, 8, 69, 8, 71, 8, 73, 16, 71,
     16, 69, 8, 73, 8, 71, 8, 71, 8, 69, 32, ToneControl.BLOCK_END, 0,
     // define the other line of the song
     ToneControl.BLOCK_START, 1, 71, 8, 71, 8, 71, 8, 71, 8, 66, 16, 66,
     16, 71, 8, 69, 8, 68, 8, 66, 8, 64, 32, ToneControl.BLOCK_END, 1,
     // play the song
     ToneControl.PLAY_BLOCK, 0, ToneControl.PLAY_BLOCK, 0,
     ToneControl.PLAY_BLOCK, 1, ToneControl.PLAY_BLOCK, 0, };
 //----------------------------------------------------------
 //   actions
 /**
  * call this when the game pauses. This method does not affect the field
  * myShouldPause because this method is called only when the system pauses
  * the music, not when the player pauses the music.
  */
 void pauseGame() {
   try {
     myGamePause = true;
     myPlayer.stop();
     // when the application pauses the game, resources
     // are supposed to be released, so we close the
     // player and throw it away.
     myPlayer.close();
     myPlayer = null;
   } catch (Exception e) {
     // the music isn"t necessary, so we ignore exceptions.
   }
 }
 /**
  * call this when the game resumes. This method does not affect the field
  * myShouldPause because this method is called only when the system reusmes
  * the music, not when the player pauses the music.
  */
 synchronized void resumeGame() {
   try {
     myGamePause = false;
     if (!myShouldPause) {
       // if the player is null, we create a new one.
       if (myPlayer == null) {
         start();
       }
       // start the music.
       myPlayer.start();
     }
   } catch (Exception e) {
     // the music isn"t necessary, so we ignore exceptions.
   }
 }
 /**
  * toggle the music. (pause it if it"s going, start it again if it"s
  * paused).
  */
 synchronized void toggle() {
   try {
     myShouldPause = !myShouldPause;
     if (myShouldPause) {
       if (myPlayer != null) {
         myPlayer.stop();
       }
     } else if (!myGamePause) {
       // if the player is null, we create a new one.
       if (myPlayer == null) {
         start();
       }
       // start the music.
       myPlayer.start();
     }
   } catch (Exception e) {
     // the music isn"t necessary, so we ignore exceptions.
   }
 }
 /**
  * stops the music.
  */
 synchronized void requestStop() {
   try {
     myPlayer.stop();
     // this is called when the game is over, to we close
     // up the player to release the resources.
     myPlayer.close();
   } catch (Exception e) {
     // the music isn"t necessary, so we ignore exceptions.
   }
 }
 //----------------------------------------------------------
 //   initialization
 /**
  * start the music.. Here the method is "start" instead of "run" because it
  * is not necessary to create a thread for the Player. the Player runs on
  * its own thread.
  */
 public void start() {
   ToneControl control = null;
   try {
     myPlayer = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
     // do the preliminary set-up:
     myPlayer.realize();
     // set a listener to listen for the end of the tune:
     myPlayer.addPlayerListener(this);
     // get the ToneControl object in order to set the tune data:
     control = (ToneControl) myPlayer.getControl("ToneControl");
     control.setSequence(myTune);
     // set the volume to the highest possible volume:
     VolumeControl vc = (VolumeControl) myPlayer
         .getControl("VolumeControl");
     vc.setLevel(100);
   } catch (Exception e) {
     // the music isn"t necessary, so we ignore exceptions.
   }
 }
 //----------------------------------------------------------
 //   implementation of PlayerListener
 /**
  * If we reach the end of the song, play it again...
  */
 public void playerUpdate(Player player, String event, Object eventData) {
   if (event.equals(PlayerListener.END_OF_MEDIA)) {
     if ((!myShouldPause) && (!myGamePause)) {
       try {
         myPlayer.start();
       } catch (Exception e) {
         // the music isn"t necessary, so we ignore exceptions.
       }
     }
   }
 }

} /**

* This is the class that plays a little tune while you play the game.
* 
* @author Carol Hamer
*/

class MusicMaker extends Thread {

 //---------------------------------------------------------
 //   fields
 /**
  * Whether or not the main thread would like this thread to stop.
  */
 public static final int NOTE_LENGTH = 250;
 /**
  * Whether or not the main thread would like this thread to pause.
  */
 private boolean myShouldPause;
 /**
  * If the whole game is paused, we pause the music too..
  */
 private boolean myGamePause;
 /**
  * Whether or not the main thread would like this thread to stop.
  */
 private static boolean myShouldStop;
 /**
  * The tune played by the game, stored as an array of notes and durations.
  * 
  * NOTE: 69 is A. To get other notes, just add or subtract their difference
  * from A on the keyboard including the black keys in the calculation. See
  * the scales below for an idea.
  *  
  */
 private byte[][] myTune = { { 69, 1 }, { 69, 1 }, { 69, 1 }, { 71, 1 },
     { 73, 2 }, { 71, 2 }, { 69, 1 }, { 73, 1 }, { 71, 1 }, { 71, 1 },
     { 69, 4 }, { 69, 1 }, { 69, 1 }, { 69, 1 }, { 71, 1 }, { 73, 2 },
     { 71, 2 }, { 69, 1 }, { 73, 1 }, { 71, 1 }, { 71, 1 }, { 69, 4 },
     { 71, 1 }, { 71, 1 }, { 71, 1 }, { 71, 1 }, { 66, 2 }, { 66, 2 },
     { 71, 1 }, { 69, 1 }, { 68, 1 }, { 66, 1 }, { 64, 4 }, { 69, 1 },
     { 69, 1 }, { 69, 1 }, { 71, 1 }, { 73, 2 }, { 71, 2 }, { 69, 1 },
     { 73, 1 }, { 71, 1 }, { 71, 1 }, { 69, 4 } };
 /**
  * An example "tune" that is just a scale.. not used.
  */
 private byte[][] myScale = { { 69, 1 }, { 71, 1 }, { 73, 1 }, { 74, 1 },
     { 76, 1 }, { 78, 1 }, { 80, 1 }, { 81, 1 } };
 /**
  * An example "tune" that is just a scale.. not used.
  */
 private byte[][] myScale2 = { { 57, 1 }, { 59, 1 }, { 61, 1 }, { 62, 1 },
     { 64, 1 }, { 66, 1 }, { 68, 1 }, { 69, 1 } };
 //----------------------------------------------------------
 //   actions
 /**
  * call this when the game pauses.
  */
 void pauseGame() {
   myGamePause = true;
 }
 /**
  * call this when the game resumes.
  */
 synchronized void resumeGame() {
   myGamePause = false;
   this.notify();
 }
 /**
  * toggle the music. (pause it if it"s going, start it again if it"s
  * paused).
  */
 synchronized void toggle() {
   myShouldPause = !myShouldPause;
   this.notify();
 }
 /**
  * stops the music.
  */
 synchronized void requestStop() {
   myShouldStop = true;
   this.notify();
 }
 /**
  * start the music..
  */
 public void run() {
   myShouldStop = false;
   myShouldPause = true;
   myGamePause = false;
   int counter = 0;
   while (true) {
     if (myShouldStop) {
       break;
     }
     synchronized (this) {
       while ((myShouldPause) || (myGamePause)) {
         try {
           wait();
         } catch (Exception e) {
         }
       }
     }
     try {
       Manager.playTone(myTune[counter][0], myTune[counter][1]
           * NOTE_LENGTH, 50);
     } catch (Exception e) {
       // the music isn"t necessary, so we ignore exceptions.
     }
     synchronized (this) {
       try {
         wait(myTune[counter][1] * NOTE_LENGTH);
       } catch (Exception e) {
       }
     }
     counter++;
     if (counter >= myTune.length) {
       counter = 0;
     }
   }
 }

} /**

* This handles the graphics objects.
* 
* @author Carol Hamer
*/

class JumpManager extends javax.microedition.lcdui.game.LayerManager {

 //---------------------------------------------------------
 //   dimension fields
 //  (constant after initialization)
 /**
  * The x-coordinate of the place on the game canvas where the LayerManager
  * window should appear, in terms of the coordiantes of the game canvas.
  */
 static int CANVAS_X;
 /**
  * The y-coordinate of the place on the game canvas where the LayerManager
  * window should appear, in terms of the coordiantes of the game canvas.
  */
 static int CANVAS_Y;
 /**
  * The width of the display window.
  */
 static int DISP_WIDTH;
 /**
  * The height of this object"s graphical region. This is the same as the
  * height of the visible part because in this game the layer manager"s
  * visible part scrolls only left and right but not up and down.
  */
 static int DISP_HEIGHT;
 //   game object fields
 // the player"s object.
 private Cowboy myCowboy;
 /**
  * the tumbleweeds that enter from the left.
  */
 private Tumbleweed[] myLeftTumbleweeds;
 /**
  * the tumbleweeds that enter from the right.
  */
 private Tumbleweed[] myRightTumbleweeds;
 /**
  * the object representing the grass in the background..
  */
 private Grass myGrass;
 /**
  * Whether or not the player is currently going left.
  */
 private boolean myLeft;
 /**
  * The leftmost x-coordinate that should be visible on the screen in terms
  * of this objects internal coordinates.
  */
 private int myCurrentLeftX;
 //-----------------------------------------------------
 //    gets/sets
 /**
  * This tells the player to turn left or right.
  * 
  * @param left
  *            whether or not the turn is towards the left..
  */
 void setLeft(boolean left) {
   myLeft = left;
 }
 /**
  * @return a handle to the tumbleweed objects.
  */
 Tumbleweed[] getTumbleweeds() {
   Tumbleweed[] retArray = new Tumbleweed[myLeftTumbleweeds.length
       + myRightTumbleweeds.length];
   for (int i = 0; i < myLeftTumbleweeds.length; i++) {
     retArray[i] = myLeftTumbleweeds[i];
   }
   for (int i = 0; i < myRightTumbleweeds.length; i++) {
     retArray[i + myLeftTumbleweeds.length] = myRightTumbleweeds[i];
   }
   return (retArray);
 }
 //-----------------------------------------------------
 //    initialization and game state changes
 /**
  * Constructor sets the data and constructs the graphical objects..
  * 
  * @param x
  *            The x-coordinate of the place on the game canvas where the
  *            LayerManager window should appear, in terms of the coordiantes
  *            of the game canvas.
  * @param y
  *            The y-coordinate of the place on the game canvas where the
  *            LayerManager window should appear, in terms of the coordiantes
  *            of the game canvas.
  * @param width
  *            the width of the region that is to be occupied by the
  *            LayoutManager.
  * @param height
  *            the height of the region that is to be occupied by the
  *            LayoutManager.
  */
 public JumpManager(int x, int y, int width, int height) throws Exception {
   CANVAS_X = x;
   CANVAS_Y = y;
   DISP_WIDTH = width;
   DISP_HEIGHT = height;
   myCurrentLeftX = Grass.CYCLE * Grass.TILE_WIDTH;
   setViewWindow(0, 0, DISP_WIDTH, DISP_HEIGHT);
   // create the player:
   if (myCowboy == null) {
     myCowboy = new Cowboy(myCurrentLeftX + DISP_WIDTH / 2, DISP_HEIGHT
         - Cowboy.HEIGHT - 2);
     append(myCowboy);
   }
   // create the tumbleweeds to jump over:
   if (myLeftTumbleweeds == null) {
     myLeftTumbleweeds = new Tumbleweed[2];
     for (int i = 0; i < myLeftTumbleweeds.length; i++) {
       myLeftTumbleweeds[i] = new Tumbleweed(true);
       append(myLeftTumbleweeds[i]);
     }
   }
   if (myRightTumbleweeds == null) {
     myRightTumbleweeds = new Tumbleweed[2];
     for (int i = 0; i < myRightTumbleweeds.length; i++) {
       myRightTumbleweeds[i] = new Tumbleweed(false);
       append(myRightTumbleweeds[i]);
     }
   }
   // create the background object:
   if (myGrass == null) {
     myGrass = new Grass();
     append(myGrass);
   }
 }
 /**
  * sets all variables back to their initial positions.
  */
 void reset() {
   if (myGrass != null) {
     myGrass.reset();
   }
   if (myCowboy != null) {
     myCowboy.reset();
   }
   if (myLeftTumbleweeds != null) {
     for (int i = 0; i < myLeftTumbleweeds.length; i++) {
       myLeftTumbleweeds[i].reset();
     }
   }
   if (myRightTumbleweeds != null) {
     for (int i = 0; i < myRightTumbleweeds.length; i++) {
       myRightTumbleweeds[i].reset();
     }
   }
   myLeft = false;
   myCurrentLeftX = Grass.CYCLE * Grass.TILE_WIDTH;
 }
 //-------------------------------------------------------
 //  graphics methods
 /**
  * paint the game graphic on the screen.
  */
 public void paint(Graphics g) {
   setViewWindow(myCurrentLeftX, 0, DISP_WIDTH, DISP_HEIGHT);
   paint(g, CANVAS_X, CANVAS_Y);
 }
 /**
  * If the cowboy gets to the end of the graphical region, move all of the
  * pieces so that the screen appears to wrap.
  */
 private void wrap() {
   if (myCurrentLeftX % (Grass.TILE_WIDTH * Grass.CYCLE) == 0) {
     if (myLeft) {
       myCowboy.move(Grass.TILE_WIDTH * Grass.CYCLE, 0);
       myCurrentLeftX += (Grass.TILE_WIDTH * Grass.CYCLE);
       for (int i = 0; i < myLeftTumbleweeds.length; i++) {
         myLeftTumbleweeds[i]
             .move(Grass.TILE_WIDTH * Grass.CYCLE, 0);
       }
       for (int i = 0; i < myRightTumbleweeds.length; i++) {
         myRightTumbleweeds[i].move(Grass.TILE_WIDTH * Grass.CYCLE,
             0);
       }
     } else {
       myCowboy.move(-(Grass.TILE_WIDTH * Grass.CYCLE), 0);
       myCurrentLeftX -= (Grass.TILE_WIDTH * Grass.CYCLE);
       for (int i = 0; i < myLeftTumbleweeds.length; i++) {
         myLeftTumbleweeds[i].move(-Grass.TILE_WIDTH * Grass.CYCLE,
             0);
       }
       for (int i = 0; i < myRightTumbleweeds.length; i++) {
         myRightTumbleweeds[i].move(-Grass.TILE_WIDTH * Grass.CYCLE,
             0);
       }
     }
   }
 }
 //-------------------------------------------------------
 //  game movements
 /**
  * Tell all of the moving components to advance.
  * 
  * @param gameTicks
  *            the remainaing number of times that the main loop of the game
  *            will be executed before the game ends.
  * @return the change in the score after the pieces have advanced.
  */
 int advance(int gameTicks) {
   int retVal = 0;
   // first we move the view window
   // (so we are showing a slightly different view of
   // the manager"s graphical area.)
   if (myLeft) {
     myCurrentLeftX--;
   } else {
     myCurrentLeftX++;
   }
   // now we tell the game objects to move accordingly.
   myGrass.advance(gameTicks);
   myCowboy.advance(gameTicks, myLeft);
   for (int i = 0; i < myLeftTumbleweeds.length; i++) {
     retVal += myLeftTumbleweeds[i].advance(myCowboy, gameTicks, myLeft,
         myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);
     retVal -= myCowboy.checkCollision(myLeftTumbleweeds[i]);
   }
   for (int i = 0; i < myLeftTumbleweeds.length; i++) {
     retVal += myRightTumbleweeds[i].advance(myCowboy, gameTicks,
         myLeft, myCurrentLeftX, myCurrentLeftX + DISP_WIDTH);
     retVal -= myCowboy.checkCollision(myRightTumbleweeds[i]);
   }
   // now we check if we have reached an edge of the viewable
   // area, and if so we move the view area and all of the
   // game objects so that the game appears to wrap.
   wrap();
   return (retVal);
 }
 /**
  * Tell the cowboy to jump..
  */
 void jump() {
   myCowboy.jump();
 }

}


      </source>