/**
* 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);
/**
* 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
/**
* 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;
/**
* 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();
}
/**
* 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);
}
}
/**
* 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 {
/**
* 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;
/**
* 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);
}
You can share your information about this topic using the form below!
Please do not post your questions with this form! Thanks.