/* MazeToolbox.java * Created on February 20, 2008 */ import java.io.*; import java.util.*; /** This class contains a number of Maze utility methods: * Reading and writing maze files, generating mazes, etc. * @author William Krieger */ public class MazeToolbox { /** Wall masks used to define the walls presnet at each cell. * "Or" these masks together to add walls to the mask. */ public final static int TOP_WALL = 8; public final static int RIGHT_WALL = 4; public final static int BOTTOM_WALL = 2; public final static int LEFT_WALL = 1; public final static int NO_WALLS = 0; public final static int ALL_WALLS = 15; // These methods detect the presence of a specific wall in a mask. public static boolean hasTopWall( int m) { return ((m & TOP_WALL) != 0); } public static boolean hasRightWall( int m) { return ((m & RIGHT_WALL) != 0); } public static boolean hasBottomWall( int m) { return ((m & BOTTOM_WALL) != 0); } public static boolean hasLeftWall( int m) { return ((m & LEFT_WALL) != 0); } // These methods add a wall to a given mask. public static int addTopWall( int m) { return (m | TOP_WALL); } public static int addRightWall( int m) { return (m | RIGHT_WALL); } public static int addBottomWall( int m) { return (m | BOTTOM_WALL); } public static int addLeftWall( int m) { return (m | LEFT_WALL); } /** Read a maze from a file. The format is CSC 210 maze file format. * @param file The input file. * @return The maze created from reading and processing the input file. * On error, null is returned. */ public static AbstractMaze readMazeFile( File file) throws IOException { AbstractMaze maze = null; // return maze String name = null; // these local vars hold tag values int rows = 0, cols = 0; int startRow = 0, startCol = 0; int endRow = 0, endCol = 0; int wallMask = 0; // we'll use a Scanner to parse the file Scanner scan = new Scanner( file); // is this a CSC 210 maze file? String magicNumber = scan.next(); if( ! magicNumber.equals( "%csc210_maze%")) { System.out.println( "Error: No magic number found. This is not a CSC 210 maze file."); return null; } // OK, it a maze file, so let's parse it one tag at a time while( scan.hasNext()) { String tag = scan.next(); if( tag.charAt(0) == '#') { continue; } // skip comment line if( tag.equalsIgnoreCase( "name")) { // name tag name = scan.next(); } else if( tag.equalsIgnoreCase( "size")) { // size tag rows = scan.nextInt(); cols = scan.nextInt(); } else if( tag.equalsIgnoreCase( "start")) { // start tag startRow = scan.nextInt(); startCol = scan.nextInt(); } else if( tag.equalsIgnoreCase( "end")) { // end tag endRow = scan.nextInt(); endCol = scan.nextInt(); } else if( tag.equalsIgnoreCase( "cells")) { // cells tag // this must be the last tag, do some checking boolean tagsOK = true; if( rows <= 0 || cols <= 0) { System.out.println( "Error: Bad 'size' tag"); tagsOK = false; } if( name == null) { System.out.println( "Error: Bad 'name' tag"); tagsOK = false; } if( startRow < 0 || startRow >= rows || startCol < 0 || startCol >= cols) { System.out.println( "Error: Bad 'start' tag"); tagsOK = false; } if( endRow < 0 || endRow >= rows || endCol < 0 || endCol >= cols) { System.out.println( "Error: Bad 'end' tage"); tagsOK = false; } if( tagsOK) { // create your maze and go! maze = new MyMaze2( name, rows, cols); maze.setStartPoint( startRow, startCol); maze.setEndPoint( endRow, endCol); maze.addAllWalls(); // get the wall mask for each cell and we're done for( int curRow = 0; curRow < rows; curRow++) { for( int curCol = 0; curCol < cols; curCol++) { wallMask = scan.nextInt(); if( wallMask < 0 || wallMask >= 16) { System.out.println( "Warning: Bad 'cell' mask value=" + wallMask + ", changed to 15"); wallMask = ALL_WALLS; } if( !hasTopWall( wallMask)) { maze.removeWall( curRow, curCol, curRow-1, curCol); } if( !hasRightWall( wallMask)) { maze.removeWall( curRow, curCol, curRow, curCol+1); } if( !hasBottomWall( wallMask)) { maze.removeWall( curRow, curCol, curRow+1, curCol); } if( !hasLeftWall( wallMask)) { maze.removeWall( curRow, curCol, curRow, curCol-1); } } } } } } return maze; } /** Write the maze to the specified file in CSC 210 format. * @param maze The maze to be written. * @param file The output file. */ public static void writeMazeFile( AbstractMaze maze, File file) throws IOException { PrintStream stream = new PrintStream( file); // write the header stuff: magic number, name, size, start, end points stream.println( "%csc210_maze%"); // magic number stream.println( "name " + maze.getName()); stream.println( "size " + maze.getNumRows() + " " + maze.getNumCols()); stream.println( "start " + maze.getStartRow() + " " + maze.getStartCol()); stream.println( "end " + maze.getEndRow() + " " + maze.getEndCol()); // write the wall mask for each cell stream.println( "cells"); int mask; for( int row = 0; row < maze.getNumRows(); row++) { for( int col = 0; col < maze.getNumCols(); col++) { mask = NO_WALLS; if( maze.hasTopWall( row, col)) { mask = addTopWall( mask); } if( maze.hasRightWall( row, col)) { mask = addRightWall( mask); } if( maze.hasBottomWall( row, col)) { mask = addBottomWall( mask); } if( maze.hasLeftWall( row, col)) { mask = addLeftWall( mask); } stream.print( mask); stream.print( " "); } stream.print("\n"); } } /** Tiny test main(). * @param args Unused. */ public static void main( String args[]) { try { File file = new File( "../test01.txt"); AbstractMaze maze = readMazeFile( file); } catch( IOException exc) { System.out.println( exc); } } }