/** * Maze * * This class represents a rectangular maze. The input * method load supports a data format used to store * a maze in a text file. This file format is adapted from * a format described in the problem set for the 2000 * North Central Section of the ACM International Collegiate * Programming Contest. The file format is: * * [Number of rows] [Number of columns] * For each row, each square in the row is * represented by a hexadecimal digit. * * The hexadecimal digits are obtained like this: * * Start with 0 * If the square has a top wall, add 1 * If the square has a right-hand wall, add 2 * If the square has a bottom wall, add 4 * If the square has a left-hand wall, add 8 * Write the resulting number (0-15) as a hexdecimal * digit (0-9, A, B, C, D, E, or F). * * For example, a 2x3 maze with exterior but no interior walls * (that is, an empty 2x3 room) would be represented by: * * 2 3 * 9 1 3 * C 4 6 * * You don't have to parse this file format--that's what * load already does--but you might want to create * example mazes for testing. * * @author Jeff Ondich, 11/1/01 - 1/19/04 */ import java.io.*; class Maze { // Any MazeSquare field is true if the corresponding // wall exists, and false otherwise. class MazeSquare { public boolean hasTopWall; public boolean hasRightWall; public boolean hasLeftWall; public boolean hasBottomWall; public MazeSquare() { hasTopWall = hasRightWall = hasLeftWall = hasBottomWall = false; } } // This Exception class will help pinpoint format errors in the data file. class MazeFormatException extends Exception { public final static int noError = 0; public final static int badRowAndColumnCounts = 1; public final static int wrongNumberOfEntriesInRow = 2; public final static int badEntry = 3; public int lineNumber; public int problem; public MazeFormatException( int line, int prob ) { lineNumber = line; problem = prob; } } // Number of rows and columns in the maze. private int nRows; private int nColumns; // The array of maze squares themselves. MazeSquare[][] square; public static void main( String[] args ) { if( args.length != 1 ) { System.err.println( "Usage: java Maze mazeFileName" ); System.exit( 1 ); } // Read the maze from the file specified on the command line. // Name the variable with a slightly obscure reference // so everyone will know you're a liberal arts student. // Then print the maze to standard output. // // I have made liberal use of exception-handling here, partly // to illustrate exceptions for you in a non-trivial context, // and partly because file input is one of the main places where // exceptions are useful. Users of the maze program will be // interested in learning that (1) they mistyped the file name, // (2) a nasty read error occurred, or (3) where and in what ways // their data files are malformed. Exceptions give us a good // way to provide this kind of feedback. Maze knosos = null; try { knosos = new Maze( args[0] ); } catch( FileNotFoundException e ) { System.err.println( "Can't open " + args[0] + ". Check your spelling." ); System.exit( 1 ); } catch( IOException e ) { System.err.println( "Severe input error. I give up." ); System.exit( 1 ); } catch( MazeFormatException e ) { switch( e.problem ) { case MazeFormatException.badRowAndColumnCounts: System.err.println( args[0] + ", line " + e.lineNumber + ": row and column counts expected" ); break; case MazeFormatException.wrongNumberOfEntriesInRow: System.err.println( args[0] + ", line " + e.lineNumber + ": wrong number of entries" ); break; case MazeFormatException.badEntry: System.err.println( args[0] + ", line " + e.lineNumber + ": non-hexadecimal digit detected" ); break; default: System.err.println( "This should never get printed." ); break; } System.exit( 1 ); } knosos.print( System.out ); } /** * Initializes an empty maze. */ public Maze() { square = null; nRows = nColumns = 0; } /** * Initializes this maze from the specified file. * * @param fileName a file containing a maze description (see above for file format) */ public Maze( String fileName ) throws FileNotFoundException, IOException, MazeFormatException { BufferedReader in = null; in = new BufferedReader( new FileReader( fileName ) ); load( in ); in.close(); } /** * Reads a maze described using the format outlined above, * and stores the results for further processing. * * @param in the input stream from which the data is read */ public void load( BufferedReader in ) throws MazeFormatException { String line; String[] tokens; int lineNumber = 0; // Get the number of rows and columns. Protect // against out-of-range values. try { line = in.readLine(); lineNumber++; tokens = line.split( "\\s+" ); nRows = Integer.parseInt( tokens[0] ); nColumns = Integer.parseInt( tokens[1] ); if( nRows <= 0 || nColumns <= 0 ) throw new Exception(); } catch( Exception e ) { throw new MazeFormatException( lineNumber, MazeFormatException.badRowAndColumnCounts ); } // Allocate the 2D array of MazeSquares. square = new MazeSquare[nRows][nColumns]; for( int i=0; i < nRows; i++ ) for( int j=0; j < nColumns; j++ ) square[i][j] = new MazeSquare(); // Read the square values from input into the array of MazeSquares. for( int i=0; i < nRows; i++ ) { try { line = in.readLine(); lineNumber++; tokens = line.split( "\\s+" ); if( tokens.length != nColumns ) throw new Exception(); } catch( Exception e ) { throw new MazeFormatException( lineNumber, MazeFormatException.wrongNumberOfEntriesInRow ); } for( int j=0; j < nColumns; j++ ) { int squareValue; try { squareValue = Integer.parseInt( tokens[j], 16 ); } catch( NumberFormatException e ) { throw new MazeFormatException( lineNumber, MazeFormatException.badEntry ); } // These are "bitwise AND" operations. We'll discuss them in class. square[i][j].hasTopWall = ((squareValue & 1) != 0); square[i][j].hasRightWall = ((squareValue & 2) != 0); square[i][j].hasBottomWall = ((squareValue & 4) != 0); square[i][j].hasLeftWall = ((squareValue & 8) != 0); } } } /** * Prints this maze to the specified output stream. X's are drawn * wherever this maze is inconsistent. For example, if * square[0][0].hasRightWall is true (there's a wall) but * square[0][1].hasLeftWall is false (there's not a wall), * then this maze is messed up. * * @param out the output stream to which this maze is printed */ public void print( PrintStream out ) { int i, j; for( i=0; i < nRows; i++ ) { // Draw the top walls of this row of squares. out.print( '+' ); for( j=0; j < nColumns; j++ ) { if( i > 0 && square[i][j].hasTopWall != square[i-1][j].hasBottomWall ) out.print( "xxx+" ); else if( square[i][j].hasTopWall ) out.print( "---+" ); else out.print( " +" ); } out.println(); // Draw the left and right walls of this row of squares. if( nColumns > 0 && square[i][0].hasLeftWall ) out.print( '|' ); else out.print( ' ' ); for( j=0; j < nColumns; j++ ) { if( j < nColumns - 1 && square[i][j].hasRightWall != square[i][j+1].hasLeftWall ) out.print( " X" ); else if( square[i][j].hasRightWall ) out.print( " |" ); else out.print( " " ); } out.println(); } if( nRows > 0 ) { // Draw the bottom walls of the bottom row of squares. out.print( '+' ); for( j=0; j < nColumns; j++ ) { if( square[nRows-1][j].hasBottomWall ) out.print( "---+" ); else out.print( " +" ); } out.println(); } } }