Package eas.users.lukas.neuroCEGPM

Source Code of eas.users.lukas.neuroCEGPM.RobNeural

/*
* 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;
    }
}
TOP

Related Classes of eas.users.lukas.neuroCEGPM.RobNeural

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.