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