I have an assignment to create an A* algorithm through a road graph of the UK through a given template (all files for this assignment I have uploaded here
I think, however, that the problem is somewhere in my Planner.java file - the A* algorithm is not finding a node to expand, it just abruptly stops without expanding any nodes at all.
Here is the code for the Planner :
public class Planner implements PlannerInterface<Object> {
// declaration and instantination of our open and closed lists
private final OpenList openList = new OpenList();
private final ArrayList<SearchThroughNodes> closedList;
public boolean empty() {
int lengthOfItem = 0;
return (lengthOfItem == 0);
}
// constructor of closed list
public Planner() {
this.closedList = new ArrayList<>();
}
#Override
public List<GraphEdge> plan(RoadGraph graph, GraphNode origin, GraphNode destination) {
// Temporary costs and other data needed
SearchThroughNodes currentNode, temp1, temp2;
temp2 = new SearchThroughNodes(null, 0, 0, null);
GraphNode temp;
double temporaryCost, heuristics;
ArrayList<GraphEdge> finalResult;
finalResult = new ArrayList<>();
// check if origin and destination exist a.k.a are not set to null
boolean originExistence;
boolean destinationExistence;
destinationExistence = false;
originExistence = false;
if (origin != null && destination != null) {
originExistence = true;
destinationExistence = true;
}
// Pre-requisites for our A-Star to work
if (originExistence == true && !destination.equals(origin) && destinationExistence == true ) {
openList.add(new SearchThroughNodes(origin, 0, Utils.distanceInKM(origin, destination) / 120, null));
} else {
return null;
}
// A-star loop
while (!openList.empty()) {
// Get BEST node to expand
currentNode = (SearchThroughNodes) openList.get();
if (closedList.contains(currentNode)) {
continue;
}
// We reached the destination
// go back through nodes and read path
GraphNode checkCurrNode;
checkCurrNode = currentNode.getGraphNode();
if (destination.equals(checkCurrNode)) {
temp1 = currentNode;
temp2 = (SearchThroughNodes) currentNode.getPrecedingItem();
while (!temp2.getGraphNode().equals(origin)) {
finalResult.add(0, graph.getEdge(temp2.getGraphNode().getId(), temp1.getGraphNode().getId()) );
temp1 = temp2;
temp2 = (SearchThroughNodes) temp2.getPrecedingItem();
}
finalResult.add(0, graph.getEdge(temp2.getGraphNode().getId(), temp1.getGraphNode().getId()));
return finalResult;
}
closedList.add(currentNode);
long currentNodeId = currentNode.getGraphNode().getId();
List<GraphEdge> outEdges = graph.getNodeOutcomingEdges(currentNodeId);
//if expandable
if (outEdges != null) {
// traverse all nodes after currentNode
for (GraphEdge edge : graph.getNodeOutcomingEdges(currentNodeId)) {
long getFromEdge;
getFromEdge = edge.getToNodeId();
//Look at node at the end of the current edge
temp = graph.getNodeByNodeId(getFromEdge);
//store nodeID in tmp2 for traversal of openList
long tmp2GetID;
tmp2GetID = temp.getId();
temp2.setNodeID(tmp2GetID);
// set current edge length in kms,edge max allowed speed and current node cost to variables
// to later compute the temporary cost
double edgeLengthInKMs = edge.getLengthInMetres() / 1000;
double edgeMaxAllowSpeed = edge.getAllowedMaxSpeedInKmph();
double currCost = currentNode.getCost();
//new heuristics and cost
temporaryCost = currCost + edgeLengthInKMs / edgeMaxAllowSpeed;
heuristics = Utils.distanceInKM(temp, destination) / 120;
// Proceed here if node wasn't expanded
if (!closedList.contains(temp2)) {
// if node isn't contained currently in closedList
// if not,check and update accordingly
if (!openList.contains(temp2)) {
openList.add(new SearchThroughNodes(temp,
temporaryCost, heuristics, currentNode));
} else {
temp1 = openList.getNode(temp2);
double tempOneCost = temp1.getCost();
if (tempOneCost > temporaryCost) {
temp1.update(temporaryCost, currentNode);
}
openList.insert(temp1);
}
}
}
}
}
return null;
}
#Override
public AbstractOpenList<Object> getOpenList() {
return openList;
}
}
Related
Below is my bfs algorithm, the algorithm works and finds the node given the start and target. But I want to save edges for the used path in a linkedList to draw the path.
My BFS:
public DGPath breadthFirstSearch(String startId, String targetId) {
V start = this.getVertexById(startId);
V target = this.getVertexById(targetId);
if (start == null || target == null) return null;
DGPath path = new DGPath();
path.start = start;
path.visited.add(start);
// easy target
if (start == target) return path;
// TODO calculate the path from start to target by breadth-first-search
// register all visited vertices while going, for statistical purposes
// if you hit the target: complete the path and bail out !!!
Queue<V> fifoQueue = new LinkedList<>();
Map<V,V> visitedFrom = new HashMap<>();
fifoQueue.offer(start);
visitedFrom.put(start, null);
while (!fifoQueue.isEmpty()) {
V current = fifoQueue.poll();
for (E e : current.getEdges()) {
V neighbour = e.getTo();
path.visited.add(neighbour);
if (neighbour == target) {
while (current != null) {
path.getEdges().addFirst(e);
current = visitedFrom.get(current);
}
return path;
} else if (!visitedFrom.containsKey(neighbour)) {
visitedFrom.put(neighbour,current);
fifoQueue.offer(neighbour);
}
}
}
// no path found, graph was not connected ???
return null;
}
The DGPath is the class that creates the path as shown below:
public class DGPath {
private V start = null;
private LinkedList<E> edges = new LinkedList<>();
private double totalWeight = 0.0;
private Set<V> visited = new HashSet<>();
/**
* representation invariants:
* 1. The edges are connected by vertices, i.e. FOR ALL i: 0 < i < edges.length: edges[i].from == edges[i-1].to
* 2. The path begins at vertex == start
* 3. if edges is empty, the path also ends at vertex == start
* otherwise edges[0].from == start and the path continues along edges[i].to for all 0 <= i < edges.length
**/
#Override
public String toString() {
StringBuilder sb = new StringBuilder(
String.format("Weight=%f Length=%d Visited=%d (",
this.totalWeight, 1 + this.edges.size(), this.visited.size()));
sb.append(start.getId());
for (E e : edges) {
sb.append(", " + e.getTo().getId());
}
sb.append(")");
return sb.toString();
}
public V getStart() {
return start;
}
public LinkedList<E> getEdges() {
return edges;
}
public double getTotalWeight() {
return totalWeight;
}
public Set<V> getVisited() {
return visited;
}
}
I want to save the right edges in de linkedlist edges from the BGPath class (called path in my BFS algo method). So I already saved the used vertices in a map to go back to the root. But when I add the edge to the path it just saves the last edge used multiple times.. The problem is the vertex can have multiple edges, so I need to add the edge from the previous that was pointing to the last "current" until I'm back to the root. But I cant wrap my head around the right way to do this.
The line where I now add the edge to the list of edges is: path.getEdges().add(e)
I think, your problem is the same line, where your are adding the edges, that line is adding the same edge in inner while loop again and again, so you are only traversing back but not adding those nodes to your edges list.
I think, it should be like this
while (!fifoQueue.isEmpty()) {
V current = fifoQueue.poll();
for (E e : current.getEdges()) {
V neighbour = e.getTo();
path.visited.add(neighbour);
if (neighbour == target) {
path.getEdges().addFirst(e):
while (current != null) {
path.getEdges().addFirst(current) ;
current = visitedFrom.get(current);
}
return path;
} else if (!visitedFrom.containsKey(neighbour)) {
visitedFrom.put(neighbour,current);
fifoQueue.offer(neighbour);
}
}
}
// no path found, graph was not connected ???
return null;
}
So trying to make dijsktra algorithm work and apparently im removing the wrong node according to labb assistant (who is no longer available for help)
These are his comments:
"the loop that you are using in the Dijkstra method (lines 158 - 178). For it to be correct, you need to loop through the outer arcs of your minNode (in your case, "n"), and then pick up the node with the minimum cost as your minNode. In your case, you always remove the first node from the tempNode list instead of the minNode. Please resubmit the modified Network.java file."
and these are lines 157-181:
while (! tempNodes.isEmpty()) { // repeat until all nodes become permanent
Node n = tempNodes.get(0);
double min= Double.POSITIVE_INFINITY;
if(n.value < min){
min = n.value;
}
for(Arc a: n.arcList){
if(n.value + a.weight < a.head.value){
a.head.value = n.value + a.weight; // Update the weight of each node that is adjacent to the minimum-
n.prev = a;
}
tempNodes.remove(n);}//Remove the minimum-weight node n from tempNodes
}
Node k;// Represent a tree by assigning 1 to its arcs and 0 to all other arcs.
for (String nodeName: nodeMap.keySet()) {
k = nodeMap.get(nodeName);
if(k.prev != null) {
k.prev.alvalue = 1.0;
}
}
am i correct in assuming the only issue is that im removing n instead of min mayby?
Il just add the rest of the lines:
public class Network {
// Attributes
public String name;
public HashMap<String, Node> nodeMap;
// Constructor
public Network(String name, String inputFileName) {
// Initialize the attributes
this.name = name;
this.nodeMap = new HashMap<String, Node>();
// You MAY need these local variables to store values or objects
// temporarily while constructing a new Network object
String line, arcID, tailName, headName;
Node tail, head;
double weight;
Arc forwardArc, backwardArc;
try {
// Get access to the contents of an ASCII file
File file = new File(inputFileName);
FileReader fReader = new FileReader(file);
BufferedReader bReader = new BufferedReader(fReader);
// Read the first line, and do nothing to skip it
line = bReader.readLine();
// Read the second line, which represents the first
// (undirected) arc stored in the file
line = bReader.readLine();
// Store each element of the network in forward star.
while (line != null) {
// Split each line into an array of 4 Strings
// using ; as separator.
String[] tokens = line.split(";");
arcID = tokens[0];
tailName = tokens[1];
headName = tokens[2];
weight = Double.parseDouble(tokens[3]);
// Check if nodeMap contains a Node whose name is tailName or headName.
if(nodeMap.containsKey(tailName)){
tail = nodeMap.get(tailName);
}
else{
tail = new Node(tailName, Double.POSITIVE_INFINITY, new LinkedList());
nodeMap.put(tailName, tail);
}
if(nodeMap.containsKey(headName)){
head = nodeMap.get(headName);
}
else{
head = new Node(headName, Double.POSITIVE_INFINITY, new LinkedList());
nodeMap.put(headName, tail);
}
// If not, create it, assign it to tail or head, and add it to nodeMap.
// Otherwise, retrieve it from nodeMap.
// Then, create two Arcs:
// one from tail to head, to be added to outArc of tail
// one from head to tail, to be added to outArc of head.
forwardArc = new Arc(arcID+"a",tail,head,weight);
backwardArc = new Arc(arcID+"b",head,tail,weight);
tail.arcList.add(forwardArc);
head.arcList.add(backwardArc);
// Read the next line
line = bReader.readLine(); }
} catch (Exception e) {
e.printStackTrace();
}
}
// Save
public void save(String outputFileName) {
// This object represents an output file, out.txt, located at ./data/.
File file = new File(outputFileName);
// This object represents ASCII data (to be) stored in the file
FileWriter fWriter;
try {
//writing to the output-file
fWriter = new FileWriter(file);
fWriter.write("TLID "+"\t");
fWriter.write("NAME "+"\t");
fWriter.write("ALVALUE"+"\n");
for (Map.Entry<String, Node> entry : nodeMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Node values = (Node) value;
for (Arc A: values.arcList) {
String TLID = A.name.substring(0,A.name.length()-1);
fWriter.write(TLID+"\t");
fWriter.write(A.name+"\t");
fWriter.write(A.alvalue+"\n");
}
}
fWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void printNodes(){
System.out.println("\tNODE NAME\tWEIGHT");
Node node;
for (String nodeName: nodeMap.keySet()) { // loop thru nodeMap
node = nodeMap.get(nodeName);
System.out.print("\t" + node.name); // \t represents tab space
System.out.print("\t\t" + node.value);
System.out.println();
}
}
public void printArcs(){
System.out.print("TLID "+"\t");
System.out.print("NAME "+"\t");
System.out.print("ALVALUE "+"\n");
for (Map.Entry<String, Node> entry : nodeMap.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Node values = (Node) value;
for (Arc A: values.arcList) {
String TLID = A.name.substring(0,A.name.length()-1);
System.out.print(TLID+"\t");
System.out.print(A.name+"\t");
System.out.print(A.alvalue+"\n");
}
}
}
public void dijkstra(Node origin) {
// Set the value (representing shortest path distance) of origin to 0
origin.value = 0;
// Create a set of nodes, called tempNodes, whose shortest path distances are not permanently determined. Initially, this set contains all nodes.
List<Node> tempNodes = new ArrayList<Node>();
for (String nodeName: nodeMap.keySet()) {
tempNodes.add(nodeMap.get(nodeName));
}
// Perform Dijkstra
while (! tempNodes.isEmpty()) { // repeat until all nodes become permanent
Node n = tempNodes.get(0);
double min= Double.POSITIVE_INFINITY;
if(n.value < min){
min = n.value;
}
for(Arc a: n.arcList){
if(n.value + a.weight < a.head.value){
a.head.value = n.value + a.weight; // Update the weight of each node that is adjacent to the minimum-
n.prev = a;
}
tempNodes.remove(n);}//Remove the minimum-weight node n from tempNodes
}
Node k;// Represent a tree by assigning 1 to its arcs and 0 to all other arcs.
for (String nodeName: nodeMap.keySet()) {
k = nodeMap.get(nodeName);
if(k.prev != null) {
k.prev.alvalue = 1.0;
}
}
}
private void clearArcWeight() {
Node n;
for (String nodeName: nodeMap.keySet()) {
n = nodeMap.get(nodeName);
for(Arc a: n.arcList){
a.weight = 0.0;
}
}
}
public void dijkstra(Node origin, Node destination) {
dijkstra(origin); // or this.dijkstra(origin);
clearArcWeight();
trace(destination);
}
// Represent a tree by assigning 1 to its arcs and 0 to all other arcs.
private void trace(Node n){
Arc a = n.prev;
while (a != null) {
a.weight = 1.0;
a = a.tail.prev;
}
}
public HashMap<String, Node> getNodeMap(){
return nodeMap;
}
public void dijkstraByName(String string) {
dijkstra(nodeMap.get(string));
}
}
I might be able to give a little hint here... If you look at these lines:
Node n = tempNodes.get(0);
// [...n is not changed here...]
tempNodes.remove(n);
you will realize that the n passed into remove() will never be anything else than the result of 'tempNodes.get(0)'. That's why the first Node is always removed from the list.
I have a list of 325 nodes on a map of earth.
These nodes map out the planet with shipping routes.
I've tasked to make an method that can find the shortest route in distance from one node to another only using a path of nodes. It needs to be real time and quick as there will be possibly hundreds of boats.
I've looked into A* and Dijkstras solutions but they don't deal with corner well?
I was thinking about using the angle and the distances but I'm not sure how I would implement it.
Each node on the map is as the following object in an objectMap. Int being the node number.
The nodes and calculations were done within the loading of the app. So we have all of that.
The below class is contained in the objectMap and works nicely.
public static class Node
{
private int nodeNumber;
private Vector2 pos;
private int connectedNodes[];
private ObjectMap<Integer, Node> masterList;
private ObjectMap<Integer, Float> connectedNodeDistances;
private ObjectMap<Integer, Float> connectedNodeDegrees;
public Node(int nodeNumber, float x, float y, int connectedNodes[], ObjectMap<Integer, Node> masterList)
{
this.nodeNumber = nodeNumber;
float areaX = x / MapCreator.WORLD_SCALE;
float areaY = y / MapCreator.WORLD_SCALE;
this.pos = new Vector2(areaX / MapCreator.CURRENT_DOWN_SIZE_X, (PolyUtils.getInstance().getScreenPercentageY(1f) - (areaY / MapCreator.CURRENT_DOWN_SIZE_X)) - MapCreator.TOP_EXTRA);
this.connectedNodes = connectedNodes;
this.masterList = masterList;
this.connectedNodeDistances = new ObjectMap<Integer, Float>();
this.connectedNodeDegrees = new ObjectMap<Integer, Float>();
}
public void calculateDistances()
{
for(int eachConnectedNode : connectedNodes)
{
float angleDegree = new Vector2(this.pos).sub(masterList.get(eachConnectedNode).getPos()).angle();
connectedNodeDistances.put(eachConnectedNode, this.pos.dst(masterList.get(eachConnectedNode).getPos()));
connectedNodeDegrees.put(eachConnectedNode, angleDegree);
}
}
public int getNodeNumber() {
return nodeNumber;
}
public float getDistanceFromNode(int number)
{
return connectedNodeDistances.get(number);
}
public Vector2 getPos() {
return pos;
}
public int[] getConnectedNodes() {
return connectedNodes;
}
}
Where I'm stuck is:
public Array<Node> getFastestRoute(Vector2 startPos, Vector2 endPos, int numberOfAttempts)
{
Array<Array<Node>> potentialRoutes = new Array<Array<Node>>();
int sizeOfShortestRouteNodes = 999999;
for(int index = 0; index < numberOfAttempts; index++)
{
Array<Node> newRoute = getListOfNodes(startPos, endPos, MathUtils.random(-0.75f, 0.75f));
if(newRoute != null)
{
if(newRoute.size < sizeOfShortestRouteNodes)
{
potentialRoutes.clear();
potentialRoutes.add(newRoute);
sizeOfShortestRouteNodes = potentialRoutes.size;
}
else if(newRoute.size == sizeOfShortestRouteNodes)
{
potentialRoutes.add(newRoute);
}
}
}
return getShortestRouteDistance(potentialRoutes);
}
private Array<Node> getListOfNodes(Vector2 startPos, Vector2 endPos, float randomizationSeed)
{
//TODO Draw as lines as test.
Array<Node> nodeList = new Array<Node>();
Array<Node> deadNodes = new Array<Node>();
int iterations = 0;
final int maxIterations = 100; //Needs to be low so that boat trip isn't too long also helps performance.
nodeList.add(getNearestNode(startPos));
Node lastNode = getNearestNode(endPos);
Node currentNode = nodeList.first();
while(true)
{
float currentNodeToEndAngle = new Vector2(new Vector2(currentNode.getPos())).sub(endPos).angle();
//Find closest direction
Node closestNodeInDirection = null;
float closestDirection = 361;
for(int eachConnectedNode : currentNode.getConnectedNodes())
{
Node potentialNode = boatNodes.get((eachConnectedNode));
if(!deadNodes.contains(potentialNode, true))
{
float angleToEndNodeFromCurrent = (new Vector2(currentNode.getPos()).sub(potentialNode.getPos()).angle());
//Randomize the direction from the seed.
angleToEndNodeFromCurrent = angleToEndNodeFromCurrent * randomizationSeed;
float differenceInDegrees = Math.abs(angleToEndNodeFromCurrent - currentNodeToEndAngle);
if(differenceInDegrees < closestDirection)
{
closestDirection = differenceInDegrees;
closestNodeInDirection = potentialNode;
}
}
}
//No new nodes.
if(closestNodeInDirection == null)
{
//Go back and try another route.
if(nodeList.size > 1)
{
nodeList.pop();
currentNode = nodeList.peek();
}
}
//Adding nodes.
if(closestNodeInDirection != null && lastNode != closestNodeInDirection)
{
nodeList.add(closestNodeInDirection);
deadNodes.add(closestNodeInDirection);
currentNode = closestNodeInDirection;
}
else if(closestNodeInDirection != null)
{
//Last node reached.
nodeList.add(lastNode);
return nodeList;
}
//Iterations too many.
iterations++;
if(iterations >= maxIterations){
return null;
}
}
}
public Array<Node> getShortestRouteDistance(Array<Array<Node>> allNodeRoutes)
{
Array<Node> shortestRoute = null;
float shortestRouteLength = 99999f;
for(int arraysIndex = 0; arraysIndex < allNodeRoutes.size; arraysIndex++)
{
Array<Node> nodeArray = allNodeRoutes.get(arraysIndex);
float lengthOfThisRoute = 0f;
for(int nodesIndex = 0; nodesIndex < nodeArray.size; nodesIndex++)
{
Node nextNode = null;
Node thisNode = nodeArray.get(nodesIndex);
if(nodesIndex + 1 < nodeArray.size)
{
nextNode = nodeArray.get(nodesIndex + 1);
}
if(nextNode != null)
{
lengthOfThisRoute += thisNode.getDistanceFromNode(nextNode.getNodeNumber());
}
}
if(lengthOfThisRoute < shortestRouteLength)
{
shortestRouteLength = lengthOfThisRoute;
shortestRoute = nodeArray;
}
}
return shortestRoute;
}
What you are describing is a well known problem with A* and Dijkstras --- and the solution is to not use either of them.
What you need is an "any-angle" algorithm --- so you should use an algorithm called Theta* instead. This approach properly cuts near the corners while avoiding obstacles.
In fact, it is quite similar to the approach you already came up with! I recommend reading the excellent article here, it is a very good explanation of what to do:
Theta*: Any-Angle Path Planning for Smoother Trajectories in Continuous Environments
I have here a single linked list for a program that makes a collage. This runs perfectly but I was wondering how to make it a double linked list. I really have no idea what a double linked is though or how to create one. Any help would be appreciated...
There are 3 classes.
class LinearCollage
{
private Picture myArray[];
private class Node
{
Picture data;
Node pNext;
};
private Node pFirst;
private Node pLast;
private int nPictures;
private Picture clipboard;
public LinearCollage()
{
pFirst = null;
pLast = null;
nPictures = 0;
}
public void addPictureAtEnd(Picture aPictureReference)
{
Node temp = new Node();
temp.data = aPictureReference;
temp.pNext = null;
if( pLast == null )
{
pLast = temp;
pFirst = temp;
}
else
{
pLast.pNext = temp;
pLast = temp;
}
nPictures++;
}
public Picture makeCollage()
{
int collageHeight = 400;
int collageWidth = 400;
for( Node finger = pFirst; finger != null; finger = finger.pNext )
{
System.out.println("Process Picture " + finger.data);
}
Picture retval = new Picture(collageHeight,collageWidth);
int i = 0;
for( Node finger = pFirst; finger != null; finger = finger.pNext )
{
System.out.println("Process Picture " + finger.data);
finger.data.compose(retval, 50*i, 50*i);
i++;
}
return retval;
}
public void cutMiddle()
{
int cutIndex = nPictures-1;
clipboard = myArray[cutIndex];
for( int i = cutIndex; i < nPictures - 1; i++ )
{
myArray[i] = myArray[i + 1];
}
nPictures--;
}
public void cutEnd()
{
int cutIndex = nPictures;
clipboard = myArray[cutIndex];
for( int i = cutIndex; i<nPictures - 1; i++)
{
myArray[i] = myArray[i + 1];
}
nPictures--;
}
public void pasteEnd()
{
myArray[nPictures] = clipboard;
nPictures++;
}
public boolean isFull()
{
return false;
}
public boolean isEmpty()
{
return nPictures == 0;
}
}
import java.util.Scanner;
class LineCollageMaker
{
public static void main(String a[])
{
LinearCollage myCollage;
Scanner uiInput = new Scanner(System.in);
myCollage = new LinearCollage();
FileChooser.pickMediaPath();
boolean inputting = true;
while( inputting )
{
System.out.println("Another picture? Type Y if so.");
String answer = uiInput.next();
if(answer.equals("Y"))
{
Picture pin = new Picture(FileChooser.pickAFile());
myCollage.addPictureAtEnd(pin);
}
else
{
inputting = false;
}
}
Picture firstToShow = myCollage.makeCollage();
firstToShow.show();
//YOU Code the user inteface loop and dispatch to methods
//of myCollage here..
boolean done = false;
while( !done )
{
System.out.println("MENU (CASE SENSITIVE!)");
System.out.println("CM - cut middle and move it to the clipboard");
System.out.println("PE - paste clipboard to end");
System.out.println("CE - cut end and move it to clipboard");
System.out.println("XX - stop running this program");
String command = uiInput.next();
if(command.equals("XX"))
done = true;
else if(command.equals("CM"))
{
if(myCollage.isEmpty())
{
System.out.println("Can't cut from an empty collage.");
}
else
{
myCollage.cutMiddle();
myCollage.makeCollage().show();
}
}
else if(command.equals("PE"))
{
if(myCollage.isFull())
{
System.out.println("Can't paste to an empty collage.");
}
else
{
myCollage.pasteEnd();
myCollage.makeCollage().show();
}
}
else if(command.equals("CE"))
{
if(myCollage.isEmpty())
{
System.out.println("Can't copy from an empty collage.");
}
else
{
myCollage.cutEnd();
myCollage.makeCollage().show();
}
}
else
System.out.println("Unrecognized command. Try again.");
}
}
}
public class Node
{
//le class variables
private Picture myPic;
private Node next;
//le constructors
public Node(Picture heldPic)
{
myPic=heldPic;
next=null;
}
public void setNext(Node nextOne)
{
this.next=nextOne;
}
public Node getNext()
{
return this.next;
}
public Picture getPicture()
{
return this.myPic;
}
//le methods
public void drawFromMeOn(Picture bg)
{
Node current;
int currentX=0, currentY=bg.getHeight()-1;
current = this;
while (current != null)
{
current.drawMeOn(bg,currentX, currentY);
currentX = currentX + current.getPicture().getWidth();
current=current.getNext();
}
}
private void drawMeOn(Picture bg, int left, int bottom)
{
this.getPicture().blueScreen(bg, left, bottom-this.getPicture().getHeight());
}
}
A doubly linked list is simply a linked list where every element has both next and prev mebers, pointing at the elements before and after it, not just the one after it in a single linked list.
so to convert your list to a doubly linked list, just change your node to be:
private class Node
{
Picture data;
Node pNext;
Node pPrev;
};
and when iterating the list, on each new node add a reference to the previous node.
A doubly-linked list takes a singly-linked list one step further by also having a reference to the previous node, rather than just the next one.
I confess I am slightly confused by your code as it looks like you have a private class for Node and then another public class for it. To make this a doubly-linked list add another Node instance variable into your Node class which references the previous node, and then update this variable when you add in new nodes.
You can convert Single linked list to Double linked list via a concept called XOR based linked list. The beauty of XOR truth table makes suitable for this use case.
Check this out : https://www.geeksforgeeks.org/xor-linked-list-a-memory-efficient-doubly-linked-list-set-1/
I'm writing a program to simulate memory fragmentation. The input file tells what segments need to be input at what time.
A sample file is:
N
C 200
P 1 2 3
P 2 3 4
P 2 3 1
R
E
where C is the memory size, P is the segment in the order (size, start time, and life time), and R (should) print out a report showing what segments, and any holes are in memory and where.
One of the rules of this assignment is to create a linked list of the events, where insertions and deletions of the segments are created as events, and I need to traverse the event list.
UPDATE: I have something different, but I know for sure it's not inserting my Events into the Event List. I don't understand why. Does anyone see where my logic is off?
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
public class TestEventList{
public static void main(String[] args){
//read file
File file = new File("b.txt");
try {
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
//send it to interpret file method:
interpretFile(line);
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} //end try-catch
}
public static void interpretFile(String command) {
EventList evtList = new EventList();
Scanner sc = new Scanner(command);
char initialCommand = command.charAt(0);
if (initialCommand == 'N') {
System.out.println("Name");
} else {
}//end else
//file error
char commandCode = command.charAt(0);
String skip = sc.next(); //skips next character to get to integers
switch (commandCode) {
case 'C':/*create Memory! which means, create Event!
Form: Event(int startTime, Segment memSegment)*/
int size = sc.nextInt();
Segment defaultMemoryNode = new Segment(size, 100, false );
/*create event node*/
Event insertDefaultNode = new Event(0, defaultMemoryNode);
/*insert this event*/
evtList.insertEvent(insertDefaultNode);
break;
case 'P':
int segmentSize = sc.nextInt();
int segmentStart = sc.nextInt();
int segmentLife = sc.nextInt();
int segmentExpiration = segmentLife + segmentStart;
Segment memorySegment = new Segment(segmentSize, segmentExpiration, true );
Event addSegment = new Event(segmentStart, memorySegment);
evtList.insertEvent(addSegment);
memorySegment.occupied = false;
Event removeSegment = new Event(segmentExpiration, memorySegment);
evtList.insertEvent(removeSegment);
break;
case 'R':
evtList.traverseEventList();
break;
case 'E':
System.exit(0);
}//end switch
}//end interpretfile method
} //end class T.E.L.
/*This class has-a Linked List, has-a memoryNode, has-a Segment*/
class MemoryList{
private Node memoryNode = new Node();
private Segment memorySegment = new Segment();
private LinkedList memoryList = new LinkedList();
Node head;
Node current;
public MemoryList(){
super();
}
/*define blocks and holes*/
public void insertBlock(Segment memorySegment) {
current = head;
if (current == null) {
memoryList.Add(memorySegment);
System.out.println(memorySegment.size);
}
else {
System.out.println("Checking for room");
System.out.println(current.getSize());
int invalidFit=0;
if(current.getStatus() == false && current.getSize()>=memorySegment.size){
System.out.println("Verified space");
int freeSpace = current.getSize() - memorySegment.size;
memoryList.Add(memorySegment);
createHole(freeSpace);
current = current.next;
} //end if
else {
current = current.next;
} //end else
}//end else
} //end insert block
public void removeBlock(Segment expiredSegment){
current = head;
//search for segment
while(current.next != null){
if(current.getTimetoLeave() == expiredSegment.timeToLeave
&& current.getSize() == expiredSegment.size){
memoryList.Remove(expiredSegment);
int freespace = expiredSegment.size;
createHole(freespace);
}
else{
current = current.next;
}
}//end while
}
private void createHole(int space) {
Node hole = new Node(space, 100, false);
memoryList.Add(hole);
//test if there are two holes together. if so, mergeHoles.
}
*Merge 2 Consecutive Holes*/
private void mergeHoles(Node a, Node b) {
//getPrev(a); //find previous of node a
//use the size through the end of a's prev to
//get start of prev.next (a)+
//make a point to b.next?
} //end mergeHoles
public void traverseMemoryList(){
current = head;
if(current == null){
System.out.println("Memoryless");
}
else{
while(current.next != null){
if(memoryNode.getStatus() == false){
System.out.println("Hole");
current = current.next;
}
}
System.out.println("Segment of size " + current.getSize());
current = current.next;
}
}
} //end MemoryList
class MemoryNode extends Node{
public MemoryNode(){
super();
}
}
class Segment{
int size;
int timeToLeave;
boolean occupied;
/*constructor*/
public Segment(){
}
public Segment(int newSize, int newTime, boolean isOccupied){
this.size = newSize;
this.timeToLeave = newTime;
this.occupied = isOccupied;
}
}
class Node {
private int size;
private int timeToDepart;
boolean occupied; // True if segment, false if hole
Node next;
public Object data; //data in a node
public Node() {
}
public Node(int segmentSize, int timeToLeave, boolean type) {
this.size = segmentSize;
this.timeToDepart = timeToLeave;
this.occupied = type;
}
public int getSize() {
return size;
}
public void setSize(int segmentSize) {
size = segmentSize;
}
public int getTimetoLeave() {
return timeToDepart;
}
public void setTimetoLeave(int timeToLeave) {
timeToDepart = timeToLeave;
}
public void setStatus(boolean type) {
occupied = type;
}
public boolean getStatus() {
return occupied;
}
} //end Node
/* class LL has-a Node*/
class LinkedList{
private Node listNode= new Node();
Node current;
Node head;
Node prev;
int size;
/*Constructors:*/
public LinkedList() {
super();
}
public LinkedList(int j, int k, boolean l) {
super(); //essentially the same as a node
}
/*LL proprietary methods*/
/*test if the list is empty, to avoid NullPointerException*/
public boolean isEmpty() {
return head == null;
}
//insert method:
public void Add(Object data1) {
listNode.data = data1;
/*special case: list is empty*/
if (isEmpty()) {
listNode.next = head;
head = listNode;
head.data = listNode.data;
}
else{
current = head;
while(current.next != null)
{
current.data = data1;
current.next = null;
head = current;
}
current.data = data1;
current.next = head; //newNode now points to head
head = current; //now newNode is the head
}
}
public void Remove(Object delData) {
/*pointers*/
//special case: if head is the removed node;
if (current.data == delData) {
head = current.next;
} else {
prev = head; //it's not the head, keep moving.
current = current.next;
while (current.next != null) { //reached end of list
if (current.data == delData) { //if
prev.next = current.next; //just skip the current node
} else {
prev = current; //now prev is that node
current = current.next; //current is the next node
}
} //end while
//what if current.next = null (it's at the end)?
if (current.next == null && current.data == delData) {
prev.next = null;
}
}//end else
}
public void traverse(){
if(head== null){
System.out.println("no elements to show");
}
else{
current = head;
while(current.next != null){
current = current.next;
}
}}
}// end LL class
/*class EventList has-an Event, is-a LinkedList*/
class EventList{
private Event event = new Event();
private LinkedList evtList = new LinkedList();
private MemoryList memList = new MemoryList();
Node current;
Node head;
int time; //set to the most recent time
/*constructor*/
public EventList(){
super();
}
public void actionOfEvent(Event event1){
Segment p = event.getMemorySegment();
if(p.occupied == true){
insertSegment(event1);
}
else
removeSegment(event1);
}
//a linked list to control creation of events
public void insertEvent(Event event) {
current = head;
if(current == null){
evtList.Add(event);
System.out.println("Added 1st event " + event.startTime);
}
else{
while(current.next != null){
if(event.startTime <= event.getTime()){
//if the event start was before the current time...
evtList.Add(event);
current = current.next;
}
else{
current = current.next;
}
}//end while
evtList.Add(event);
System.out.println("Added 2nd event");
}
}//end insertEvent
public void traverseEventList(){
current = head;
if(current == null){
System.out.println("At time " + event.getTime());
System.out.println("uneventful");
}
else{
while (current.next != null){
Segment segment1 = event.getMemorySegment();
if(segment1.occupied = true){
memList.insertBlock(segment1);
System.out.println(segment1.size + " inserted");
}
else{
memList.removeBlock(segment1);
System.out.println(segment1.size + " removed from memory.");
}
}
}
}
public void insertSegment(Event addEvent){
addEvent.getMemorySegment();
memList.insertBlock(addEvent.getMemorySegment());
}
public void removeSegment(Event expEvent){
}
} //end eventList
/*class Event is-a Node*/
class Event{
int startTime;
Segment memoryNode;
int time;
public Event(){
super();
}
//pretty much the same as Node.
public Event(int newStartTime, Segment newMemNode){
super();
this.startTime = newStartTime;
this.memoryNode = newMemNode;
}
public void setTime(int newStartTime){
time = newStartTime;
}
public int getTime(){
return time;
}
public void setMemorySegment(Segment newMemNode){
memoryNode = newMemNode;
}
public Segment getMemorySegment(){
return memoryNode;
}
}//end class Event
class Report{
int currentTime= 0;
//this creates and prints the segments/holes in the list at curTime
}
I ran your code and it seems that you never call:
setMemoryNode();
This is causing NullPointerExceptions.
Also:
Some of the multiple event instances are being caused by these lines:
EventSequenceList expiredNode = new EventSequenceList(newMemNode,
1, expir, 1, true);
insertEvent(expiredNode);
I will edit this as I see more.
Just a few (other) remarks
Design
You use a lot of inheritance. Is that really necessary? Later on, for production code, you should consider using composition instead of inheritance and code against interfaces. That will remove a lot of ugly dependencies and improve maintainability. Now you have
EventSequenceList is-a MemoryList is-a LinkedList is-a Node
Just from the names, I have some doubt, that a LinkedList really is-a Node. I expect a Node in trees or graphs and even there it's usually a has-a relationship.
Naming
Sometimes you break with Java naming conventions: method names should not start with a capital letter (like Add). Sometimes you use one-letter-variable names (like in most of your constructors).
Sometimes a methodname does not tell us, what the method is really doing (like iterpretFile which actually does not interpret a file but only a single command that may have been read from a file)
The more I look at the assignment, the more I get the feeling, that you'll get stuck with your design sooner or later (more sooner than later). From what I read, what is required:
One event model class. A Class, that represents an insertion or deletion event.
One memory model class. A Class, that represents the entire memory
One segment model class. A Class that represents a segment. A memory class has a list or an array of segments
One linked list that holds all events. This custom linked list may be capable of inserting an event at the right place
One reporting class. A class that can create and print a report.
One input file parser. It will use the input to
create a memory class (with an appropriate number of segments)
create insertion and deletion events from the P lines
insert the events in the linked list
Absolutely no inheritance is needed.
Edit - in response to your last comments
A memory has-an array of cells. The cells are indexed, starting with 0. They are not linked, so I actually don't see any reason to use a LinkedList here. A memory model could look like:
public class Memory {
private int[] cells;
public Memory(int size) { cells = new int[size]; }
public void store(int index, int value) {
if (index < 0 || index >= size) throw new IllegalArgumentException("..");
cells[index] = value;
}
public int read(int index) {
if (index < 0 || index >= size) throw new IllegalArgumentException("..");
return cells[index];
}
}
A segment could be seen as a subclass of Memory. In real life, a segment is requested from a memory manager and the manager allocates a region, if possible. Segments are totally independant, no link between them, no use for a LinkedList here. A quick draft:
public class MemoryManager {
private Memory managedMemory;
public MemoryManager(Memory memory) { this.memory = memory; }
public Segment getSegment(int size) {
int startAddress = allocateSegment(int size);
if (startAddress != -1) {
return new Segment(this, startAddress, size);
}
return null;
}
}
public class Segment extends Memory {
private MemoryManager memoryManager;
private int startAddress; // usually - a handle, so that the memoryManager can
// relocate the segment - we keep it simple
public Segment(MemoryManager memoryManager, int startAdress, int size) {
super(size);
this.memoryManager = memoryManager;
this.startAddress = startAddress;
}
Now back to the events.
One of the rules of this assignment is to create a linked list of the events [eventList = new EventList<Event>()] , where insertions and deletions of the segments are created as events [new Event(EventType.INSERT, int time, Segment segment)); new Event(EventType.DELETE, int time, Segment segment);] , and I need to traverse the event list [for(Event event:eventList)].
That's the task. implement an Event class, implement an EventList class, implement a small enum EventType. The challenge is to implement an insert method in EventClass that inserts two events for one P line at the right places (timestamps).