Mega Code Archive

 
Categories / Java / J2ME
 

Maze game

/* 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);   } }