// Models a maze as a 2-d array of characters public class Maze { private char[][] squares; private boolean[][] explored; private boolean animated; private int lastIsRow, lastIsCol; // last location on which an 'is' method // was called; used for toString '?' public Maze(String text) { if (text == null || (text = text.trim()).length() == 0) { throw new IllegalArgumentException("empty lines data"); } String[] lines = text.split("[\r]?\n"); squares = new char[lines.length][lines[0].length()]; explored = new boolean[lines.length][lines[0].length()]; for (int row = 0; row < getHeight(); row++) { if (lines[row].length() != getWidth()) { throw new IllegalArgumentException("line " + (row + 1) + " wrong length (was " + lines[row].length() + " but should be " + getWidth() + ")"); } for (int col = 0; col < getWidth(); col++) { squares[row][col] = lines[row].charAt(col); } } lastIsRow = -1; lastIsCol = -1; animated = false; } // Returns the maze height. public int getHeight() { return squares.length; } // Returns the maze width. public int getWidth() { return squares[0].length; } // Returns the character at the given location. public char getSquare(int row, int col) { checkIndexes(row, col); return squares[row][col]; } // Returns true if the maze has been solved, false otherwise. public boolean isEscaped() { // check left/right edges for (int row = 0; row < getHeight(); row++) { if (explored[row][0] || explored[row][getWidth() - 1]) { return true; } } // check top/bottom edges for (int col = 0; col < getWidth(); col++) { if (explored[0][col] || explored[getHeight() - 1][col]) { return true; } } return false; } // Returns true if the given location has been explored. public boolean isExplored(int row, int col) { checkIndexes(row, col); lastIsRow = row; lastIsCol = col; return explored[row][col]; } // Returns true if the given location is on a solution path. public boolean isMarked(int row, int col) { checkIndexes(row, col); lastIsRow = row; lastIsCol = col; return squares[row][col] == '.'; } // Returns true if the given location is a wall. public boolean isWall(int row, int col) { checkIndexes(row, col); lastIsRow = row; lastIsCol = col; return squares[row][col] == '#'; } // Marks the given location as being on a solution path. public void mark(int row, int col) { checkIndexes(row, col); maybePause(); squares[row][col] = 'x'; } // Sets whether the maze can be animated. public void setAnimated(boolean value) { animated = value; } // Marks the given location has having been explored. public void setExplored(int row, int col) { checkIndexes(row, col); explored[row][col] = true; } // Returns a string representation of this maze. // '?' represents the current location in an exploration // '#' represents walls // 'x' represents a location on the solution path // '.' represents a visited location public String toString() { StringBuilder result = new StringBuilder(getWidth() * (getHeight() + 1)); for (int row = 0; row < getHeight(); row++) { for (int col = 0; col < getWidth(); col++) { if (row == lastIsRow && col == lastIsCol) { result.append('?'); } else if (squares[row][col] == '#') { result.append('#'); } else if (squares[row][col] == 'x') { result.append('x'); } else if (explored[row][col]) { result.append('.'); } else { result.append(' '); } } result.append('\n'); } maybePause(); return result.toString(); } // Throws an IllegalArgumentException if the given row and column values are // outside the maze bounds. private void checkIndexes(int row, int col) { if (row < 0 || row >= getHeight() || col < 0 || col >= getWidth()) { throw new IllegalArgumentException("illegal indexes: (" + row + ", " + col + ")"); } } // Pauses if the maze is animated. private void maybePause() { if (animated) { sleep(100); } } // Pauses, ignoring errors. private void sleep(int ms) { try { Thread.sleep(ms); } catch (InterruptedException ie) { } } }