CMSI 281
Homework #4

Read [Gray], Chapters 3 through 7. Consider the "Checkpoint" questions as part of your reading assignment: answer them but don't turn in your answers.You are encouraged to do all of these because many of the quiz and final exam questions will be based on these.

Use your CVS repository (or an SVN repository if you prefer) while working on this assignment.

The real-life programming projects are back! Please provide professionally crafted solutions to the following:

  1. Write a class called LinkedPolynomial that implements the Polynomial interface I wrote for the Homework #1 answers. Represent polynomials internally with a doubly-linked circular list, with a header node, that remains sorted by degree at all times. Also write a unit test.
  2. Write a MazeSolver class that contains one method
        /**
         * Moves a rat from (x1,y1) to (x2,y2), filling in the cells
         * as it goes, and notifying a listener at each step.
         */
        public boolean solve(Maze maze, int x1, int y1, int x2, int y2,
                MazeListener listener) {
            . . .
        }
    
    You are required to "solve" the maze using a stack-based backtracking algorithm; you must use the SimpleStack class in the online course notes. You are not required to turn in a unit test, but you should write one anyway. I've already written the Maze class and a silly graphics program that will use your solver:
    package edu.lmu.cs.maze;
    
    /**
     * A maze simple rectangular maze.
     */
    public class Maze {
    
        // A maze is a rectangular array of cells, whose width and height
        // are stored separately for convenience.  The class is set up
        // so that mazes can't get whacked out of a rectangular shape.
        private Cell[][] cells;
        private int width;
        private int height;
    
        // Hide the constructor, since we'll only allow construction
        // via the factory method that constructs the maze from a string.
        private Maze() {
        }
    
        /**
         * An (x,y) position, with methods returning whether
         * or not the position is within the maze, whether or
         * not the position is open, and its neighbors.
         */
        public class Location {
            private int x;
            private int y;
            Location(int x, int y) {this.x = x; this.y = y;}
            boolean isLegal() {return x >= 0 && x < width && y >= 0 && y < height;}
            boolean isOpen() {return isLegal() && cells[y][x] == Cell.OPEN;}
            Location above() {return new Location(x, y - 1);}
            Location below() {return new Location(x, y + 1);}
            Location left() {return new Location(x - 1, y);}
            Location right() {return new Location(x + 1, y);}
            void set(Cell cell) {cells[y][x] = cell;}
            Cell get() {return cells[y][x];}
            public boolean isAt(int x, int y) {return this.x == x && this.y == y;}
        }
    
        /**
         * A simple cell value.  A cell can be open (meaning a rat has
         * never visited it), a wall, part of the rat's current path,
         * or "tried" (meaning the rat found it to be part of a dead end.
         */
        public static enum Cell {
            OPEN('.'), WALL('#'), TRIED('x'), PATH('o'), RAT('r');
            private char display;
            private Cell(char display) {this.display = display;}
            public String toString() {return Character.toString(display);}
        }
    
        /**
         * A listener interface for responding to maze changes.
         */
        public interface MazeListener {
            void mazeChanged(Maze maze);
        }
    
        /**
         * Build and return a new maze given a description in the
         * form of a string of 0's and 1's -- 0's for open spaces and
         * 1's for walls.  Each row is separated by whitespace.
         * For example "1100 0100 1001" represents a 3-row, 4-column
         * maze that looks like this:
         * <pre>
         *     ##..
         *     .#..
         *     #..#
         * </pre>
         */
        public static Maze fromText(String description) {
            Maze maze = new Maze();
            String[] rows = description.trim().split("\\s+");
            maze.height = rows.length;
            maze.width = rows[0].length();
            maze.cells = new Cell[maze.height][maze.width];
            for (int y = 0; y < maze.height; y++) {
                String row = rows[y];
                if (!row.matches("[01]+")) {
                    throw new IllegalArgumentException("Illegal characters in maze");
                }
                if (row.length() != maze.width) {
                    throw new IllegalArgumentException("Non-rectangular maze");
                }
                for (int x = 0; x < maze.width; x++) {
                    maze.cells[y][x] = row.charAt(x) == '0' ? Cell.OPEN : Cell.WALL;
                }
            }
            return maze;
        }
    
        /**
         * Replaces all non-walls with open marks.  Used to clean up
         * a maze after a rat ran through it.
         */
        public void clear() {
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (cells[y][x] != Cell.WALL) cells[y][x] = Cell.OPEN;
                }
            }
        }
    
        /**
         * Returns a textual description of the maze, separating each
         * row with a newline.
         */
        public String toString() {
            StringBuilder builder = new StringBuilder(height * width);
            for (Cell[] row: cells) {
                for (Cell cell: row) {
                    builder.append(cell);
                }
                builder.append("\n");
            }
            return builder.toString();
        }
    }
    
    package edu.lmu.cs.maze;
    
    import java.awt.BorderLayout;
    import java.awt.Font;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    
    /**
     * Really cheap application that "animates" a rat moving through
     * a maze.  It simply keeps refreshing a monospaced font rendering
     * of the maze's toString() output.
     */
    public class SwingMazeDemo {
    
        public static class MazePanel extends JPanel implements Maze.MazeListener {
            private JTextArea display = new JTextArea(40, 40);
    
            public MazePanel() {
                setLayout(new BorderLayout());
                display.setFont(new Font("Monospaced", Font.PLAIN, 32));
                add(new JScrollPane(display), BorderLayout.CENTER);
            }
    
            /**
             * Waits a second then refreshes the maze in the textarea.
             */
            public void mazeChanged(Maze maze) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignored) {
                }
                display.setText(maze.toString());
            }
        }
    
        public static void main(String[] args) throws Exception {
            JFrame frame = new JFrame("Maze");
            MazePanel panel = new MazePanel();
            frame.setSize(800, 600);
            frame.getContentPane().add(panel);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setVisible(true);
    
            Maze maze = Maze.fromText("000110000 110100010 010000010 000100000");
            boolean success = new MazeSolver().solve(maze, 6, 0, 1, 3, panel);
            frame.setTitle(success ? "Made it! " : "Stuck");
        }
    }
    
  3. Write a utility class called ListUtils with two methods:
    1. Write a utility method that takes a java.util.List and returns a new list made by duplicating each element in the original as in this example: stutter([A,B,C]) = [A,A,B,B,C,C].
    2. Write a utility method that interleaves two java.util.List objects. That is, interleave([A,Z,G,Q], [P, X, R]) would return a new list [A, P, Z, X, G, R, Q].
    Also write a unit test.
  4. Implement a class for the following bizarre kind of sequence. A sequence can be opened in either read-only or write-only mode. Items are read in order starting from the first element; though at any time you may reset the read pointer to the first element. Writing to the sequence is allowed only by appending items to the end; at any time you can erase all the items in the sequence (there is no way to selectively delete an item). A sequence must be closed before its mode can be changed. Provide operations to query the current size, the current mode, etc. Use relevant exceptions. You may choose either an array-based or a linked representation. Write a unit test, too.

With this assignment, version control is now a requirement. Organize your work in your CVS (or SVN) repository with the following structure:

homework
  cmsi281
    src
      main
        docs
          hw4.tex
          <diagram source files>, if any
          <images>, if any
        java
          edu
            lmu
              cs
                collections
                  Sequence.java
                maze
                  MazeSolver.java
                math
                  Polynomial.java (you should already have this from HW 2)
                  LinkedPolynomial.java (implements Polynomial)
                collections
                  ListUtils.java
                  Stack.java
                  SimpleStack.java
                maze
                  Maze.java
                  SwingMazeApplication.java
      test
        java
          edu
            lmu
              cs
                collections
                  ListUtilsTest.java
                  SequenceTest.java
                math
                  LinkedPolynomialTest.java

Your LaTeX file will contain solutions to each problem, numbered, and in order. You may embed source code in the document, or give the answer as "See <filename>" and submit the sources for these answers at the end of the document. Generate a pdf from the LaTeX file and hand in a printed copy. Only turn in printed copies of your own files; do not waste trees by printing the sources for the code I gave you.

Notes