CSCI-UA 102 (Data Structures)

Project 5
due date: December 3

submission mode: individual

Project 5: Binary Tree Maze


You may discuss any of the assignments with your classmates and tutors (or anyone else) but all work for all assignments must be entirely your own. Any sharing or copying of assignments will be considered cheating (this includes posting of partial or complete solutions on Ed, GitHub, Discord, Groupme, … or any other forum). If you get significant help from anyone, you should acknowledge it in your submission (and your grade will be proportional to the part that you completed on your own). You are responsible for every line in your program: you need to know what it does and why. You should not use any data structures and features of Java that have not been covered in class (or the prerequisite class). If you have doubts whether or not you are allowed to use certain structures, just ask your instructor.

If your submitted code matches closely or exactly code submitted by another person in the class, it will get a zero grade and the offense will be reported to the department and the dean.




Introduction and objectives

The goal of this project is to implement a program that uses a binary search tree to model a binary tree maze exploration: Our hero starts at the root of the maze and needs to find their way to the exit. Your program will produce a list of all the possible paths that our hero can take to find the exit.

The binary tree maze is represented by a binary search tree. Nodes in this tree represent places where the paths fork. They are also places at which our hero can earn some life points. They use one life point to get from one node to the next. When our hero starts on a particular path, they cannot go back. Some of the paths lead to trap doors (not a desirable scenario), others lead to exits. Your job is to supply a list of the paths that lead to exits. See example below for more details.

Start early! This project may not seem like much coding, but debugging and testing always takes time, especially for recursive algorithms.


Your program has to be a console based program (no graphical interface). The user should not be prompted for any information (all required information for the program to run is provided in the input file given as a command line argument). The output of the program should be printed to standard output stream. Any error messages should be printed to the standard error stream.

The program is started from the command line (or run within an IDE). It expects one command line argument: the name of the input file.

The input file describes the binary tree maze. Each line in the input is a single node in the maze (node in a BST). The format of each line is as follows:

` LABEL LIFE_POINTS`

In the above, LABEL is a string with the name of the node. The alphanumeric comparison of the labels determines the shape of the tree. LIFE_POINTS is a number of life points that our hero can collect at that node.

See below for an example.

If the program is executed with non-existent or invalid command line argument, it should print an error message and terminate.

If the file contains any additional (and invalid) strings in the description of a node, those extra strings should be ignored. (Each valid line contains only one string followed by one number.)

The program should not be interactive. All input should be provided as the command line arguments. The user should not be prompted for any additional information.

The program has to calculate all possible paths through the maze that lead to the valid exits that are reachable by our hero. Valid exits are leaves in the tree that are at the lowest level. But our hero needs to have enough life points to reach such an exit. If the life points run out before the exit can be reached, the path to such an exit should not be listed among the ones recommended by your program.

The program output should consist of a number of lines equal to the number of valid paths. Each valid path should consist of a space separated list of the node labels in order in which they are followed from the root of the tree to the leaves that are valid exits. The paths should be printed in order from left to right in the binary search tree that represents the maze (this is also the alphanumeric ordering of the paths).

Binary Tree Maze Example

The above figure depicts visually a tree that results from the following input file

J 2
G 1
Q 0
B 0
I 1
A 0
C 1
E 2
D 0
H 1
F 0
L 3
R 1
U 0
S 1
V 1
T 1
K 2
O 0
P 1
N 2
M 0

The starting point for the maze is at its root at node lebeled "J". The valid exits from the maze are at the nodes at the lowest level. They are labeled "D", "F", "M", and "T". There are trap doors at any leaf node that is not at the lowest level. They are nodes labeled "A", "H", "K", "P", and "V".

The tree also shows the number of life points that the hero can collect at each of the life-points. Note that if a number of life points at the room is less than one, the hero cannot go anywhere since they need exactly one life-point to travel from one node to the next.

There are several possible paths through the maze depicted above, but only three lead to reachable valid exits that are reachable by our hero with sufficient life points. The four paths that reach the bottom-most level are J G B C E D, J G B C E F, J Q L O N M, and J Q R U S T. The last path does not allow our hero to collect enough life points to make it to the bottom-most level.

In this case, the program should produce three lines of output:

J G B C E D
J G B C E F
J Q L O N M

The design of classes is up to you, but you do need to implement certain classes to represent certain entities in the program. You need to make decisions about how to design these classes to produce an efficient and well-put-together program. Make sure that all methods that you include in a particular class belong in that class.

BinaryTreeMaze class

This is the class that is the program. This means it has the main method. This class is responsible for parsing and validating the command line arguments, reading and parsing the input file, producing any error messages, handling any exceptions thrown by other classes, and producing output.

BST<E> class

This is a generic BST<E> class. The specification is similar to the one for TreeSet class provided by Java libraries, but your implementation will be very different.

The specification for this class is provided at its javadoc page. You can use the source code that we wrote in class, but keep in mind that the class that you are implementing requires additional functionality and you may need to rewrite some of the methods that were created in class.

NOTE: normally, all data fields in the class should be private. But since the BST class serves as a base class for the Maze class below, its data fields can be made protected instead to allow the subclass to access these data fields.

Node class

The program should provide and use a nested class (to learn more about nested and inner classes see: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html) that provides nodes for your tree. The details of the implementation of that class are up to you, but this class should be private (or protected, if another class in your code inherits from it and needs to be able to create variables of type Node):

private class Node

HINT: to improve the performance of your BST algorithms, it may be useful to keep additional data fields in the nodes, i.e., more than just data, left and right. Those design decisions are up to you. But you should explain in comments for this class, why you have additional data fields if you chose to do so.

Iterator

The BST<E> class implements Iterable<E> interface. This means that its iterator() method needs to return an instance of a class that implements the Iterator<E> interface. The iterator() method should return an iterator instance that accesses the values in the tree according to the inorder traversal of the binary search tree. The two additional methods preorderIterator() and postOrederIterator() need to return iterators that access the values in the tree according to the preorder and postorder traversals, respectively.

The details of the implementation are up to you and you are free to implement more than one internal private iterator class. The next() and hasNext() methods of the iterator classes should perform in O(1). The constructor of the iterator classes should be O(N).

The remove method in the Iterator<E> interface is optional and you do not need to provide the actual remove functionality. (This means that the method has to exist, but instead of performing its function, it throws an instance of UnsopportedOperationException.)

RESTRICTION: You should not use an iterator that is already implemented in one of the Java classes (like the one in the ArrayList class). In practice, this would be a good idea, but the objective here is for you to implement yout own iterator.

Maze class

This should be the class that inherits from your own BST<MazeNode>. Your class should represent the maze itself (therefore, it should not be generic and its nodes should store data items of type MazeNode). It is up to you to decide how to implement this class, which methods to provide etc. The important distinction is that the Maze class should contain all the methods that are specific to the maze itself, not the general BST methods that are inherited from the BST class.

MazeNode class

This class should represent the points in the maze at which our hero can collect life-points and at which they need to make a decisions as to which way to continue. It should be capable of storing the label of the node and the number of possible life points our hero can collect at this maze node. It may be useful (or may be even necessary) to implement the Comparable interface.

Note that his is NOT the same as the internal Node class for the BST class itself.

Hero class

This class should represent our hero traveling through the maze. An object of this class should be capable of keeping track of all the life points that our hero possesses at any given time. This information should be updated as the hero travels along the different potential paths through the maze.

You may, but you are not required to, implement other classes.

  • You should follow the rules outlined in the document Code conventions document.

  • You have to work with your own implementation of a BST class. It should be based on the one we have been using in class, but it cannot be based on one of the implementations in Java libraries.

  • The BST class that you implement should be just a basic binary search tree. Do not try to implement a balanced tree (doing this will change the shape of the maze and will result in incorrect paths).

  • You may use any exception-related classes.

  • You should start right away!
  • You should modularize your design so that you can test it regularly: for example, implement the part of the code that reads and parses the input file, then implement and test individual classes, then implement the part that provides the interactive part of the program, … .
  • Make sure that at all times you have a working program! You should also implement stubs of methods that return 0 or null. This way your code compiles, even though it may not work completely. You can implement methods that perform one task at a time. This way, if you run out of time, at least parts of your program will be functioning properly.
  • You should make sure that you are testing the program on much smaller data set for which you can determine the correct output manually. You can create a test input file that contains only a few rows.
  • You should make sure that your program's results are consistent with what is described in this specification by running the program on carefully designed test inputs and examining the outputs produced to make sure they are correct. The goal in doing this is to try to find the mistakes you have most likely made in your code.
    DO NOT test your program on the entire large input file. This may take a long time and you will never know if the results are correct or not.
  • Each class that you submit will be tested by itself without the context of other classes that you are implementing for this assignment.
    This means that you need to make sure that your methods can perform their tasks correctly even if they are executed in situations that would not arise in the context of this specific program.
  • You should backup your code after each time you spend some time working on it. Save it to a flash drive, email it to yourself, upload it to your Google drive, push it to a private git repository, do anything that gives you a second (or maybe third copy). Computers tend to break just a few days or even a few hours before the due dates - make sure that you have working code if that happens.

If your program does not compile or if it crashes (almost) every time it is run, you will get a zero on the assignment. Make sure that you are submitting functioning and documented code, even if it is not a complete implementation so that you can get partial credit.

If the program does not adhere to the specification, the grade will be low and will depend on how easy it is to figure out what the program is doing and how to work with it.

The grade will be determined by several factors:

  • 45 points: class correctness: correct behavior of methods of the required classes (this will be determined by the autograder), these points will be assigned as follows:
    • 0 points: code passes fewer than a half of the autograder tests
    • 20 points: code passes [50-70)% of the autograder tests
    • 35 points: the code passes [70-80)% of the autograder tests
    • 40 points: the code passes [80-90)% of the autograder tests
    • 45 points: the code passes 90% or more of the autograder tests
  • 10 points: correct behavior of the program when executed as a whole with several different intput files
  • 15 points: design and the implementation of your code (this will be determined by a code review)
  • 15 points: efficient implementation (this will be determined by a code review)
  • 15 points: proper documentation, program style and format of submission (this will be determined by a code review)

For the purpose of grading, your project must be be in the package called project5. This means that each of your submitted source code files should start with a line:

package project5;

Your should submit all your source code files (the ones with .java extensions only) to Gradescope. DO NOT submit .class files or any project files that your IDE might produce. Do not submit the data file or any data files that you might have created. Once you submit, you should look at all the files that Gradescope has - make sure there is nothing there that should not be there.

You may resubmit to Gradescope as many times as you wish before the submission link closes. But if you resubmit after the grace period ends, your assignment will be subject to the late point deductions.

For this project, you will see some of the results for the autograded unit tests. Some of the results will be hidden. (When some results are hidden, Gradescope does not display the score for the autograded part. )