/*
* Datei: RobEinfach.java
* Autor(en): Lukas König
* Java-Version: 6.0
* Erstellt: 13.03.2009
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.users.lukas.neuroCEGPM;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import eas.math.MiscMath;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Vector2D;
import eas.simulation.ConstantsSimulation;
import eas.simulation.Wink;
import eas.simulation.agent.GenericSensor;
import eas.simulation.spatial.sim2D.marbSimulation.endlAutomat.GlobaleMARBVariablen;
import eas.simulation.spatial.sim2D.standardAgents.AbstractAgent2D;
import eas.startSetup.ParCollection;
import eas.users.lukas.neuroCEGPM.simpleNeural.NeuroController;
import eas.users.lukas.neuroCEGPM.simpleNeural.NeuroTranslator;
import eas.users.lukas.neuroCEGPM.simpleNeural.mutation.Mutation;
import eas.users.lukas.neuroCEGPM.simpleNeural.mutation.MutationExpDecreasing;
import eas.users.lukas.neuroCEGPM.simpleNeural.mutation.MutationLinearGauss;
/**
* Beschreibt einen Roboter, der im Wesentlichen ein endlicher Automat mit
* einigen Zusatzeigenschaften ist. Insbesondere wird in dieser Klasse nicht
* auf die Besonderheit mutierbarer übersetzer eingegangen. Wenn mutierbare
* übersetzer (Translatoren) verwendet werden sollen, dann muss die abgeleitete
* Klasse <code>Roboter</code> verwendet werden.
*
* @author Lukas König
*/
public class RobNeural extends AbstractAgent2D<EnvironmentNeural> {
/**
*
*/
private static final long serialVersionUID = -702291816950310160L;
private LinkedList<BigDecimal> globalBest = null;
private LinkedList<BigDecimal> globalBestTrans = null;
private double globalBestFitness = Double.NEGATIVE_INFINITY;
private int memoryCount;
public double getGlobalBestFitness() {
return this.globalBestFitness;
}
/**
* Die aktuelle Position des Roboters.
*/
private Vector2D position;
/**
* Der Winkel der Blickrichtung (gemessen zu einem Nullwinkel, der in
* die Richtung (1, 0) zeigt).
*/
private double winkel;
/**
* Vektor zur Beschreibung der Blickrichtung.
*/
private Vector2D blickrichtung;
/**
* Die Ausdehnung als Rechteck in X- und Y-Richtung.
*/
private Vector2D ausd;
/**
* Die Sensoren. Wenn ein Sensor den Wert -1 enthält, wird er als
* "nicht aktuell" betrachtet und ein neues Ausrechnen beim nächsten
* Abfragen veranlasst.
*/
private int[] sensoren;
/**
* Liste generischer Sensoren
*/
private LinkedList<GenericSensor<?, ?, ?>> genericSensors;
/**
* Mapping von Sensor-IDs zu internen Sensornummern.
*/
private HashMap<String, Integer> sensorMapping;
/**
* Die Umgebung, auf der der Roboter steht.
*/
private EnvironmentNeural umg;
/**
* Speichert die Exkpunkte des Roboters.
*/
private Vector2D[] eckPunkte;
/**
* Speichert, ob die Eckpunkte aktuell berechnet sind.
*/
private boolean eckPunkteAktuell;
/**
* Die Winkel der Sensoren, gemessen am aktuellen Blickwinkel.
*/
private double[] absSensorWink;
/**
* Die Winkel der Sensoren, gemessen am aktuellen Nullwinkel.
*/
private double[] relSensorWink;
/**
* Die �ffnungswinkel der Sensoren.
*/
private double[] sensOeffn;
/**
* Die Anzahl der Sensoren.
*/
private int sensAnzIR;
/**
* Speichert alle zu prüfenden Sensorrichtungen (d.h. evtl. n / Sensor).
*
*/
private Vector2D[][] alleAbsSensRicht;
/**
* Die aktuelle Dauer des Experiments in Zyklen.
*/
private long expAktZyklen;
/**
* Der zuletzt ausgeführte Befehl.
*/
private int lastBf;
/**
* Identifizierungsnummer des Roboters (-1 heißt ungültig).
*/
private int id;
/**
* Der Zufallsgenerator.
*/
private Random rand;
/**
* Die Parameter.
*/
private ParCollection pars;
/**
* Die zuletzt ausgeführten Befehle.
*/
private ArrayList<Integer> befehle;
/**
* Ob der Roboter selektiert ist.
*/
private boolean selektiert;
/**
* Ab welcher Sensornummer nicht mehr berechnet werden muss.
*/
private static final int NICHT_BER_AB = 7;
/**
* Die Anzahl der bisher registrierten Unf�lle.
*/
private int anzUnfaelle = 0;
/**
* Die aktuell verwendeten Plugins für diesen Roboter.
*/
// private List<StandardPluginForEA> pluginsAkt;
/**
* Die Anzahl generischer Sensoren, die evolviert werden.
*/
private int anzahlGenSensEvolv;
private List<BigDecimal> genome;
private List<BigDecimal> genomeTrans;
private NeuroController controller;
private NeuroTranslator translator;
private Mutation mut;
private Mutation mutTrans;
public List<BigDecimal> getGenomeTrans() {
return this.genomeTrans;
}
/**
* Sets controller and (optionally) translator genomes of this robot and mutates the
* genomes before applying.
*
* @param genome The genome of the controller.
* @param genomeTrans The genome of the translator (may be null).
* @param rand A random number generator.
* @param parallel If calculations are allowed to be carried out in parallel.
*
* @return The thread to carry out calculations, if parallel allowed,
* null otherwise.
*/
public Thread setController(List<BigDecimal> genome, List<BigDecimal> genomeTrans, Random rand, boolean parallel) {
Runnable target = () -> {
this.genome = genome;
mut.mutate(this.genome);
this.genomeTrans = genomeTrans;
this.translator = new NeuroTranslator(rand.nextLong(), 10, 15);
if (this.genomeTrans != null) {
mutTrans.mutate(this.genomeTrans);
this.translator = this.translator.translateGenomeToTranslator(this.genomeTrans, false);
this.genomeTrans = this.translator.translateReverseTranslatorToGenome(); // Selbstreflexive Sequenz erstellen.
}
this.controller = this.translator.translateGenomeToController(genome, false);
};
if (parallel) {
Thread thread = new Thread(target);
thread.start();
return thread;
} else {
target.run();
return null;
}
}
// public Thread mutate(boolean parallel) {
// Runnable target = () -> {
// if (this.genomeTrans != null) {
// this.mutationTrans.mutate(this.genomeTrans); // Sequenz mutieren.
// NeuroTranslator transNeu = this.translator.translateGenomeToTranslator(this.genomeTrans, false); // Neuen mutierten Übersetzer erstellen.
// this.translator = transNeu;
// this.genomeTrans = this.translator.translateReverseTranslatorToGenome(); // Selbstreflexive Sequenz erstellen.
// }
//
// this.mutation.mutate(this.genome);
// this.controller = this.translator.translateGenomeToController(genome, false);
// };
//
// if (parallel) {
// Thread thread = new Thread(target);
// thread.start();
// return thread;
// } else {
// target.run();
// return null;
// }
// }
public List<BigDecimal> getGenome() {
return this.genome;
}
public NeuroController getController() {
return this.controller;
}
public NeuroTranslator getTranslator() {
return this.translator;
}
public Random getRand() {
return this.rand;
}
/**
* Initialisiert einen Roboter mit Werten von einem übergebenen Roboter.
* Dabei wird NICHT der Automat des Roboters kopiert und die ID des neuen
* Roboters wird auf einen UNG�LTIGEN Wert gesetzt.
*
* @param andererRob Der Roboter, dessen Werte übernommen werden sollen.
* @param params Parametersatz.
* @param zufall Der Zufallsgenerator.
* @param sel Ob der Roboter selektiert ist.
*/
public RobNeural(
final RobNeural andererRob,
final ParCollection params,
final Random zufall,
final boolean sel) {
this(andererRob.getPosition().x,
andererRob.getPosition().y,
andererRob.getAngle(),
andererRob.getAusd().x,
andererRob.getAusd().y,
andererRob.umg,
-1,
params,
zufall,
sel);
this.expAktZyklen = andererRob.expAktZyklen;
}
/**
* Initialisiert den Roboter.
*
* @param x X-Koordinate der Anfangsposition.
* @param y Y-Koordinate der Anfangsposition.
* @param wink Anfangswinkel der Blickrichtung in Grad.
* @param xAusd X-Ausdehnung.
* @param yAusd Y-Ausdehnung.
* @param umgebung Die Elternumgebung des Roboters.
* @param earl Der Earley-Erkenner für die Sequenz-Grammatik.
* @param mutation Das Mutationsverfahren für diesen Roboter.
* @param ident Die Identifikationsnummer des Roboters.
* @param params Die Parameter.
* @param zufall Der Zufallsgenerator.
* @param sel Ob der Roboter selektiert ist.
* @param autAnzahl Die Anzahl der Automaten, mit denen der Roboter
* ausgestattet sein soll.
*/
public RobNeural(
final double x,
final double y,
final double wink,
final double xAusd,
final double yAusd,
final EnvironmentNeural umgebung,
final int ident,
final ParCollection params,
final Random zufall,
final boolean sel) {
super(ident, umgebung, params);
this.pars = params;
this.selektiert = sel;
this.befehle = new ArrayList<Integer>();
this.id = ident;
this.rand = zufall;
this.mut = new MutationLinearGauss(rand, 0.075, 10);
this.mutTrans = new MutationExpDecreasing(rand, 0.01, 1000);
double alpha = wink * Math.PI / 180;
this.sensoren = new int[256];
this.position = new Vector2D(x, y);
this.ausd = new Vector2D(xAusd, yAusd);
verzerr = this.pars.getParValueDouble(eas.statistics.ConstantsStatistics.VERZERR_ATTR);
this.blickrichtung = new Vector2D(
Math.cos(alpha) * verzerr,
Math.sin(alpha) * verzerr);
this.winkel = this.gultWinkel(wink);
this.umg = umgebung;
this.eckPunkte = new Vector2D[4];
this.setEckpVeraendert();
this.sensAnzIR = ConstantsSimulation.SENS_RICHT.length;
this.absSensorWink = new double[this.sensAnzIR];
this.sensOeffn = ConstantsSimulation.SENS_OEFFN;
this.relSensorWink = new double[this.sensAnzIR];
this.alleAbsSensRicht = new Vector2D[ConstantsSimulation.ABSTUFUNGEN]
[this.sensAnzIR];
for (int i = 0; i < this.sensAnzIR; i++) {
this.relSensorWink[i] = ConstantsSimulation.SENS_RICHT[i];
}
this.setzeSensorRicht();
this.lastBf = 0;
// Generische Sensoren.
this.anzahlGenSensEvolv = 0;
this.genericSensors = new LinkedList<GenericSensor<?, ?, ?>>();
for (int i = 0; i < this.sensoren.length; i++) {
this.sensoren[i] = -1;
this.genericSensors.add(null);
}
this.sensorMapping = new HashMap<String, Integer>();
}
/**
* Simuliert einen Unfall, bei dem der Roboter um zufällige Grad gedreht
* und um einige Pixel verschoben wird. Ein Unfall sollte bei einer
* Kollision eingeleitet werden. Die Verschiebung findet nur statt, falls
* eine Kollision NACH dem Unfall vermieden werden kann (der Unfallzähler
* wird in jedem Fall hochgezählt).
*
* @param maxWinkel Der Winkel, um den der Roboter maximal gedreht wird.
* @return Ob der Unfall stattgefunden hat.
*/
public boolean unfallSimulation(final double maxWinkel) {
boolean erfolgreich = false;
double xVersch = 0;
double yVersch = 0;
int modus;
ArrayList<Integer> modi = new ArrayList<Integer>(6);
modi.add(new Integer(0));
modi.add(new Integer(1));
modi.add(new Integer(2));
modi.add(new Integer(3));
modi.add(new Integer(4));
modi.add(new Integer(5));
this.anzUnfaelle++;
GlobaleMARBVariablen.setCollCount(GlobaleMARBVariablen.getCollCount() + 1);
while (!erfolgreich && modi.size() > 0) {
modus = this.rand.nextInt(modi.size());
switch (modi.get(modus)) {
case 0:
xVersch = 1;
yVersch = 0;
break;
case 1:
xVersch = 0;
yVersch = 1;
break;
case 2:
xVersch = 1;
yVersch = 1;
break;
case 3:
xVersch = -1;
yVersch = 0;
break;
case 4:
xVersch = 0;
yVersch = -1;
break;
case 5:
xVersch = -1;
yVersch = -1;
break;
default:
break;
}
if (this.setPos(this.position.x + xVersch * ConstantsSimulation.SCHRITTW,
this.position.y + yVersch * ConstantsSimulation.SCHRITTW,
true)) {
erfolgreich = true;
}
modi.remove(modus);
}
if (this.setAngle(this.getAngle()
- (this.rand.nextDouble() * maxWinkel))) {
erfolgreich = true;
}
fitness -= 100;
return erfolgreich;
}
/**
* Gibt zurück, ob der aktuelle Roboter selektiert ist.
*
* @return Ob selektiert.
*/
public boolean isSelektiert() {
return this.selektiert;
}
/**
* Erzeugt einen gültigen Winkel zwischen 0°(=) und 360°(<).
*
* @param w Der zu normalisierende Winkel.
*
* @return Der normalisierte Winkel.
*/
private double gultWinkel(final double w) {
double w2 = w;
while (w2 >= 360) { // Modulo??
w2 = w2 - 360;
}
while (w2 < 0) {
w2 = w2 + 360;
}
return w2;
}
/**
* Setzt die Sensorrichtungen in abhängigkeit des aktuellen Blickwinkels.
*/
private void setzeSensorRicht() {
double betaRAD;
double beta;
double teilWinkel;
for (int i = 0; i < this.sensAnzIR; i++) {
this.absSensorWink[i] = this.gultWinkel(
this.winkel
+ this.relSensorWink[i]);
// Der Startwinkel für die Sensor�ffnung.
beta = this.absSensorWink[i] - this.sensOeffn[i] / 2;
// Der Teilwinkel der Messung.
teilWinkel = this.sensOeffn[i]
/ ((double) ConstantsSimulation.ABSTUFUNGEN - 1);
for (int j = 0; j < ConstantsSimulation.ABSTUFUNGEN; j++) {
betaRAD = Math.PI * beta / 180;
this.alleAbsSensRicht[j][i] = new Vector2D(
Math.cos(betaRAD),
Math.sin(betaRAD));
beta = beta + teilWinkel;
}
}
}
/**
* @return Der zu setzende String für diesen Roboter.
*/
private String setzString() {
// if (this.pars.getAnzModus() == 1) {
// if (this.isSelektiert()) {
// return this.getFitString();
// }
// } else if (this.pars.getAnzModus() == 2) {
// return this.getFitString();
// }
return "";
}
/**
* Setzt die Position des Roboters.
*
* @param x X-Koordinate.
* @param y Y-Koordinate.
*
* @return Ob der Roboter gesetzt werden konnte.
*/
public boolean setPos(final double x,
final double y) {
return this.setPos(x, y, false);
}
/**
* Setzt die Position des Roboters.
*
* @param x X-Koordinate.
* @param y Y-Koordinate.
* @param gegKollIgnore Ob die Kollision mit einem Gegenstand ignoriert
* werden soll (führt NICHT zu Verschieben des
* Gegenstands).
*
* @return Ob der Roboter gesetzt werden konnte.
*/
public boolean setPos(final double x,
final double y,
final boolean gegKollIgnore) {
double xOld = this.position.x;
double yOld = this.position.y;
byte farbe;
if (this.isSelektiert()) {
farbe = ConstantsSimulation.FARBE_SEL;
} else {
farbe = ConstantsSimulation.FARBE_ROB;
}
this.position.setzeKoord(x, y);
this.setEckpVeraendert();
if (this.umg.setzeRobWennMoegl(this,
gegKollIgnore,
farbe,
this.setzString())) {
return true;
} else {
this.position.setzeKoord(xOld, yOld);
this.setEckpVeraendert();
return false;
}
}
/**
* Setzt die Blickrichtung auf einen bestimmten Winkel in Grad. Dabei wird
* auch die SchrittLänge durch die _Länge_ des Blickrichtungs-Vektors
* gesetzt (automatisch). Der Winkel wird relativ zu einem Nullwinkel
* gemessen.
*
* @param alph Der Winkel der Blickrichtung in Grad.
*
* @return Ob der Blickwinkel geändert werden konnte.
*/
public boolean setAngle(final double alph) {
double alpha = this.gultWinkel(alph) * Math.PI / 180;
double winkOld = this.winkel;
double bxOld = this.blickrichtung.x;
double byOld = this.blickrichtung.y;
byte farbe;
if (this.isSelektiert()) {
farbe = ConstantsSimulation.FARBE_SEL;
} else {
farbe = ConstantsSimulation.FARBE_ROB;
}
this.winkel = this.gultWinkel(alph);
this.blickrichtung.setzeKoord(
Math.cos(alpha) * verzerr,
Math.sin(alpha) * verzerr);
this.setEckpVeraendert();
if (this.umg.setzeRobWennMoegl(this,
true,
farbe,
this.setzString())) {
this.setzeSensorRicht();
return true;
} else {
this.blickrichtung.setzeKoord(bxOld, byOld);
this.winkel = winkOld;
this.setEckpVeraendert();
return false;
}
}
/**
* Setzt einen Roboter unbedingt in das Feld. Kollisionen werden nicht
* betrachtet.
*
* @param x X-Koordinate der Position.
* @param y Y-Koordinate der Position.
* @param alpha Winkel der Blickrichtung.
*/
public void setRobUnbed(final double x,
final double y,
final double alpha) {
byte farbeRob;
if (this.isSelektiert()) {
farbeRob = ConstantsSimulation.FARBE_SEL;
} else {
farbeRob = ConstantsSimulation.FARBE_ROB;
}
Vector2D[] eckpunkte = new Vector2D[this.getEckp().nPoints()];
for (int i = 0; i < eckpunkte.length; i++) {
eckpunkte[i] = this.getEckp().get(i);
}
this.umg.setzeRob(
eckpunkte,
ConstantsSimulation.FARBE_HGND,
true,
false,
true);
this.setPosition(new Vector2D(x, y));
this.setBlickRicht(alpha);
this.umg.setzeRob(
eckpunkte,
farbeRob,
true,
false,
true);
}
/**
* @return Returns the blickrichtung.
*/
public Vector2D getBlickrichtung() {
return this.blickrichtung;
}
/**
* @return Returns the position.
*/
public Vector2D getPosition() {
return this.position;
}
/**
* @return Returns the winkel.
*/
public double getAngle() {
return this.winkel;
}
/**
* Berechnet die Eckpunkte des Roboters.
*/
private void berechneEckpunkte() {
Vector2D e1 = new Vector2D(this.position.x, this.position.y);
Vector2D e2 = new Vector2D(this.position.x, this.position.y);
Vector2D e3 = new Vector2D(this.position.x, this.position.y);
Vector2D e4 = new Vector2D(this.position.x, this.position.y);
Vector2D b = this.blickrichtung;
Vector2D bn = new Vector2D(-this.blickrichtung.y, this.blickrichtung.x);
e1.x = e1.x + (b.x * this.ausd.x + bn.x * this.ausd.y) / 2;
e1.y = e1.y + (b.y * this.ausd.x + bn.y * this.ausd.y) / 2;
e2.x = e2.x + (b.x * this.ausd.x - bn.x * this.ausd.y) / 2;
e2.y = e2.y + (b.y * this.ausd.x - bn.y * this.ausd.y) / 2;
e3.x = e3.x + (-b.x * this.ausd.x + bn.x * this.ausd.y) / 2;
e3.y = e3.y + (-b.y * this.ausd.x + bn.y * this.ausd.y) / 2;
e4.x = e4.x + (-b.x * this.ausd.x - bn.x * this.ausd.y) / 2;
e4.y = e4.y + (-b.y * this.ausd.x - bn.y * this.ausd.y) / 2;
this.eckPunkte[0] = e1;
this.eckPunkte[1] = e2;
this.eckPunkte[2] = e3;
this.eckPunkte[3] = e4;
this.eckPunkteAktuell = true;
}
private Polygon2D pol;
private int gatePassings;
public int getGatePassings() {
return this.gatePassings;
}
public void resetGatePassings() {
this.gatePassings = 0;
}
@Override
public Polygon2D getAgentShape() {
if (pol == null) {
pol = new Polygon2D();
pol.add(new Vector2D(-this.ausd.x / 2, -this.ausd.y / 2));
pol.add(new Vector2D(+this.ausd.x / 2, -this.ausd.y / 2));
pol.add(new Vector2D(+this.ausd.x / 2, +this.ausd.y / 2));
pol.add(new Vector2D(-this.ausd.x / 2, +this.ausd.y / 2));
}
return pol;
}
public Polygon2D getEckp() {
Polygon2D pol;
if (!this.eckPunkteAktuell) {
this.berechneEckpunkte();
}
pol = new Polygon2D();
for (Vector2D ecke : this.eckPunkte) {
pol.add(ecke);
}
return pol;
}
/**
* Geht in den nächsten Zustand über.
*
* @param simZyk Der Simulationszyklus.
*/
@Override
public void step(final Wink simZyk) {
if (lastPos == null) {
lastPos = new Vector2D(this.getPosition());
}
final double speedFactor = 5;
int[] sensValues = getSensorWerte();
for (int i = 0; i < 7; i++) {
this.controller.setNeuronValue(i, sensValues[i]);
}
this.controller.propagate();
double left = speedFactor * this.controller.getNeuronOutput(
this.controller.getNeuronCount() + NeuroController.LEFT_WHEEL_NUM_COUNTED_FROM_END);
double right = speedFactor * this.controller.getNeuronOutput(
this.controller.getNeuronCount() + NeuroController.RIGHT_WHEEL_NUM_COUNTED_FROM_END);
this.setGeschRadLinks(left);
this.setGeschRadRechts(right);
if (!this.vorwaerts()) {
if (!this.unfallSimulation(360)) {
// this.getEnvironment().setzeRobotRand(this);
// System.out.println("HAPPENED");
}
}
// Evolution - Fitness.
if (simZyk.getLastTick() % 40 == 24) {
this.fitness += this.getPosition().distance(lastPos);
this.fitness /= 1.2;
if (isOnLeftHalf(this.getPosition()) != isOnLeftHalf(lastPos)) {
fitness += 200;
gatePassings++;
}
lastPos = new Vector2D(this.getPosition());
}
// Memory genome.
storeMemoryGenome();
applyMemoryGenome(simZyk);
}
private boolean isOnLeftHalf(Vector2D position) {
return position.x <= this.getEnvironment().ausdX() / 2;
}
private void applyMemoryGenome(final Wink simZyk) {
Double movAvFit = MiscMath.movingAverage("RobNeural.FitApply" + this.id(), this.getFitness(), 10);
if (simZyk.getLastTick() % 200 == 100) {
if (movAvFit != null && movAvFit < 0 && this.globalBest != null) {
this.memoryCount++;
this.genome = new LinkedList<>(this.globalBest);
LinkedList<BigDecimal> trGenome = null;
if (this.globalBestTrans != null) {
trGenome = new LinkedList<BigDecimal>(this.globalBestTrans);
}
this.genomeTrans = trGenome;
this.setFitness(this.globalBestFitness);
this.setController(this.genome, this.genomeTrans, rand, false);
}
}
}
private void storeMemoryGenome() {
Double movAvFit = MiscMath.movingAverage("RobNeural.FitStore" + this.id(), this.getFitness(), 10);
if (movAvFit != null && movAvFit > this.globalBestFitness) {
this.globalBest = new LinkedList<BigDecimal>(this.genome);
LinkedList<BigDecimal> trGenome = null;
if (this.genomeTrans != null) {
trGenome = new LinkedList<BigDecimal>(this.genomeTrans);
}
this.globalBestTrans = trGenome;
this.globalBestFitness = movAvFit;
}
}
/**
* @param memoryCount The memoryCount to set.
*/
public void resetMemoryCount() {
this.memoryCount = 0;
}
public int getMemoryCount() {
return this.memoryCount;
}
private Vector2D lastPos;
private double fitness = 0;
public double getFitness() {
return fitness;
}
public void setFitness(double fitness) {
this.fitness = fitness;
}
/**
* Gibt einen Sensorenwert als den zugehörigen in der Umgebung gemessenen
* Wert zurück. Außerdem wird der betreffende Sensor im Array auf den
* entsprechenden Wert gesetzt, falls es ein statischer Sensor ist.
*
* @param sensNum Die Nummer des zu berechnenden Sensors.
*
* @return Der gemessene Sensorwert.
*/
@Override
public Object sense(final int sensNum) {
if (sensNum < NICHT_BER_AB) {
if (this.sensoren[sensNum] < 0) {
this.sensoren[sensNum] = this.umg.berSensWerte(
this, sensNum);
}
}
if (this.genericSensors.get(sensNum) != null) {
if (this.sensoren[sensNum] >= 0) {
throw new RuntimeException("Kollision von generischem ("
+ this.sensoren[sensNum]
+ ") mit statischem ("
+ extracted(sensNum).sense(
this.umg, this)
+ ") Sensor auf Position " + sensNum);
}
}
if (this.genericSensors.get(sensNum) != null) {
return extracted(sensNum).sense(
this.getEnvironmentNeural(),
this);
}
return this.sensoren[sensNum];
}
/**
* Extrahierte Methode zum ungeprüften Casten.
*
* @param sensNum Die Sensornummer.
*
* @return Der generische Sensor mit dieser Nummer.
*/
@SuppressWarnings("unchecked")
private GenericSensor<?, EnvironmentNeural, RobNeural> extracted(final int sensNum) {
return (GenericSensor<?, EnvironmentNeural, RobNeural>)
this.genericSensors.get(sensNum);
}
/**
* Gibt einen generischen Sensorenwert als den zugehörigen in der Umgebung
* gemessenen Wert zurück.
*
* @param sensID Der Name des zu berechnenden Sensors.
*
* @return Der berechnete Sensorwert.
*/
public Object getGenSensWert (final String sensID) {
return this.sense(this.getGenSensInternalNum(sensID));
}
/**
* Die intern verwendete Nummer für diesen Sensor.
*
* @param sensID Die ID des Sensors (String).
*
* @return Die interne ID (int). RuntimeException, falls nicht vorhanden.
*/
public int getGenSensInternalNum(final String sensID) {
return this.sensorMapping.get(sensID);
}
/**
* Achtung, diese Methode sollte nicht in jedem Zyklus aufgerufen werden,
* da sie zeitaufw�ndig sein kann.
*
* @return Alle Sensorwerte der statischen Sensoren.
*/
public int[] getSensorWerte() {
for (int i = 0; i < 7; i++) {
this.sense(i);
}
return this.sensoren;
}
/**
* @return Returns the alleAbsSensRicht.
*/
public Vector2D[][] getAlleAbsSensRicht() {
this.setzeSensorRicht();
return this.alleAbsSensRicht;
}
/**
* @return Returns the sensOeffn.
*/
public double[] getSensOeffn() {
return this.sensOeffn;
}
/**
* Markiert die aktuellen Eckpunkte und berechneten Sensorwerte als
* ungültig.
*/
private void setEckpVeraendert() {
this.eckPunkteAktuell = false;
for (int i = 0; i < NICHT_BER_AB; i++) {
this.sensoren[i] = -1;
}
}
/**
* ACHTUNG! Diese Methode garantiert kein gültiges Platzieren des Roboters
* auf dem Feld. Sie ist nur dazu gedacht, den Roboter einmalig zu Beginn
* der Simulation zu verschieben. Es muss jedoch immer geprüft werden, ob
* die Verschiebung gültig war!
*
* @param pos The position to set.
*/
public void setPosition(final Vector2D pos) {
this.setEckpVeraendert();
this.position = pos;
}
/**
* ACHTUNG! Diese Methode garantiert kein gültiges Platzieren des Roboters
* auf dem Feld. Sie ist nur dazu gedacht, den Roboter einmalig zu Beginn
* der Simulation zu verschieben. Es muss jedoch immer geprüft werden, ob
* die Verschiebung gültig war!
*
* @param beta Die Blickrichtung des Roboters.
*/
public void setBlickRicht(final double beta) {
this.setEckpVeraendert();
this.winkel = beta;
double alpha = this.gultWinkel(beta) * Math.PI / 180;
this.winkel = this.gultWinkel(beta);
this.blickrichtung.setzeKoord(
Math.cos(alpha) * verzerr,
Math.sin(alpha) * verzerr);
}
/**
* Setzt die Attribute des Objekts zurück.
*/
public void zuruecksetzen() {
this.expAktZyklen = 0;
this.lastBf = 0;
}
/**
* @return Returns the umgebung.
*/
@Override
public EnvironmentNeural getEnvironment() {
return this.umg;
}
/**
* @return Returns the umgebung.
*/
public EnvironmentNeural getEnvironmentNeural() {
return this.getEnvironment();
}
/**
* @return Returns the id.
*/
@Override
public int id() {
return this.id;
}
/**
* @return Returns the ausd.
*/
public Vector2D getAusd() {
return this.ausd;
}
/**
* @return Returns the befehle.
*/
public ArrayList<Integer> getBefehle() {
return this.befehle;
}
/**
* Selektiert den aktiven Roboter.
*
* @param sel The selektiert to set.
*/
public void setSelektiert(final boolean sel) {
if (this.selektiert != sel) {
this.selektiert = sel;
}
}
/**
* @return Returns the anzUnfaelle.
*/
public int getAnzUnfaelle() {
return this.anzUnfaelle;
}
/**
* Setzt die Anzahl der Unf�lle zurück.
*/
public void resetAnzUnf() {
this.anzUnfaelle = 0;
}
/**
* @return Returns the pars.
*/
@Override
public ParCollection getPars() {
return this.pars;
}
/**
* @return Die aktuelle Zyklenzahl des Gesamtexperiments.
*/
public long getExpAktZyklen() {
return this.expAktZyklen;
}
/**
* @return Returns the lastBf.
*/
public int getLastBf() {
return this.lastBf;
}
/**
* @param lastBf The lastBf to set.
*/
public void setLastBf(int lastBf) {
this.lastBf = lastBf;
}
/**
* Maximalgeschwidigkeit der Räder.
*/
private final double maxGesch = 10;
/**
* Minimalgeschwidigkeit der Räder.
*/
private final double minGesch = -10;
/**
* Geschwindigkeit des linken Rades in Winkel (Radians) / Zyklus.
*/
private double geschwRechts = 0;
/**
* Geschwindigkeit des rechten Rades in Winkel (Radians) / Zyklus.
*/
private double geschwLinks = 0;
/**
* Geschwindigkeit, unter der das Rad als nichtbewegend gilt.
*/
private final double epsilon = 1e-100;
/**
* Berechnet die Strecke, die ein Rad auf eine Ebene abrollt, wenn es sich
* bei einem angegeben Radius um einen bestimmten Winkel um die Mittelachse
* dreht. Die Reibung wird dabei als unendlich angesehen. (Also eigentlich
* wird einfach die Länge des Kreisbogens um den angegebenen Winkel
* berechnet.)
*
* @param winkRadians Der Winkel, um den sich das Rad dreht.
* @param radRadius Der Radius des Rades.
*
* @return Die vom Rad abgerollte Strecke.
*/
private double streckeGefahrenBeiWink(
final double winkRadians,
final double radRadius) {
return radRadius * winkRadians;
}
/**
* führt die angegebene Drehung / Zyklus der beiden Räder INVERTIERT ab.
* Für eine nicht invertierte Bewegung ist die Methode
* <code>vorwaerts</code> vorgesehen.
*
* @return Ob die Bewegung erfolgreich war (oder aufgrund von Kollisionen
* zurückgesetzt werden musste).
*/
public boolean rueckwaerts() {
double altdrehL = this.geschwLinks;
double altdrehR = this.geschwRechts;
boolean erfolgreich;
this.geschwLinks *= -1;
this.geschwRechts *= -1;
erfolgreich = this.vorwaerts();
this.geschwLinks = altdrehL;
this.geschwRechts = altdrehR;
return erfolgreich;
}
private Double verzerr = null;
/**
* führt die angegebene Drehung / Zyklus der beiden Räder ab. Dabei wird
* die Drehrichtung so beibehalten, wie sie die Motoren angeben. Für eine
* Invertierung der Drehrichtung kann die Methode <code>rueckwaerts</code>
* verwendet werden.
*
* @return Ob die Bewegung erfolgreich war (oder aufgrund von Kollisionen
* zurückgesetzt werden musste).
*/
public boolean vorwaerts() {
// Bevor "getRadRadius" in die Umgebung verlagert wurde.
// double r = this.streckeGefahrenBeiWink(
// this.geschwRechts, this.pars.getRadRadius().x)
// * this.pars.getVerzerr();
// double l = this.streckeGefahrenBeiWink(
// this.geschwLinks, this.pars.getRadRadius().y)
// * this.pars.getVerzerr();
double r = this.streckeGefahrenBeiWink(
this.geschwRechts, 1)
* verzerr;
double l = this.streckeGefahrenBeiWink(
this.geschwLinks, 1)
* verzerr;
double b = this.ausd.x * verzerr;
double rad;
double alpha;
Vector2D neuPos = new Vector2D(this.getPosition());
Vector2D mitte = new Vector2D(this.getPosition());
Vector2D richtung = new Vector2D(this.getBlickrichtung());
Vector2D altePos;
double alteBlick;
boolean bool;
if (Math.abs(l) < this.epsilon && Math.abs(r) < this.epsilon) {
return true; // Beide Räder auf Null.
} else if (Math.abs(l - r) < this.epsilon) { // Differenz gleich.
neuPos = new Vector2D(this.getPosition());
richtung = new Vector2D(this.getBlickrichtung());
richtung.setLength(r);
neuPos.translate(richtung);
// umg.setzeLinie(mitte.x, mitte.y, neuPos.x, neuPos.y,
// Konstanten.FARBEN_BENUTZER[farbe], true, false, true, false);
return this.setPos(neuPos.x, neuPos.y, false);
} else if (Math.abs(l + r) < this.epsilon) { // Genau entgegengesetzt.
rad = b / 2;
alpha = r / rad;
// umg.setzeLinie(mitte.x, mitte.y, neuPos.x, neuPos.y,
// Konstanten.FARBEN_BENUTZER[farbe], true, false, true, false);
return this.setAngle(this.getAngle() - alpha / Math.PI * 180);
} else if (Math.abs(l) > Math.abs(r)) {
rad = b / (l / r - 1) + b / 2;
richtung.ortho();
richtung.setLength(rad);
mitte.sub(richtung);
if (r * l > 0) { // r und l haben dasselbe Vorzeichen.
alpha = r / rad;
} else {
alpha = 2 * r / b;
}
alpha = l / rad;
neuPos.rotate(mitte, alpha);
altePos = new Vector2D(this.getPosition());
alteBlick = this.getAngle();
bool = this.setPos(neuPos.x, neuPos.y, false);
// umg.setzeLinie(mitte.x, mitte.y, neuPos.x, neuPos.y,
// Konstanten.FARBEN_BENUTZER[farbe], true, false, true, false);
if (bool) {
bool = this.setAngle(this.getAngle() + alpha / Math.PI * 180);
if (!bool) {
this.setAngle(alteBlick);
this.setPos(altePos.x, altePos.y, false);
return false;
}
return true;
} else {
this.setPos(altePos.x, altePos.y, false);
return false;
}
} else if (Math.abs(r) > Math.abs(l)) {
rad = b / (r / l - 1) + b / 2;
richtung.ortho();
richtung.setLength(rad);
mitte.translate(richtung);
if (r * l > 0) { // r und l haben dasselbe Vorzeichen.
alpha = r / rad;
} else {
alpha = 2 * r / b;
}
neuPos.rotate(mitte, -alpha);
altePos = new Vector2D(this.getPosition());
alteBlick = this.getAngle();
bool = this.setPos(neuPos.x, neuPos.y, false);
// umg.setzeLinie(mitte.x, mitte.y, neuPos.x, neuPos.y,
// Konstanten.FARBEN_BENUTZER[farbe], true, false, true, false);
if (bool) {
bool = this.setAngle(this.getAngle() - alpha / Math.PI * 180);
if (!bool) {
this.setAngle(alteBlick);
this.setPos(altePos.x, altePos.y, false);
return false;
}
return true;
} else {
this.setPos(altePos.x, altePos.y, false);
return false;
}
}
throw new RuntimeException("Fall nicht abgefangen in Methode vorwaerts"
+ ": l = " + l + " / r = " + r + ".");
}
/**
* Setzt die Radgeschwidigkeit des linken Rades
* in Winkel (Radians) / Zyklus. (Wird bei minGesch und maxGesch
* abgeschnitten.)
*
* @param wert Der Wert, auf den die Geschwindigkeit gesetzt werden soll.
*/
public void setGeschRadLinks(final double wert) {
// double alterWert = this.geschwLinks;
this.geschwLinks = Math.min(
Math.max(wert, this.minGesch),
this.maxGesch);
// if (Math.abs(alterWert - this.geschwLinks) >= this.epsilon) {
// StaticMethods.log(
// StaticMethods.LOG_STAGE1,
// "Neue Geschwindigkeit linkes Rad - " + wert + " / Zyklus",
// this.pars);
// }
}
// int farbe = 0;
/**
* Setzt die Radgeschwidigkeit des rechten Rades
* in Winkel (Radians) / Zyklus. (Wird bei minGesch und maxGesch
* abgeschnitten.)
*
* @param wert Der Wert, auf den die Geschwindigkeit gesetzt werden soll.
*/
public void setGeschRadRechts(final double wert) {
// double alterWert = this.geschwRechts;
this.geschwRechts = Math.min(
Math.max(wert, this.minGesch),
this.maxGesch);
// if (Math.abs(alterWert - this.geschwRechts) >= this.epsilon) {
//// farbe = new Random().nextInt(Konstanten.FARBEN_BENUTZER.length);
//
// StaticMethods.log(
// StaticMethods.LOG_STAGE1,
// "Neue Geschwindigkeit rechtes Rad - " + wert + " / Zyklus",
// this.pars);
// }
}
/**
* @return Returns the anzahlGenSensEvolv.
*/
public int getAnzahlGenSensEvolv() {
return this.anzahlGenSensEvolv;
}
/**
* @return Returns the geschwRechts.
*/
public double getGeschwRechts() {
return this.geschwRechts;
}
/**
* @return Returns the geschwLinks.
*/
public double getGeschwLinks() {
return this.geschwLinks;
}
private transient BufferedImage sensImg;
/**
* @return View of the robot's controller.
*/
@Override
public BufferedImage getInsideView() {
if (sensImg != null && this.getEnvironment().getSimTime().getCurrentTime().getLastTick() % 10 != 0) {
return sensImg;
}
Graphics2D g;
BufferedImage mrtController = this.controller.getNeuralImage();
BufferedImage mrtTranslator = this.translator.getNeuralImage();
sensImg = new BufferedImage(
this.umg.getFeld()[0].length + mrtController.getWidth() + mrtTranslator.getWidth() + 100,
700,
BufferedImage.TYPE_INT_RGB);
g = sensImg.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, sensImg.getWidth(), sensImg.getHeight());
g.setColor(Color.BLUE);
g.drawString("Fitness: " + this.fitness, 250, 50);
// Sensoren.
int i = 0;
// Standardsensoren:
final int letterWidth = 10;
final int letterHeight = 15;
for (int j = 0; j < this.sensAnzIR; j++) {
String value = "IR " + (j + 1) + ": " + this.sense(j).toString();
int width = value.length() * letterWidth;
int height = letterHeight;
g.setColor(Color.white);
g.fillRect(146, 52 + (i - 1) * 15, width, height);
g.setColor(Color.black);
g.drawRect(146, 52 + (i - 1) * 15, width, height);
g.drawString(
value,
150,
50 + i * 15);
i++;
}
// zusätzliche Sensoren:
for (String s : this.sensorMapping.keySet()) {
g.drawImage(this.getSensor(s).getSensorView(this.getEnvironment(), this),
0,
50 + i * 15,
null);
i++;
}
g.drawString("Controller", mrtTranslator.getWidth(), 170);
g.drawImage(
mrtController,
mrtTranslator.getWidth(),
185,
null);
g.drawString("Translator", 0, 170);
g.drawImage(
mrtTranslator,
0,
185,
null);
g.drawString("AbsWeightHash: " + translator.getAbsWeightMeasure(), 0, 200 + Math.max(mrtTranslator.getHeight(), mrtController.getHeight()));
return sensImg;
}
}