PROGRAM 3



Clarifications

  • Typo with the .java file URL's in the "Steps" section corrected. You should be able to access the class files properly now.
  • Game.java has been updated to correct the issue with line 141. You can either download the updated file or please change line 141 to this instead: loc.displayCostToEachNeighbor();
  • pickUpSpyCam description updated with this information: increment the number of spy cams remaining.
  • Description for DFS method updated for SpyGraph class.
  • Clarification added regarding sort functionality expectations on addNeighbor function in GraphNode class.
  • Description for BFS method updated for SpyGraph class.
  • Description for dropSpycam method updated for Player class. The message to be displayed in case there was already a spy cam at player's current location was missing and the same has been updated.
  • Typo's fixed in sample runs files. Please refresh your browser if you already have these loaded.
  • Typo's and inconsistencies with Sample Run 1, Sample Run 3 fixed.
  • Inconsistency with Sample Run 4 fixed.
  • To fully enable testing of your Game implementation, make your path finding algorithms BFS, DFS and Dijkstra functions return an empty list instead of null. This way your program will not crash when you execute the path command.
  • SpyGraph class iterator() method return value discrepancy: The method header says "@return iterator through all nodes in alphabetical order.". Since this method is pre-implemented for you and there is no TODO, you don't have to modify either the method header or the implementation of the method itself. This is reference to this Piazza post.
  • SpyGraph class addGraphNode method: if you sort the vertices, then your output would be different from the provided sample output. This might be the primary reason if you have an output mismatch with sample output 3. Please note that this method did not have a requirement to sort the vertices while being added to the graph and hence we do not expect you to sort the vertices.

Overview

Goals

The goals of this assignment are to:

  • Understand and implement the Graph components: an undirected weighted graph.
  • Understand and implement BFS, DFS, and Dijkstra's Shortest path graph traversal methods.
  • Complete implemenation of an interesting game that can be configured in a variety of ways.

Description

  • You belong to UWPD and you are going to catch the spy moving across our city. If he/she is not caught within a particular time frame (a number of steps-moves), he/she will destroy Madison!
  • The spy moves randomly in the city and you move around searching for him. You can also use a limited number of your spycams to catch the spy, since you cannot be everywhere! You win when you catch or surround him before the time expires. If not, the Spy wins.
  • You will be implementing an interactive game that will allow the player to take various actions by typing a command. If the player lands on the same location as the spy or successfully surrounds the spy before the number of steps is reached, the city is saved.

Specifications


The Neighbor Class


  • The Neighbor class represents an edge between two nodes.
  • Neighbor class should implement Comparable<Neighbor> interface.
  • Each GraphNode instance maintains a list of its neighbors. Description of this class is provided below.
  • When, an edge is found in the area input file, each node becomes a neighbor of the other node.
  • Each edge also has a cost or weight.
  • It is the instance of the Neighbor class that stores this cost.
  • Note that Neighbor and GraphNode classes have inter-dependency and have to be compiled simultaneously.

The Neighbor class outline is provided for you (see Neighbor.java). The Neighbor class has the following constructor and methods, which you need to implement:

Constructor Description
Neighbor(int cost, GraphNode neighbor) Constructs a Neighbor with cost of the link and the neighbor at the other end.
Method Description
int getCost() Returns the cost of travelling this edge to get to the Neighbor at the other end of this edge.
GraphNode getNeighborNode() Returns the Neighbor (node) that is at the other end of "this" node's edge.
int compareTo(Neighbor otherNode) Return the results of comparing this node's neighbor name to the other node's neighbor name.
Allows List of Neighbors to be sorted.
Hint: Read the java docs for String class carefully.
String toString() Returns a String representation of this Neighbor.
The String that is returned shows an arrow (with the cost in the middle) and then the Neighbor node's name.
Example: " --1--> b" indicates a cost of 1 to get to node b
Note: Quotes are given here for clarification, do not print the quotes.

You may not add any public methods to the Neighbor class, but you may add private helper methods.

The GraphNode Class


  • Instances of this class maintain a vertex name and a list of adjacent vertexes (neighbors) and supporing operations.
  • It is comparable by vertex name (so it can be sorted) and its neighbors are returned in a list that can be iterated through.
  • Our implementation maintains the neighbors in a sorted list to ensure that the "367 convention" for picking nodes alphabetically is preserved.
  • Note that Neighbor and GraphNode classes have inter-dependency and have to be compiled simultaneously.
  • Hint: In any operation that requires you to operate on the Neighbor class instance, make sure to call the getNeighborNode() function to retrieve the GraphNode at the other end of the edge.

A GraphNode class outline is also provided for you (see GraphNode.java). The GraphNode class has the following constructor and methods, which you need to implement:

Constructor Description
GraphNode(String name) Constructs a GraphNode with the vertex name and empty neighbors list.
Method Description
String getNodeName() Returns the name of the vertex.
List<Neighbor> getNeighbors() Returns the neigbors of this vertex.
void setVisited(boolean flagVal) Sets the visited flag of this vertex.
boolean getVisited() Gets the visited flag of this vertex.
int compareTo(GraphNode otherNode) Return the results of comparing this node's name to the other node's name.
Return negative value or 0 or positive value
void addNeighbor(GraphNode neighbor, int cost) Adds a new neighbor and maintains sorted order of neighbors by neighbor name.
You can sort any List which stores object instances from a class that implements Comparable<T> interface, simply by calling <List_instance_variable>.sort(null);
You need not implement your own sort functionality, but can use the built-in sort function.
void displayCostToEachNeighbor() Prints a list of neighbors of this GraphNode and the cost of the edge to them.
Example:
"1 b"
"4 c"
Note: Quotes are given here for clarification, do not print the quotes.
int getCostTo(String neighborName) Returns cost to reach the neighbor.
Throws NotNeighborException if neighborName is not a neighbor.
GraphNode getNeighbor(String neighborName) Returns the GraphNode associated with name that is a neighbor of the current node.
Throws NotNeighborException if neighborName is not a neighbor.
Iterator<String> getNeighborNames() Returns an iterator that can be used to find neighbor names of this GraphNode.
Hint: You need yo construct a new List here.
void setSpycam(boolean cam) Sets/unsets spycam at this node.
boolean getSpycam() Returns information about spycam presense in this node.
boolean isNeighbor(String neighborName) Returns true if this node name is a neighbor of current node.
Retrue if the node is an adjacent neighbor or false otherwise.
String toString() Returns the name of this node.

You may not add any public methods to the GraphNode class, but you may add private helper methods.

The Player Class


  • Represents a player in the Game.

A Player class outline is also provided for you (see Player.java). The Player class has the following constructor and methods, which you need to implement:

Constructor Description
Player(String name, int budget, int spycams, GraphNode startnode) Constructs an instance of Player to track location and other information for the player.
Method Description
void decreaseBudget(int dec) Subtract the decrease amount from the budget.
boolean dropSpycam() If there are no remaining spy cams to drop, display "Not enough spycams" and return false.
Otherwise:
If there is not a spy cam at the player's current location:
  1. display "Dropped a Spy cam at D" where D is the node name.
  2. drop a spy cam (here) D. Note: Make sure to set the spycam on the GraphNode as well.
  3. decrement the remaining spy cam count if there was not already a spycam
Else (there is already a spy cam at the player's current location):
  1. Display "Already a Spy cam there"
int getBudget() Return the amount remaining in this player's budget.
GraphNode getLocation() Returns the node where the player is currently located.
String getLocationName() Returns the name of the node where the player is currently located.
String getName() Return the name of the player.
void getSpycamBack(boolean pickupSpyCam) If pickupSpyCam is true, increment the number of spy cams remaining.
Note: Make sure to unset the spycam on the GraphNode as well.
int getSpycams() Returns the number of spy cams available to drop.
boolean move(String name) Change the location of the player to the specified node. Moves to nodes with a cost of one are not counted. (ie. it is "free" to "walk" to a node).
If attempt to move to a node that is not a neighbor (node with name nn) is made, displays the message: "nn is not a neighbor of your current location" and returns false.
If there is no sufficient budget, then display "Not enough money cost is <COST> budget is <BUDGET>"
Example: "Not enough money cost is 20 budget is 15"
Note:Quotes are given here for clarification, do not print the quotes.
Note: Write a try-catch block for NotNeighborException, but you should do necessary checks such that this exception will never be thrown from the GraphNode functions that you are invoking.
Hint: Carefully consider which function to call first to ensure that an exception will never be thrown.
boolean pickupSpycam(GraphNode node) Check the node to see if there is a spy cam. If there is a spy cam at that node, remove the spy cam from that node.
Also, remove the spy cam name from the Player's list of spy cam names.
Otherwise, return false.
Note: Make sure to unset the spycam on the GraphNode as well.
Increment the number of spy cams remaining.
void printSpyCamLocations() Display the names of the locations where Spy Cams were dropped (and are still there).
If there are spy cams at nodes named a, f, and g, the display would show:
"Spy cam at a"
"Spy cam at f"
"Spy cam at g"
Note: Quotes are given here for clarification, do not print the quotes.

You may not add any public methods to the Player class, but you may add private helper methods.


The SpyGraph Class


  • The Graph represents various locations in the Game as vertices.
  • In this class, you will need to implement the Breadth-First Traversal and Depth-First Traversal algorithms.
  • You may also implement Dijkstra's Shortest path algorithm, but this is not required. Simply return an empty ArrayList if you are not implementing this.
  • GraphNode and Neighbor are comparable, so collections of them can be sorted by calling the sort method on the collection.

A SpyGraph class outline is also provided for you (see SpyGraph.java). The SpyGraph class has the following constructor and methods. Implement the TODO's and any missing public method implementations in this class.

Constructor Description
SpyGraph() Initializes an empty list of GraphNode objects
Method Description
void addGraphNode(String name) Add a vertex with this label to the list of vertexes.
No duplicate vertex names are allowed.
void addEdge(String v1name, String v2name, int cost) Adds v2 as a neighbor of v1 and adds v1 as a neighbor of v2. Also sets the cost for each neighbor pair.
Iterator<GraphNode> iterator() Return an iterator through all nodes in the SpyGraph
List<Neighbor> BFS(String start, String end) Identify path between the start node and end node by performing BFS traversal.
Hint: Write your BFS helper companion function recursively. You will need to use multiple Queue data structures as parameters [You may use a List instead of a Queue if that makes the processing easier]. Apart from using a data structure to track neighbors during every level of recursion, you will also need to use another data structure to track the parent node association from the previous level of recursion.
Note: This is not the only possible approach to solve this problem. You might be able to achieve an iterative solution using HashMap instead. Apart from these solutions, there still could be an alternate solution to this problem. As long as you follow the BFS order for generating the path, any solution will be accepted.
To enable testing of your program with a partial implementation, make sure to return an empty list instead of a null to prevent your program from crashing.
List<Neighbor> DFS(String start, String end) Identify path between the start node and end node by performing DFS traversal.
While you are storing the DFS traversal result in the List<Neighbor>, if you found that you completed the traversal of a particular path without finding the end node, then you need to remove the predecessor node from the List<Neighbor> before proceeding with trying out the rest of the paths.
Hint: Make your companion / helper function's return variable to boolean and set it to true / false when you found the end node. When you write the recursive call, process the returned boolean value appropriately to either keep the found path as such in List<Neighbor> or to delete the last node from List<Neighbor>.
To enable testing of your program with a partial implementation, make sure to return an empty list instead of a null to prevent your program from crashing.
GraphNode getNodeFromName(String name) Return the GraphNode instance which corresponds to the given vertex name.
List<Neighbor> Dijkstra(String start, String end) OPTIONAL: Implements Dijkstra's shortest path list of nodes on path.
To enable testing of your program with a partial implementation, make sure to return an empty list instead of a null to prevent your program from crashing.
GraphNode getRandomNode() Returns a random node from the Graph. Implementation of this method is provided to you - Do not edit this method.

You may not add any public methods to the SpyGraph class, but you may add private helper methods.

Other Class Files


Following are the list of class files that have already been implemented for you.
You should download these files after completing the above class implementations.

Game Play


We have coded the Game class for you and this will handle much of the game play once you implement the missing classes. At the start of the program, the input files are read and the game parameters and SpyGraph are initialized. Next, the player is asked for their name and a brief help instruction is provided.

java -cp . Game config1.txt area1.txt
Enter the name of your player: 
Bucky Badger
Your random starting location is a
Type 'h' at any time for a list of commands.
Enter command: 

Various areas in Madison are modelled as a graph. Each location represents a vertex or node in the graph and the road connecting between the two locations represent the edge between the nodes. In addition, each road has a toll fee (weight) that should be spent in order to use the road. The player and spy start out at a random spots in the city. As a player, enter your name, and then choose your action to wisely to catch the spy before time expires.  At each instant of time, the player has the following set of actions to choose from:

All commands can be activated with the first character.
'budget' prints the money you have remaining to use on moves.
'clock' returns the number of moves remaining.
'drop' places a spycam at your current location if there is one available.
'get NODE' retrieves a spycam from NODE if NODE has a spycam
'location' prints your location
'move NODE' moves you to NODE if possible.
'neighbors' prints all neighbors of your location and the cost to get there.
'onspy' tells you if you are at the same location as the spy and decreases your budget by a prespecified amount
'path NODE' prints three possible paths from your location to NODE using DFS, BFS, and Dijkstra's
'quit' ends the game
'spycams' prints the nubmer of spycams remaining and the locations of placed spycams.

The spy will periodically reveal their location and this will help you to make a better decision when choosing the next course of action to catch the spy.

If you catch the spy, you'll see the win message:

     GAME OVER, spy is surrounded.
     Bucky Badger wins!
     Spy is at node e
     Player is at node f

If you don't catch the spy, you'll see this message:

     GAME OVER, you did not find the spy, spy wins
     Spy is at node red_gym
     Player is at node gordon_commons

Input Files: Used to configure the game


To configure different game play, the program requires two input data files entered as command-line arguments.  The names of these two files must be included as command-line arguments in your build process.  The configN.txt file is listed first and the areaN.txt file is listed second.  We provide the sample files input files.

area.txt

A text only file that contains the area information (the graph information), that is, the locations(nodes) and paths(edges).  The contents of the area file will follow this form.  The NODES section lists the node names and the EDGES section lists the edges between nodes that exist.  The Game class will parse these files for you, but you must have the SpyGraph complete before this can by completed correctly. Each line following the line "NODES" represent a node in the graph.  Each line following "EDGES" specifies an edge between the two nodes

    NODES
    a
    b
    EDGES
    a b 1

The graph described by this area file has two nodes, "a" and "b" and one edge, between "a" and "b" with a cost or weight of 1.  That means it will cost 1 unit of your budget to move from a to b or b to a.  The graph is undirected and constructed from GraphNode instances that are linked together according to the edge list.

config.txt

The config.txt file gives information about the parameters required for the gameplay.

REVEALSPY 2
MOVES 10
BUDGET 15
SPYCAMS 3
UNIT pound
COST 2
  • UNIT specifies the currency type.
  • BUDGET is the total amount money units you are allowed during the entire gameplay. This budget is used to spend on different moves during game play.
  • REVEALSPY is the number of time units after which the spy reveals his location. These hints help the player follow the spy.
  • MOVES is the total number of moves along paths the player has to find the spy during the entire gameplay.
  • SPYCAMS is the number of spycams the player has available to drop in an effort to surround the spy.
  • COST is the cost of checking if the spy is in your location or not.
Following are the sample input files.

Sample Runs

To help you understand the game play, we have created several sample runs for you to review.

Steps

After you have read this program page and given thought to the problem we suggest the following steps:

  1. You may use the Java development environment of your choice in CS 367. However, all programs must compile and run on the lab computers for grading.
  2. Download the following to your programming assignment 3 directory:
  3. Implement Neighbor.java and GraphNode.java.
  4. Then verify compilation of those two classes using the command: javac -cp . *.java
  5. Then proceed to implementing Player.java class.
  6. And finally implement the SpyGraph.java class.
  7. For many functions, additional note or hint is provided along with the description. These are really important and given to ease your implementation tasks. Please go through these carefully.
  8. If you are not using the lab computers to develop your program, make sure you compile and run your program to ensure that it works on the Linux lab computers. You can compile your Java source using javac in a terminal window as in this example: javac -cp . *.java and the run your program using java as in: java -cp . Game <Config(number).txt> <Area(number).txt>
  9. Submit your work for grading.

SUBMISSION

  • Make sure your code follows the style and commenting standards. You should write appropriate private methods, instead of repeating the same code inside multiple public methods.
  • Follow the submission instructions from Programs page
  • For this pogram, you need to turn in the following files:
    • Neighbor.java
    • GraphNode.java
    • Player.java
    • SpyGraph.java
    • Spy.java
    • Game.java
    • InvalidAreaFileException.java
    • NotNeighborException.java
    • "*.java" additional classes (if any) that you've implemented for your program.