/*-------------------------------------------------------------------* * NTU Student: Java With Engineering Applications, Spring 1999 * Assignment # 2 - Java How To Program 2nd edition, Deitel & Deitel * Problem 5.38 on page 278 - Translate a maze recursively * * Michael Kusmierz * Xerox Corporation * Rev 1.0 Apr 20, 1999 * * This code represents a students work in progress * and is not to be used beyond that purpose. *-------------------------------------------------------------------*/ import java.applet.*; import java.awt.*; import java.awt.event.*; /** * Applet to recursively solve a maze. */ public class Maze extends Applet implements ActionListener { //--- anonymous array with a maze, "#"=walls, "."=posible paths private char aMaze1[][]; //--- instance variables static final int kXSTARTPOS = 0; static final int kYSTARTPOS = 2; //--- other tests case // static final int kXSTARTPOS = 11; // static final int kYSTARTPOS = 4; char chrNextDirection; //Next direction to go boolean bolStartup = true; //state = start of maze condition int iWatchDogCount = 1; // prevent recursive lock up int x; //Current position in the array int y; boolean bolMapOrXmode = false; //default to display X in maze boolean bolHighSpeedDisplay = true; //speed of display (depends on cpu speed) private Graphics gMazeGraphics; //graphics g for display maze progress //--- GUI components static final int kINIT_X = 25; //position of array on applet static final int kINIT_Y = 40; private Button ivjbtnStart = null; private TextArea ivjTextArea1 = null; private Button ivjbtnStartMap = null; private Button ivjbtnClear = null; private Button ivjbtnDisplaySpeed = null; /* *********************************************************************** * respond to the start button */ public void actionPerformed( ActionEvent e ) { String sBtnSelected; sBtnSelected = e.getActionCommand(); //--- User Selected Map mode or X display mode or Clear if (sBtnSelected.compareTo("Start") == 0) { bolMapOrXmode = false; ivjTextArea1.setVisible(false); ivjbtnStart.setEnabled(false); ivjbtnStartMap.setEnabled(false); ivjbtnStartMap.setEnabled(false); ivjbtnClear.setEnabled(false); //--- start the maze at position 0,2 mazeTraverse(aMaze1,kXSTARTPOS,kYSTARTPOS); ivjbtnClear.setEnabled(true); } else if (sBtnSelected.compareTo("Start Map") == 0) { // show map bolMapOrXmode = true; ivjTextArea1.setVisible(true); ivjbtnStart.setEnabled(false); ivjbtnStartMap.setEnabled(false); ivjbtnClear.setEnabled(false); //--- start the maze at position 0,2 mazeTraverse(aMaze1,kXSTARTPOS,kYSTARTPOS); ivjbtnClear.setEnabled(true); } else if (sBtnSelected.compareTo("Clear") == 0) { // clear the screen showStatus("Wait..."); ivjTextArea1.setVisible(false); ivjbtnStart.setEnabled(false); ivjbtnStartMap.setEnabled(false); ivjbtnClear.setEnabled(false); startup(); repaint(); //paintArray(aMaze1); showStatus("Ready"); //--- enable the buttons after traversal completed ivjbtnStart.setEnabled(true); ivjbtnStartMap.setEnabled(true); ivjbtnClear.setEnabled(true); } else if (sBtnSelected.compareTo("High Display Speed Selected") == 0) { bolHighSpeedDisplay = false; ivjbtnDisplaySpeed.setLabel("Low Display Speed Selected"); } else if (sBtnSelected.compareTo("Low Display Speed Selected") == 0) { bolHighSpeedDisplay = true; ivjbtnDisplaySpeed.setLabel("High Display Speed Selected"); } } /** ********************************************************************** * Checks if compassHeading is passable, * returns true for passable and increments forward, false for walls. */ private boolean checkAndMove(char a[][], char compassHeading) { switch (compassHeading) { case 'N': { if (a[y-1][x] != '#') { y--; a[y][x] = testDone(compassHeading); return(true); } break; } case 'S': { if (a[y+1][x] != '#') { y++; a[y][x] = testDone(compassHeading); return(true); } break; } case 'E': { if (a[y][x+1] != '#') { x++; a[y][x] = testDone(compassHeading); return(true); } break; } case 'W': { if (a[y][x-1] != '#') { x--; a[y][x] = testDone(compassHeading); return(true); } break; } } //end switch return(false); //default } /* *********************************************************************** * Gets the applet information. * @return java.lang.String */ public String getAppletInfo() { return "Maze created using VisualAge for Java."; } /** * Return the btnClear property value. * @return java.awt.Button */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private Button getbtnClear() { if (ivjbtnClear == null) { try { ivjbtnClear = new java.awt.Button(); ivjbtnClear.setName("btnClear"); ivjbtnClear.setBounds(180, 217, 56, 23); ivjbtnClear.setLabel("Clear"); // user code begin {1} // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } }; return ivjbtnClear; } /** * Return the btnDisplaySpeed property value. * @return java.awt.Button */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private Button getbtnDisplaySpeed() { if (ivjbtnDisplaySpeed == null) { try { ivjbtnDisplaySpeed = new java.awt.Button(); ivjbtnDisplaySpeed.setName("btnDisplaySpeed"); ivjbtnDisplaySpeed.setBounds(13, 217, 165, 23); ivjbtnDisplaySpeed.setLabel("High Display Speed Selected"); // user code begin {1} bolHighSpeedDisplay = true; // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } }; return ivjbtnDisplaySpeed; } /** * Return the btnStart property value. * @return java.awt.Button */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private Button getbtnStart() { if (ivjbtnStart == null) { try { ivjbtnStart = new java.awt.Button(); ivjbtnStart.setName("btnStart"); ivjbtnStart.setBounds(13, 245, 56, 23); ivjbtnStart.setLabel("Start"); // user code begin {1} // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } }; return ivjbtnStart; } /** * Return the btnStartMap property value. * @return java.awt.Button */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private Button getbtnStartMap() { if (ivjbtnStartMap == null) { try { ivjbtnStartMap = new java.awt.Button(); ivjbtnStartMap.setName("btnStartMap"); ivjbtnStartMap.setBounds(13, 274, 56, 23); ivjbtnStartMap.setLabel("Start Map"); // user code begin {1} // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } }; return ivjbtnStartMap; } /** * Return the TextArea1 property value. * @return java.awt.TextArea */ /* WARNING: THIS METHOD WILL BE REGENERATED. */ private TextArea getTextArea1() { if (ivjTextArea1 == null) { try { ivjTextArea1 = new java.awt.TextArea(); ivjTextArea1.setName("TextArea1"); ivjTextArea1.setBounds(77, 245, 159, 63); // user code begin {1} // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } }; return ivjTextArea1; } /* *********************************************************************** * Called whenever the part throws an exception. * @param exception java.lang.Throwable */ private void handleException(Throwable exception) { /* Uncomment the following lines to print uncaught exceptions to stdout */ // System.out.println("--------- UNCAUGHT EXCEPTION ---------"); // exception.printStackTrace(System.out); } /* *********************************************************************** * INITIALIZATION */ public void init() { super.init(); try { setName("Maze"); setLayout(null); setSize(426, 317); add(getbtnStart(), getbtnStart().getName()); add(getTextArea1(), getTextArea1().getName()); add(getbtnStartMap(), getbtnStartMap().getName()); add(getbtnClear(), getbtnClear().getName()); add(getbtnDisplaySpeed(), getbtnDisplaySpeed().getName()); // user code begin {1} //--- setup listener on buttons ivjbtnStart.addActionListener(this); ivjbtnStartMap.addActionListener(this); ivjbtnClear.addActionListener(this); ivjbtnDisplaySpeed.addActionListener(this); gMazeGraphics = this.getGraphics(); startup(); // user code end } catch (java.lang.Throwable ivjExc) { // user code begin {2} // user code end handleException(ivjExc); } } /* *********************************************************************** * main entrypoint - starts the part when it is run as an application * @param args java.lang.String[] */ public static void main(java.lang.String[] args) { try { Frame frame; try { Class aFrameClass = Class.forName("com.ibm.uvm.abt.edit.TestFrame"); frame = (Frame)aFrameClass.newInstance(); } catch (java.lang.Throwable ivjExc) { frame = new Frame(); } Maze aMaze; Class iiCls = Class.forName("Maze"); ClassLoader iiClsLoader = iiCls.getClassLoader(); aMaze = (Maze)java.beans.Beans.instantiate(iiClsLoader,"Maze"); frame.add("Center", aMaze); frame.setSize(aMaze.getSize()); frame.setVisible(true); } catch (Throwable exception) { System.err.println("Exception occurred in main() of java.applet.Applet"); exception.printStackTrace(System.out); } } /* *********************************************************************** * This method traverses the array until an exit is found * @param a char[][] - array to traverse * @param iStartX int - starting x position in maze * @param iStartY int - starting y position in maze */ private void mazeTraverse(char a[][],int iStartX,int iStartY) { //--- Startup... use maze iStartX and Y as the start position if (bolStartup == true) { bolStartup = false; //--- get initial positions x=iStartX; y=iStartY; chrNextDirection = 's'; //s=start,not to be confused with S=South a[y][x] = chrNextDirection; //--- print start on maze printMazeProgress(a,gMazeGraphics,x,y); ivjTextArea1 .setText("Journey:\n"+"Start="+y+","+x+"\n Row, Col, Direction At Entry\n"); //--- START of maze ONLY: Check for start & find direction to go // At the START ONLY: Assumes STARTlocation is valid // (not a corner and has an opening towards the center of the maze). if (y<1) { chrNextDirection = 'S'; //Start of maze at top, go south one step y++; } else if (y>10) { chrNextDirection = 'N'; //Start on bottom, go north y--; } else if (x<1) { chrNextDirection = 'E'; //Start on left side, go east x++; } else if (x>10) { chrNextDirection = 'W'; //Start on right side, go west x--; } //--- Display results on applet printMazeProgress(a,gMazeGraphics,x,y); } //--- Select NEXT STEP DIRECTION, print it and MOVE there chrNextDirection = setNextDirection( a, chrNextDirection ); //--- Check for END of maze if (a[y][x] == 'F') { showStatus("Done in "+ iWatchDogCount + " steps."); //--- Display finish on applet printMazeProgress(a,gMazeGraphics,x,y); return; } //--- Recurse Watchdog iWatchDogCount++; if (iWatchDogCount >=250) { //traversed array twice+ some showStatus("Unable to solve maze...is the exit missing. Steps ="+iWatchDogCount); return; } //--- RECURSE mazeTraverse(a,x,y); } /* *********************************************************************** * Paint used to print the initial array on the screen */ public void paint(Graphics g) { g.drawString("Maze Solving Machine - uses recursion ",25,25); printMazeArray(aMaze1,g); } /* *********************************************************************** * This method was created to print an array * @param a char[][] - the char array to be printed * @param g java.awt.Graphics */ private void printMazeArray(char a[][],Graphics g) { int x = kINIT_X; int y = kINIT_Y; for (int i = 0; i< a.length; i++ ) { // # row (y) for (int j = 0; j < a[i].length; j++) { // # col (x) g.drawString("" + a[i][j] ,x ,y ); // a(row,col)=a(y,x) x += 15; } x = kINIT_X; //reset x to prepare for the next line y +=15; //pos the next line y start position } } /**************************************************************************** * This method was created to speed up the painting array process * It paints a single array element in the correct position a(y,x). * @param a char[][] -array(y,x) , the source of the value to print * @param g java.awt.Graphics * @param x int -x array position to print * @param y int -y array position to print */ private void printMazeProgress(char a[][],Graphics g,int x, int y) { if (bolHighSpeedDisplay) { g.drawString(""+ a[y][x], (kINIT_X + x*15), (kINIT_X+15 + y*15)); } else { for (int t=0; t<100; t++) //slow it down so we can see progress g.drawString(""+ a[y][x], (kINIT_X + x*15), (kINIT_X+15 + y*15)); } } /* *********************************************************************** * This method chooses what direction to go next, takes a step and prints it. * It assumes the maze is surrounded by a wall(#) * * It assumes the right hand rule.Any thing other then # is a path. * * @return char - direction moved * @param direction char is the current direction of travel (ignored on start) * returns A:array out of bound, F-finished, N,S,E,W-tested direction of travel * * @param x int - x initial position and next position * @param y int - y initial position and next position */ private char setNextDirection(char a[][], char direction) { int i; try { //--- map display & log or just the simple path marked with x if (bolMapOrXmode == true) { a[y][x] = direction; ivjTextArea1.append(" "+x+" , "+y+" , "+direction+"\n"); } else a[y][x] = 'X'; //--- Display results on applet printMazeProgress(a,gMazeGraphics,x,y); //--- PICK OF FOUR DIRECTIONS to go, based on right hand rule switch (direction) { //--- drag your right hand means look clockwise first, translate counter clokwise case 'N': if (checkAndMove(a, 'E')) direction='E'; else if (checkAndMove(a, 'N')) direction='N'; else if (checkAndMove(a, 'W')) direction='W'; else if (checkAndMove(a, 'S')) direction = 'S'; break; case 'E': if (checkAndMove(a, 'S')) direction='S'; else if (checkAndMove(a, 'E')) direction='E'; else if (checkAndMove(a, 'N')) direction='N'; else if (checkAndMove(a, 'W')) direction='W'; break; case 'S': if (checkAndMove(a, 'W')) direction='W'; else if (checkAndMove(a, 'S')) direction='S'; else if (checkAndMove(a, 'E')) direction='E'; else if (checkAndMove(a, 'N')) direction='N'; break; case 'W': if (checkAndMove(a, 'N')) direction='N'; else if (checkAndMove(a, 'W')) direction='W'; else if (checkAndMove(a, 'S')) direction='S'; else if (checkAndMove(a, 'E')) direction='E'; break; } //end switch return (direction); } catch (java.lang.Throwable exc ) { //Catch out of array bounds conditions showStatus("Array out of bounds in moving to next direction (setNextDirection)."); return ('A'); } } /**************************************************************************************************** * initialization for restart of the maze program */ private void startup() { iWatchDogCount = 1; //reset counter to prevent lockup errors bolStartup = true; ivjTextArea1.setVisible(false); //trace map off //--- initialize the array with a maze, "#"=walls, "."=posible paths aMaze1 = new char[][] { {'#','#','#','#','#','#','#','#','#','#','#','#'}, {'#','.','.','.','#','.','.','.','.','.','.','#'}, {'.','.','#','.','#','.','#','#','#','#','.','#'}, {'#','#','#','.','#','.','.','.','.','#','.','#'}, {'#','.','.','.','.','#','#','#','.','#','.','.'}, {'#','#','#','#','.','#','.','#','.','#','.','#'}, {'#','.','.','#','.','#','.','#','.','#','.','#'}, {'#','#','.','#','.','#','.','#','.','#','.','#'}, {'#','.','.','.','.','.','.','.','.','#','.','#'}, {'#','#','#','#','#','#','.','#','#','#','.','#'}, {'#','.','.','.','.','.','.','#','.','.','.','#'}, {'#','#','#','#','#','#','#','#','#','#','#','#'} }; //--- WAIT for a user to select a button } /** ****************************************************************************** * This method returns a 'F' if the outside of the maze is reached (maze wall) * otherwise a X * @return char */ private char testDone(char chrDirection) { //--- FINISH maze check; if statements shown for display of further results if (y<1) { chrDirection = 'F'; //Finish of maze at top } else if (y>10) { chrDirection = 'F'; //Finish on bottom } else if (x<1) { chrDirection = 'F'; //Finish on left side } else if (x>10) { chrDirection = 'F'; //Finish on right side }else //not done yet if (bolMapOrXmode == false) chrDirection = 'X'; return (chrDirection); } }