/** Graphical maze running program. For the program to work, the maze * must be surrounded by walls! The Maze input is found in class MazeValues. * See that class for codes of entries for walls, start, finish, etc. * * Written by Kim Tabtiang and Kim Bruce * Revised by Kim Bruce 3/04 */ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import structure.*; import objectdraw.*; public class GraphicsMaze extends WindowController { // constants protected static final int HGAP = 10; // gap between buttons protected static final int VGAP = 20; protected static final int HEIGHT = 25; // height of cell protected static final int WIDTH = 20; // width of cell protected static final int TOP = 100; // coords of upper-left protected static final int LEFT = 30; // corner of grid protected static final int HSPACE = 2; // space between cells protected static final int VSPACE = 2; protected static final int NUM_MAZES = 4; protected static final int SLOW_SPEED = 1; protected static final int FAST_SPEED = 10; // instance variables protected int numRows, numCols; // Dimensions of maze protected Position start; // Position of start and finish of maze protected Position finish; protected Position current; // Current position in maze protected Stack path; // Path from start to current protected MazeGrid grid; // Grid displaying maze protected JButton startButton; // Button to start solving maze protected JButton clearMaze; // Button to clear maze solution protected JComboBox mazeChoice; // Button to allow choice of mazes protected JSlider speedSlider; // Slider to control speed of solving protected JTextArea stackDisplay; // Show stack while solving protected MazeValues minput; // Source for maze data protected boolean success; // Remember whether succeeded in last operation protected MazeSolverInterface mazeThread; /** * post: Loaded initial maze and displayed it in a grid. Buttons set up to * allow user to select, start solving, or clear maze. */ public void begin() { minput = new MazeValues(); // Set up start button startButton = new JButton("Push to solve maze"); startButton.setBackground(MazeGrid.START_COLOR); startButton.addActionListener(new StartListener(this)); // Set up clear button ClearAndChooseListener clChooseListener = new ClearAndChooseListener(); clearMaze = new JButton("Push to clear maze"); clearMaze.setBackground(MazeGrid.FINISH_COLOR); clearMaze.addActionListener(clChooseListener); // Set up choice button to select maze to solve mazeChoice = new JComboBox(); for (int item = 0; item < NUM_MAZES; item++) { mazeChoice.addItem("Maze " + item); } mazeChoice.addActionListener(clChooseListener); JPanel bottomPanel = new JPanel(new GridLayout(2,1)); speedSlider = new JSlider(JSlider.HORIZONTAL, SLOW_SPEED,FAST_SPEED,SLOW_SPEED); speedSlider.addChangeListener(new SpeedListener()); JLabel speedLabel = new JLabel("Adjust speed",JLabel.RIGHT); JPanel speedPanel = new JPanel(new GridLayout(1,2)); speedPanel.add(speedLabel); speedPanel.add(speedSlider); // Set up panel to hold buttons JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(1, 3, HGAP, VGAP)); buttonPanel.add(startButton); buttonPanel.add(mazeChoice); buttonPanel.add(clearMaze); // Add speedPanel and buttonPanel to bottomPanel bottomPanel.add(speedPanel); bottomPanel.add(buttonPanel); // Put panel on bottom of screen Container contentPane = getContentPane(); contentPane.add(BorderLayout.SOUTH, bottomPanel); // Create area to display stack contents stackDisplay = new JTextArea(40,10); stackDisplay.setEditable(false); JScrollPane scrollableDisplay = new JScrollPane(stackDisplay); contentPane.add(BorderLayout.WEST, scrollableDisplay); JLabel title = new JLabel("CS 136 MAZE SOLVER\n\n",JLabel.CENTER); title.setFont(new Font("myLabel",Font.ITALIC,36)); contentPane.add(BorderLayout.NORTH, title); // Create object with data for multiple mazes // Set up grid displaying the maze. loadGrid(0); // Load first maze if (success) { // Successfully loaded in maze showStatus("setting up grid"); } else { showStatus("Error - maze load failed"); } } /** * Load info for maze from MazeInput class. Used because applets can't read * files. 's' marks the start. 'f' marks the finish. Both start and finish * are considered to be spaces (non-walls). any other char marks a wall. * Maze must have a start and finish. * * pre: selection is legal number for a maze post: New maze is loaded into * data. success is true iff maze has both start and finish */ public void loadGrid(int selection) { // Load appropriate input for maze String[] input = minput.getInput(selection); // Array of strings, one for each row numRows = minput.getNumRows(); // Number of rows in maze numCols = minput.getNumCols(); // Number of columns in maze showStatus("Loading grid"); grid = new MazeGrid(LEFT, TOP, numRows, numCols, WIDTH, HEIGHT, canvas); for (int row = 0; row < numRows; row++) { // process each row in maze for (int col = 0; col < numCols; col++) { // process each entry char c = input[row].charAt(col); switch (c) { case 's' : // start position start = new Position(row, col); grid.setStart(start); break; case 'f' : // finish position finish = new Position(row, col); grid.setFinish(finish); break; case ' ' : // open space grid.setOpen(new Position(row, col)); break; case '#' : // wall } } } // Require at least one start & finish success = ((start != null) && (finish != null)); showStatus("Grid loaded"); } /** * post: Return printable version of maze held in data. */ public String toString() { StringBuffer s = new StringBuffer(); // Create updatable string for (int row = 0; row < numRows; row++) { for (int col = 0; col < numCols; col++) { Position posn = new Position(row, col); if (grid.isStart(posn)) { s.append('s'); } else if (grid.isFinish(posn)) { s.append('f'); } else if (grid.isOpen(posn)) { s.append(' '); } else if (grid.isOnPath(posn)) { s.append('p'); } else if (grid.isFailed(posn)) { s.append('f'); } else if (grid.isWall(posn)) { s.append('#'); } else { s.append('e'); } } s.append('\n'); // Add carriage return after each row } return s.toString(); } public class StartListener implements ActionListener { protected GraphicsMaze gMaze; public StartListener(GraphicsMaze gMaze) { this.gMaze = gMaze; } /** * Post: If clicked start and maze successfully loaded, then attempted to * solve maze. If selected new maze, then loaded and displayed. If * clicked clear, then old path removed and maze redisplayed */ public void actionPerformed(ActionEvent e) { if (success) { // start solving maze gMaze.showStatus("Running maze"); stackDisplay.setText("Path contents:\n"); mazeThread = new RecMazeSolver(gMaze, grid, start, finish, speedSlider.getValue(),stackDisplay); mazeThread.start(); } } } public class ClearAndChooseListener implements ActionListener { /** * Post: If clicked start and maze successfully loaded, then attempted to * solve maze. If selected new maze, then loaded and displayed. If * clicked clear, then old path removed and maze redisplayed */ public void actionPerformed(ActionEvent e) { // load data for maze and set up grid to display it loadGrid(mazeChoice.getSelectedIndex()); if (!success) { showStatus("Error - maze load failed"); } } } // Listener class to respond to changes in slider by adjusting // speed of mazeThread public class SpeedListener implements ChangeListener { /* * Adjust speed of mazeThread */ public void stateChanged(ChangeEvent e) { if (mazeThread != null) { mazeThread.setSpeed(speedSlider.getValue()); } } } }