215 lines
5.7 KiB
Java
215 lines
5.7 KiB
Java
package base;
|
|
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Abstrakte generische Klasse um Wege zwischen Knoten in einem Graph zu finden.
|
|
* Eine implementierende Klasse ist beispielsweise {@link game.map.PathFinding}
|
|
*
|
|
* @param <T> Die Datenstruktur des Graphen
|
|
*/
|
|
public abstract class GraphAlgorithm<T> {
|
|
|
|
/**
|
|
* Innere Klasse um {@link Node} zu erweitern, aber nicht zu verändern Sie weist
|
|
* jedem Knoten einen Wert und einen Vorgängerknoten zu.
|
|
*
|
|
* @param <T>
|
|
*/
|
|
private static class AlgorithmNode<T> {
|
|
|
|
private Node<T> node;
|
|
private double value;
|
|
private AlgorithmNode<T> previous;
|
|
|
|
AlgorithmNode(Node<T> parentNode, AlgorithmNode<T> previousNode, double value) {
|
|
this.node = parentNode;
|
|
this.previous = previousNode;
|
|
this.value = value;
|
|
}
|
|
}
|
|
|
|
private Graph<T> graph;
|
|
|
|
// Diese Liste enthält alle Knoten, die noch nicht abgearbeitet wurden
|
|
private List<Node<T>> availableNodes;
|
|
|
|
// Diese Map enthält alle Zuordnungen
|
|
private Map<Node<T>, AlgorithmNode<T>> algorithmNodes;
|
|
|
|
/**
|
|
* Erzeugt ein neues GraphAlgorithm-Objekt mit dem dazugehörigen Graphen und dem
|
|
* Startknoten.
|
|
*
|
|
* @param graph der zu betrachtende Graph
|
|
* @param sourceNode der Startknoten
|
|
*/
|
|
public GraphAlgorithm(Graph<T> graph, Node<T> sourceNode) {
|
|
this.graph = graph;
|
|
this.availableNodes = new LinkedList<>(graph.getNodes());
|
|
this.algorithmNodes = new HashMap<>();
|
|
|
|
for (Node<T> node : graph.getNodes())
|
|
this.algorithmNodes.put(node, new AlgorithmNode<>(node, null, -1));
|
|
|
|
this.algorithmNodes.get(sourceNode).value = 0;
|
|
}
|
|
|
|
/**
|
|
* Diese Methode gibt einen Knoten mit dem kleinsten Wert, der noch nicht
|
|
* abgearbeitet wurde, zurück und entfernt ihn aus der Liste
|
|
* {@link #availableNodes}. Sollte kein Knoten gefunden werden, wird null
|
|
* zurückgegeben. Verbindliche Anforderung: Verwenden Sie beim Durchlaufen der
|
|
* Liste Iteratoren
|
|
*
|
|
* @return Der nächste abzuarbeitende Knoten oder null
|
|
*/
|
|
private AlgorithmNode<T> getSmallestNode() {
|
|
|
|
if (availableNodes.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
Iterator<Node<T>> iter = availableNodes.iterator();
|
|
AlgorithmNode<T> MinElem;
|
|
AlgorithmNode<T> tempElem;
|
|
|
|
if (iter.hasNext()) {
|
|
|
|
// Set minimum to first elements
|
|
MinElem = algorithmNodes.get(iter.next());
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
tempElem = algorithmNodes.get(iter.next());
|
|
if (tempElem.value > 0) {
|
|
if (tempElem.value < MinElem.value) {
|
|
MinElem = tempElem;
|
|
}
|
|
}
|
|
|
|
}
|
|
iter.remove();
|
|
|
|
return MinElem;
|
|
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Diese Methode startet den Algorithmus. Dieser funktioniert wie folgt: 1.
|
|
* Suche den Knoten mit dem geringsten Wert (siehe {@link #getSmallestNode()})
|
|
* 2. Für jede angrenzende Kante: 2a. Überprüfe ob die Kante passierbar ist
|
|
* ({@link #isPassable(Edge)}) 2b. Berechne den Wert des Knotens, in dem du den
|
|
* aktuellen Wert des Knotens und den der Kante addierst 2c. Ist der alte Wert
|
|
* nicht gesetzt (-1) oder ist der neue Wert kleiner, setze den neuen Wert und
|
|
* den Vorgängerknoten 3. Wiederhole solange, bis alle Knoten abgearbeitet
|
|
* wurden
|
|
*
|
|
* Nützliche Methoden:
|
|
*
|
|
* @see #getSmallestNode()
|
|
* @see #isPassable(Edge)
|
|
* @see Graph#getEdges(Node)
|
|
* @see Edge#getOtherNode(Node)
|
|
*/
|
|
public void run() {
|
|
|
|
AlgorithmNode<T> v = getSmallestNode();
|
|
|
|
while (v != null) {
|
|
for (Edge<T> e : graph.getEdges(v.node)) {
|
|
if (isPassable(e)) {
|
|
|
|
AlgorithmNode<T> n = algorithmNodes.get(e.getOtherNode(v.node));
|
|
|
|
double a = v.value + getValue(e);
|
|
|
|
if (n.value == -1 || n.value > a) {
|
|
|
|
n.value = a;
|
|
n.previous = v;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
v = getSmallestNode();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Diese Methode gibt eine Liste von Kanten zurück, die einen Pfad zu dem
|
|
* angegebenen Zielknoten representiert. Dabei werden zuerst beginnend mit dem
|
|
* Zielknoten alle Kanten mithilfe des Vorgängerattributs
|
|
* {@link AlgorithmNode#previous} zu der Liste hinzugefügt. Zum Schluss muss die
|
|
* Liste nur noch umgedreht werden. Sollte kein Pfad existieren, geben Sie null
|
|
* zurück.
|
|
*
|
|
* @param destination Der Zielknoten des Pfads
|
|
* @return eine Liste von Kanten oder null
|
|
*/
|
|
public List<Edge<T>> getPath(Node<T> destination) {
|
|
|
|
// Stage 1
|
|
ArrayList<Edge<T>> reversePath = new ArrayList<Edge<T>>();
|
|
|
|
AlgorithmNode<T> prevNode = algorithmNodes.get(destination);
|
|
if (prevNode == null)
|
|
return null;
|
|
|
|
while (prevNode != null) {
|
|
|
|
AlgorithmNode<T> prevPrevNode = prevNode.previous;
|
|
|
|
if (prevPrevNode == null)
|
|
return null;
|
|
|
|
reversePath.add(new Edge<T>(prevPrevNode.node, prevNode.node));
|
|
prevNode = prevPrevNode;
|
|
|
|
}
|
|
|
|
// Stage 2
|
|
Collections.reverse(reversePath);
|
|
return reversePath;
|
|
|
|
}
|
|
|
|
/**
|
|
* Gibt den betrachteten Graphen zurück
|
|
*
|
|
* @return der zu betrachtende Graph
|
|
*/
|
|
protected Graph<T> getGraph() {
|
|
return this.graph;
|
|
}
|
|
|
|
/**
|
|
* Gibt den Wert einer Kante zurück. Diese Methode ist abstrakt und wird in den
|
|
* implementierenden Klassen definiert um eigene Kriterien für Werte zu
|
|
* ermöglichen.
|
|
*
|
|
* @param edge Eine Kante
|
|
* @return Ein Wert, der der Kante zugewiesen wird
|
|
*/
|
|
protected abstract double getValue(Edge<T> edge);
|
|
|
|
/**
|
|
* Gibt an, ob eine Kante passierbar ist.
|
|
*
|
|
* @param edge Eine Kante
|
|
* @return true, wenn die Kante passierbar ist.
|
|
*/
|
|
protected abstract boolean isPassable(Edge<T> edge);
|
|
|
|
/**
|
|
* Gibt an, ob ein Knoten passierbar ist.
|
|
*
|
|
* @param node Ein Knoten
|
|
* @return true, wenn der Knoten passierbar ist.
|
|
*/
|
|
protected abstract boolean isPassable(Node<T> node);
|
|
}
|