/**
* Copyright (c) 2012 mops� Productions
*/
package de.hwrberlin.it11.tsp.controller;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import de.hwrberlin.it11.tsp.constant.IterationMode;
import de.hwrberlin.it11.tsp.constant.PropertyChangeTypes;
import de.hwrberlin.it11.tsp.interfaces.AlgorithmListener;
import de.hwrberlin.it11.tsp.model.AntProject;
import de.hwrberlin.it11.tsp.model.Edge;
import de.hwrberlin.it11.tsp.model.Node;
import de.hwrberlin.it11.tsp.model.Result;
/**
* Verwaltet den Ameisenalgorithmus.
*/
public class AntController {
/** Das Ameisenprojekt */
private AntProject _project;
/** Der momentan benutzte Iterationsmodus */
private IterationMode _iterationMode;
/** Iterationsz�hler, z�hlt mit wie viele Iterationen vor�ber sind */
private int _iterationCounter;
/** Referenz auf den Thread, der momentan den Algorithmus bearbeitet */
private volatile Thread _algorithm;
/** Die Liste an AlgorithmListener */
private List<AlgorithmListener> _listenerList;
/** Pausiert Flag */
private boolean _pause;
/**
* Erzeugt einen neuen AntController mit dem angegebenen AntProject.
*
* @param pProject
* das zu benutzende AntProject
*/
public AntController(AntProject pProject) {
_project = pProject;
_iterationMode = IterationMode.COUNT;
_listenerList = new ArrayList<AlgorithmListener>();
}
/**
* Gibt das AntProject zur�ck.
*
* @return das AntProject
*/
public AntProject getProject() {
return _project;
}
/**
* Setzt den angegebenen Iterationsmodus auf den benutzten Iterationsmodus.
*
* @param pMode
* der zu benutzende Iterationsmodus
*/
public void setIterationMode(IterationMode pMode) {
_iterationMode = pMode;
for (AlgorithmListener listener : _listenerList) {
listener.iterationModeChanged(pMode);
}
}
/**
* Initialisiert den Algorithmus. Das umfasst das Zur�cksetzen der Kanten (Pheromonwert auf den initialen Pheromonwert setzen), Reinitialisierung
* des Results, Setzen des Iterationsz�hlers auf 0.
*/
public void init() {
_project.initEdges();
_project.getResult().init();
_iterationCounter = 0;
}
/**
* �berpr�ft anhand des momentan benutzten Iterationsmodus', ob der Algorithmus stoppen muss oder nicht.
*
* @return true, wenn die Abbruchbedingung erreicht ist, andernfalls false
*/
private boolean isConditionMet() {
if (_iterationCounter > 0) {
if (_iterationMode == IterationMode.COUNT) {
if (_iterationCounter == _project.getParameter().getIterationCount()) {
return true;
}
}
if (_iterationMode == IterationMode.LENGTH) {
if (_project.getResult().getBestTourLengthGlobal() <= _project.getParameter().getMaximumTourLength() && _iterationCounter != 0) {
return true;
}
}
if (_iterationMode == IterationMode.SOLUTION) {
// Liste der besten Tour klonen, um die Ausgangsliste nicht in ihrer Struktur zu ver�ndern
List<Node> bestTourGlobalCopy = new ArrayList<Node>(_project.getResult().getBestTourGlobal());
// Die Liste der Knoten, so wie sie in der Ausgangsdatei gespeichert waren
List<Node> nodeList = _project.getTSPData().getNodeList();
List<Integer> indexList = _project.getOptimalTourIndeces();
// Die Liste so rotieren, dass die erste Node in der nodeList dem ersten Index der indexList entspricht
Node startNode = nodeList.get(indexList.get(0) - 1);
int startNodeIndex = bestTourGlobalCopy.indexOf(startNode);
Collections.rotate(bestTourGlobalCopy, startNodeIndex * -1);
for (int i = 0; i < bestTourGlobalCopy.size(); i++) {
Node node = bestTourGlobalCopy.get(i);
Node otherNode = nodeList.get(indexList.get(i) - 1);
if (node != otherNode) {
return false;
}
}
return true;
}
}
return false;
}
/**
* Startet den Algorithmus. Ein neuer Thread wird erstellt, der diesen Algorithmus abarbeitet.
*/
public void start() {
for (AlgorithmListener listener : _listenerList) {
listener.algorithmStarted();
}
_algorithm = new Thread() {
@Override
public void run() {
init();
double bestTourLengthGlobal = -1;
List<Double> averageLengthGlobalValues = new ArrayList<Double>();
Result result = _project.getResult();
while (!isConditionMet()) {
if (Thread.currentThread() != _algorithm) {
return;
}
long iterationStartTime = System.currentTimeMillis();
if (!_pause) {
// Ameisen erzeugen
List<Ant> antList = new ArrayList<Ant>();
for (int j = 0; j < _project.getParameter().getAntCount(); j++) {
antList.add(new Ant(_project.getTSPData().getNodeList()
.get((int) (Math.random() * _project.getTSPData().getNodeList().size())), _project));
}
double bestTourLengthIteration = -1;
List<Double> averageLengthIterationValues = new ArrayList<Double>();
// Jede Ameise laufen lassen und auf eine neue beste Tour �berpr�fen
for (Ant ant : antList) {
while (!ant.isFinished()) {
ant.next();
}
double travelledDistance = ant.getTravelledDistance();
averageLengthIterationValues.add(travelledDistance);
if (travelledDistance < bestTourLengthIteration || bestTourLengthIteration == -1) {
bestTourLengthIteration = travelledDistance;
result.setBestTourLengthIteration(travelledDistance);
result.setBestTourIteration(ant.getVisitedNodes());
if (bestTourLengthIteration < bestTourLengthGlobal || bestTourLengthGlobal == -1) {
bestTourLengthGlobal = bestTourLengthIteration;
averageLengthGlobalValues.add(bestTourLengthGlobal);
result.setBestTourLengthGlobal(bestTourLengthIteration);
result.setBestTourGlobal(result.getBestTourIteration());
}
}
}
// Verdunsten der Pheromone auf den Kanten
for (Edge edge : _project.getEdgeList()) {
edge.setPheromone((1 - _project.getParameter().getEvaporationParameter()) * edge.getPheromone());
}
// Jede Ameise platziert Pheromone auf den Kanten, die sie besucht hat
for (Ant ant : antList) {
for (int j = 0; j < ant.getVisitedNodes().size() - 1; j++) {
Node node = ant.getVisitedNodes().get(j);
Node otherNode = ant.getVisitedNodes().get(j + 1);
Edge edge = node.getEdge(otherNode);
edge.setPheromone(edge.getPheromone() + _project.getParameter().getPheromonUpdateParameter()
/ ant.getTravelledDistance());
}
}
// Berechnung der Durchschnittswerte der Tourl�ngen
double iterationLengthSum = 0;
for (Double iterationLengthValue : averageLengthIterationValues) {
iterationLengthSum += iterationLengthValue;
}
result.setAverageTourLengthIteration(iterationLengthSum / averageLengthIterationValues.size());
double globalLengthSum = 0;
for (Double globalLengthValue : averageLengthGlobalValues) {
globalLengthSum += globalLengthValue;
}
result.setAverageTourLengthGlobal(globalLengthSum / averageLengthGlobalValues.size());
// Eine Iteration ist vor�ber, Event am AntProject feuern
if (Thread.currentThread() == _algorithm) {
_project.firePropertyChange(PropertyChangeTypes.PROJECT_ITERATIONFINISHED, null, ++_iterationCounter);
}
}
long iterationEndTime = System.currentTimeMillis();
if (!_pause) {
result.setElapsedTime(result.getElapsedTime() + iterationEndTime - iterationStartTime);
}
}
AntController.this.stop();
}
};
_algorithm.start();
}
/**
* Stoppt den Algorithmus, indem die Referenz auf den ausf�hrenden Thread auf null gesetzt wird.
*/
public void stop() {
_algorithm = null;
_pause = false;
for (AlgorithmListener listener : _listenerList) {
listener.algorithmStopped();
}
}
/**
* Pausiert den Algorithmus.
*/
public void pause() {
_pause = true;
for (AlgorithmListener listener : _listenerList) {
listener.algorithmPaused();
}
}
/**
* Setzt den Algorithmus fort.
*/
public void resume() {
_pause = false;
for (AlgorithmListener listener : _listenerList) {
listener.algorithmResumed();
}
}
/**
* Gibt an, ob der Algorithmus gerade l�uft.
*
* @return true wenn der Algorithmus l�uft, andernfalls false
*/
public boolean isRunning() {
return _algorithm != null;
}
/**
* F�gt den angegebenen AlgorithmListener der ListenerList hinzu.
*
* @param pListener
* der hinzuzuf�gende AlgorithmListener
*/
public void addAlgorithmListener(AlgorithmListener pListener) {
_listenerList.add(pListener);
}
/**
* Entfernt den angegebenen AlgorithmListener aus der ListenerList.
*
* @param pListener
* der zu entfernende AlgorithmListener
*/
public void removeAlgorithmListener(AlgorithmListener pListener) {
_listenerList.remove(pListener);
}
}