import java.awt.*; public class WallMaze extends Maze { public static final byte BLOCKED = (byte) (TOP | RIGHT | BOTTOM | LEFT); int lastX, lastY; PathStack pathStack; protected byte initSq () { return (byte) (BLOCKED | DIRTY); } public WallMaze () { setBackground (Color.white); lineWidth = lineHeight = 1; } public boolean isOpen (int x, int y) { return inBounds (x, y) && sqr (x, y) != BLOCKED; } /* * Here is the algorithm used to generate the maze: * 1. Set all the squares in maze grid to the BLOCKED state. * 2. Randomly select a square on the left side of the maze as the current square, * clear the wall on the left side of the square, and set to LEFT. * 3. Randomly choose a sequence to rotate through the remaining sides (the sides * other than ) of the current square. * 4. Set nextSide to the next selected side in the rotation. * 5. If there is a BLOCKED square adjacent to the of the current * square, then goto step 9. * 6. If there are more sides rotation sequence, then goto step 4. * 7. You get to this step when the current square is not adjacent to any * BLOCKED squares. Randomly select a non-BLOCKED square (a square already * added to the maze) as the current square. This creates a new branch * in the maze. * 9. You get to this step when you have found a square to add to the maze. * Remove the wall between the current square and the square adjacent to * its . * 10. Set the new current square to the adjacent square and set to * the side of this square that was removed in step 9. This sets * to the side opposite . For example, if is TOP, then * the new is BOTTOM. * 11. If there are any BLOCKED squares remaining in the maze grid then goto * step 3, otherwise you're done. */ public synchronized void generate (boolean displayConstruction) { int curX, curY, nextX=0, nextY=0, lastSide = LEFT; byte sq; boolean stuck = false; int blocked = mazeWidth * mazeHeight, nonBlocked = 0, threshold = blocked/8; generating = true; path = null; pathStack = null; // 1. Set all the squares in maze grid to the BLOCKED state. clearMaze (); if (displayConstruction) showMaze (true); //2. Randomly select a square on the left side of the maze //as the current square startX = curX = 0; startY = curY = (int) (Math.random() * mazeHeight); // clear the wall on the left side of the square, //and set to LEFT. maze [curX] [curY] = sq = (byte) ((BLOCKED | DIRTY) & ~LEFT); dirtySquare (curX, curY); lastSide = LEFT; blocked --; nonBlocked ++; if (displayConstruction) showMaze (true); do { // 3. Randomly choose a sequence to rotate through the remaining sides int rot = (int) (Math.random () * 4); //System.out.println ("Step 3, pos=("+curX+","+curY+"),rot = "+rot); int count = 3; boolean goodSquare = false; int nextSide = 0; byte oppSide = 0; do { // 4. Set nextSide to the next selected side in the rotation. nextSide = lastSide << rot; oppSide = (byte) nextSide; if (nextSide > BLOCKED) nextSide >>= 4; switch (nextSide) { case LEFT: oppSide = RIGHT; nextX = curX-1; nextY = curY; break; case RIGHT: oppSide = LEFT; nextX = curX+1; nextY = curY; if (!inBounds (nextX, nextY) && finishX < 0) { finishX = curX; finishY = curY; maze [curX] [curY] &= ~RIGHT; } break; case TOP: oppSide = BOTTOM; nextX = curX; nextY = curY-1; break; case BOTTOM: oppSide = TOP; nextX = curX; nextY = curY+1; break; } goodSquare = inBounds (nextX, nextY) && !isOpen (nextX, nextY); //System.out.println ("Step 6: "+nextX+" " + nextY+": "+goodSquare); count --; if (!goodSquare) { rot ++; if (rot > 3) rot = 1; } // 6. If there are more sides rotation sequence, //then goto step 4. } while (!goodSquare && count > 0 && generating); // 5. If there is a BLOCKED square adjacent to the // of the current square, then goto step 9. if (goodSquare) { //9. Remove the wall between the current square and the square //adjacent to its . maze [curX] [curY] &= (byte) ~nextSide; maze [nextX] [nextY] &= (byte) ~oppSide; dirtySquare (curX, curY); dirtySquare (nextX, nextY); //10. Set the new current square to the adjacent square //and set to the side of this square that was //removed in step 9. curX = nextX; curY = nextY; lastSide = oppSide; blocked --; nonBlocked ++; if (displayConstruction) showMaze (true); } else { //7. Randomly select a non-BLOCKED square as the current //square. goodSquare = true; do { int pickSquare = (int) (Math.random () * nonBlocked); count = 0; for (int x = 0; x < mazeWidth; x ++) { for (int y = 0; y < mazeHeight; y ++) { if (isOpen (x, y)) { if (count++ == pickSquare) { curX = x; curY = y; } } } } //System.out.println ("Open square at ("+curX+","+curY+")"); if (blocked < threshold) { goodSquare = false; for (int i = 0; i < 4; i ++) { int nx = curX, ny = curY; switch (i) { case 0: nx --; break; case 1: nx ++; break; case 2: ny --; break; case 3: ny ++; break; } if (inBounds (nx, ny) && sqr (nx, ny) == BLOCKED) goodSquare = true; } } } while (!goodSquare && generating); //8. pick a new lastSide sq = sqr (curX, curY); for (nextSide = 0; (sq & nextSide) != 0 && nextSide < BLOCKED; nextSide <<= 1); } //* 11. If there are any BLOCKED squares remaining in the maze grid then goto //* step 3, otherwise you're done. } while (blocked > 0 && generating); generating = false; showMaze (true); } public void stopGenerating () { generating = false; } //////////////////////////////////////// // PATH / TRAVERSAL METHODS //////////////////////////////////////// public boolean traverse (int fromX, int fromY, int toX, int toY, boolean displaySearch) { return traverse (fromX, fromY, toX, toY, displaySearch, 1000); } //algorithm for traversal from square s1 to s2 over distance d //1. if s1 = s2, end in success //2. if d = 0, end in failure //3. push this location onto the stack / make s1 display //4. check each possible direction where maze can go and no path there //5. if one of the directions returns success, end //6. if none of the directions returns success: //7. remove last location from the stack / undisplay s1 //8. remove previous location from stack / undisplay last public boolean traverse (int fromX, int fromY, int toX, int toY, boolean displaySearch, int distance) { if (fromX == toX && fromY == toY) { activatePath (toX, toY); lastX = toX; lastY = toY; return true; } if (distance == 0) { return false; } int x = fromX, y = fromY; pathStack.push (new MazeSquare (fromX, fromY)); activatePath (x, y); boolean left = inBounds (x-1, y) && (maze [x] [y] & LEFT) == 0 && path [x-1] [y] == 0, right = inBounds (x+1, y) && (maze [x] [y] & RIGHT) == 0 && path [x+1] [y] == 0, top = inBounds (x, y-1) && (maze [x] [y] & TOP) == 0 && path [x] [y-1] == 0, bottom = inBounds (x, y+1) && (maze [x] [y] & BOTTOM) == 0 && path [x] [y+1] == 0; if ((left && traverse (x-1, y, toX, toY, displaySearch, distance-1)) || (right && traverse (x+1, y, toX, toY, displaySearch, distance-1)) || (top && traverse (x, y-1, toX, toY, displaySearch, distance-1)) || (bottom && traverse (x, y+1, toX, toY, displaySearch, distance-1)) ) { return true; } MazeSquare m = pathStack.pop (); deactivatePath (fromX, fromY); //System.out.println ("I hereby deactivate "+fromX+", "+fromY); //try backing up one square MazeSquare m2 = pathStack.pop (); if (m2 != null) { deactivatePath (m.x, m.y); dirtySquare (m.x, m.y); if (traverse (m2.x, m2.y, toX, toY, displaySearch, distance-1)) { dirtySquare (m.x, m.y); //System.out.println ("TRUE"); return true; } else { pathStack.push (m2); //System.out.println ("FALSE"); //System.out.println ("lastY is now "+lastY); activatePath (m2.x, m2.y); } } return false; } public boolean traverse (int destX, int destY) { if (path == null) path = new byte [mazeWidth] [mazeHeight]; int i = (destX-leftOffset) / squareWidth, j = (destY-topOffset) / squareHeight; activatePath (i, j); repaint (); return true; } public synchronized boolean activatePath (int x, int y) { //System.out.println ("ADD: "+x+", "+y); boolean left = inBounds (x-1, y) && (maze [x] [y] & LEFT) == 0, right = inBounds (x+1, y) && (maze [x] [y] & RIGHT) == 0, top = inBounds (x, y-1) && (maze [x] [y] & TOP) == 0, bottom = inBounds (x, y+1) && (maze [x] [y] & BOTTOM) == 0; if (left || right || top || bottom || x == startX && y == startY) { path [x] [y] = PATHON; dirtySquare (x, y); if (left && path [x-1] [y] != 0) { path [x] [y] |= LEFT; path [x-1] [y] |= RIGHT; dirtySquare (x-1, y); } if (right && path [x+1] [y] != 0) { path [x] [y] |= RIGHT; path [x+1] [y] |= LEFT; dirtySquare (x+1, y); } if (top && path [x] [y-1] != 0) { path [x] [y] |= TOP; path [x] [y-1] |= BOTTOM; dirtySquare (x, y-1); } if (bottom && path [x] [y+1] != 0) { path [x] [y] |= BOTTOM; path [x] [y+1] |= TOP; dirtySquare (x, y+1); } return true; } return false; } public synchronized boolean deactivatePath (int x, int y) { path [x] [y] = 0; dirtySquare (x, y); if (inBounds (x-1, y)) { path [x-1] [y] &= (byte) ~RIGHT; dirtySquare (x-1, y); } if (inBounds (x+1, y)) { path [x+1] [y] &= (byte) ~LEFT; dirtySquare (x+1, y); } if (inBounds (x, y-1)) { path [x] [y-1] &= (byte) ~BOTTOM; dirtySquare (x, y-1); } if (inBounds (x, y+1)) { path [x] [y+1] &= (byte) ~TOP; dirtySquare (x, y+1); } return true; } public void pathToSquare (int destX, int destY) { if (path == null) { path = new byte [mazeWidth] [mazeHeight]; lastX = startX; lastY = startY; pathStack = new PathStack (); } int i = (destX-leftOffset) / squareWidth, j = (destY-topOffset) / squareHeight; if (traverse (lastX, lastY, i, j, false, 3)) { lastX = i; lastY = j; repaint (); } } public void flipSquare (int x, int y) { if (path [x] [y] == 0) { activatePath (x, y); } else { deactivatePath (x, y); } dirtySquare (x, y); repaint (); } public boolean won () { return (path != null && path [finishX] [finishY] != 0); } //////////////////////////////////////// // DRAWING METHODS //////////////////////////////////////// public void drawSquare (int x, int y) { byte sq = maze [x] [y]; int xoff = x*squareWidth+leftOffset, yoff = y*squareHeight+topOffset; if (sqr (x, y) == BLOCKED) { offscr.setColor (Color.black); offscr.fillRect (xoff, yoff, sqLineWidth, sqLineHeight); } else { int halfWidth = lineWidth/2; offscr.setColor (Color.white); offscr.fillRect (xoff, yoff, sqLineWidth, sqLineHeight); offscr.setColor (Color.black); if ((sq & LEFT) != 0) { offscr.fillRect (xoff, yoff, lineWidth, sqLineHeight); } if ((sq & RIGHT) != 0) { offscr.fillRect (xoff+squareWidth, yoff, lineWidth, sqLineHeight); } if ((sq & TOP) != 0) { offscr.fillRect (xoff, yoff, sqLineWidth, lineHeight); } if ((sq & BOTTOM) != 0) { offscr.fillRect (xoff, yoff+squareHeight, sqLineWidth, lineHeight); } offscr.fillRect (xoff, yoff, lineWidth, lineHeight); offscr.fillRect (xoff+squareWidth, yoff, lineWidth, lineHeight); offscr.fillRect (xoff, yoff+squareHeight, lineWidth, lineHeight); offscr.fillRect (xoff+squareWidth, yoff+squareHeight, lineWidth, lineHeight); } } public void drawFace (int x, int y) { int sq = maze [x] [y]; int xoff = x*squareWidth+leftOffset, yoff = y*squareHeight+topOffset; offscr.setColor (Color.yellow); offscr.fillArc (xoff+1, yoff+1, squareWidth-2, squareHeight-2, 0, 360); offscr.setColor (Color.black); offscr.drawArc (xoff+squareWidth/4, yoff+squareHeight/4, (squareWidth)/2, (squareHeight)/2, 180, 180); offscr.drawLine (xoff+squareWidth/3, yoff+squareHeight/3, xoff+squareWidth/3, yoff+squareHeight/3); offscr.drawLine (xoff+squareWidth*2/3, yoff+squareHeight/3, xoff+squareWidth*2/3, yoff+squareHeight/3); } protected void drawPathSquare (int px, int py, Color color) { int psq; if (path == null || (psq = path[px][py]) == 0) return; int xoff = leftOffset + (px * squareWidth); int yoff = topOffset + (py * squareHeight); boolean top = false, bottom = false, left = false, right = false; offscr.setColor(color); if ((psq & TOP) != 0) { top = true; offscr.fillRect(xoff + pxoff, yoff, pathWidth, pyoff + pathHeight); } if ((psq & BOTTOM) != 0) { offscr.fillRect(xoff + pxoff, yoff + pyoff, pathWidth, pyoff + pathHeight); bottom = true; } if ((psq & LEFT) != 0) { offscr.fillRect(xoff, yoff + pyoff, pxoff + pathWidth, pathHeight); left = true; } if ((psq & RIGHT) != 0) { offscr.fillRect(xoff + pxoff, yoff + pyoff, pxoff + pathWidth, pathHeight); right = true; } if (!top && !left && !right && !bottom) offscr.fillRect(xoff + pxoff, yoff + pyoff, pathWidth, pathHeight); } }