function [] = strengthga( overallfitness )
%STRENGTHGA Genetischer Algorithmus zur Anpassung der Fitnesswerte
%   Dieser GA soll nach mehreren erfolgten Simulationsläufen mit dem
%   gleichen Regelsatz aber unterschiedlichen Fitnesswerten der einzelnen
%   Regeln die Ergebnisse der Läufe miteinander vergleichen und aufgrund
%   der globalen Fitness der entstandenen Strukturen die einzelnen
%   Stärkewerte der Regeln genetisch rekombinieren. Dadurch sollen neue
%   Regelsätze erzeugt werden, die sich nur durch die Fitness der einzelnen
%   Regeln von den alten Regelsätzen unterscheiden.

global wespe;
global Wabengitter;
global MittelWabe;

% variable Parameter
%
probcrossover = 0.6; % feste Crossoverwahrscheinlichkeit
probmutation = 0.2; % feste Mutationswahrscheinlichkeit
elternzahl = 2; % Zahl der auszuwählenden Individuen pro GA-Durchlauf
% - muss ein Vielfaches von 2 sein

overallrezip = 1 ./ overallfitness; % Die reziproken Werte der Gesamtfitness
% zur Berechnung der zu löschenden Elemente

overallrezip = overallrezip / sum(overallrezip); 
% Normierung auf Gesamtsumme 1

overallfitness = overallfitness / sum(overallfitness); % Normierung auf Gesamtfitness 1
% wichtig für Roulette_wheel-Selection

cumulfitness = 0;
cumulrezip = 0;
% es werden zwei Vektoren (maxfitness und maxrezip) erzeugt, in denen für
% jeden Regelsatz in wespe ein Wert abgespeichert ist, der der Position des
% jeweiligen Abschnitts auf dem Roulette-Wheel entspricht. Der Vektor ist
% normiert auf 1, d.h. der letzte Wert im Vektor sollte 1.0 betragen.
for regelsatz=1:length(overallfitness)
    cumulfitness = cumulfitness + overallfitness(regelsatz);
    maxfitness(regelsatz) = cumulfitness;
    cumulrezip = cumulrezip + overallrezip(regelsatz);
    maxrezip(regelsatz) = cumulrezip;
end

% Selektion
eltern = roulettemz(overallfitness, maxfitness);

% Rekombination
newstrings = onepointxover(eltern);

% Mutation
newstrings = mutation(newstrings);

% Selektion der zu löschenden bauagent-Objekte
% - Es wird das gleiche Prinzip zur Roulette-Wheel-Selektion angewendet wie
% bei der Eltern-Selektion, nur dass auf den Kehrwerten der Fitnesswerte
% gearbeitet wird und deshalb die Selektionswahrscheinlichkeit mit
% steigender Fitness abnimmt, d.h. dass weniger fitte bauagent-Objekte mit
% höherer Wahrscheinlichkeit gelöscht werden.
todelete = roulette(overallrezip, maxrezip);

% Erzeugen von zwei zusätzlichen bauagent-Objekten in wespe
% - nicht mehr nötig, da die Populationsgröße konstant gehalten werden soll
% und für die neuen Objekte alte überschrieben werden
% Stattdessen direktes Ersetzen der zu löschenden Objekte und Einfügen der
% neuen Fitnessstrings

% rulesetanzahl = size(wespe,1);
for elternindex=1:elternzahl
    wespe(todelete(elternindex),1) = bauagent(Wabengitter(1:2, MittelWabe)', 1);
    wespe(todelete(elternindex),1).classifierfitnessstring = newstrings(1,:);
    wespe(todelete(elternindex),1).updatefitness;
end


    function index = roulette(fitnessvektor, maxvektor)
        % Roulette-Wheel-Selection ohne Zurücklegen - wird zur Auswahl der aus
        % der Population zu löschenden Individuen verwendet. Jedes Individuum kann
        % nur einmal gelöscht werden.
        
        for candid=1:elternzahl
            a = rand;
            % b = rand;
            
            for ruleset=length(fitnessvektor):-1:2
                if ( (a >= maxvektor(ruleset-1)) && (fitnessvektor(ruleset) > 0.0) )
                    index(candid) = ruleset
                    % maxfitness(ruleset-1) = 1.0; % verhindert, dass für index2 der gleiche Wert gewählt wird
                    
                    break;
                end
            end
            if ( a < maxvektor(1) )
                index(candid) = 1
            end
            
            fitnessvektor(index(candid)) = 0.0; % Fitness des gewählten Individuums auf 0 setzen
            fitnessvektor = fitnessvektor / sum(fitnessvektor); % neue Normierung
            
            cumul = 0;
            for regel=1:length(fitnessvektor)
                cumul = cumul + fitnessvektor(regel);
                maxvektor(regel) = cumul;
            end % neuen Vektor mit Maximalwerten für Roulette-Selection erzeugen
        end
    end

    function index = roulettemz(fitnessvektor, maxvektor)
        % Roulette-Wheel-Selection mit Zurücklegen - wird zur Auswahl der
        % Eltern-Individuen verwendet. Jedes Individuum soll mehrmals ausgewählt
        % werden können.
        
        for candid=1:elternzahl
            a = rand;
            % b = rand;
            
            for ruleset=length(fitnessvektor):-1:2
                if ( (a >= maxvektor(ruleset-1)) && (fitnessvektor(ruleset) > 0.0) )
                    index(candid) = ruleset
                    break;
                end
            end
            if ( a < maxvektor(1) )
                index(candid) = 1
            end
        end
    end

    function kinder = onepointxover(eltern)
        for xoverindex=1:uint8(elternzahl/2)
            string1 = wespe(eltern(xoverindex),1).classifierfitnessstring;
            string2 = wespe(eltern(xoverindex+1),1).classifierfitnessstring;
            % kopiert die Fitness-Strings der ausgewählten Eltern in Graycode
            % in zwei lokale String-Variablen
            % Es wird der Gesamtstring (Stärke aller Regeln
            % hintereinander) verwendet
            xoverpoint = uint16(rand * length(string1));
            % wählt einen zufälligen Crossoverpunkt
            
            if (rand <= probcrossover) % nur dann Crossover, wenn Zufallszahl
                % kleiner als
                % Crossover-Wahrscheinlichkeit ist
                neustring1 = [string1(1:xoverpoint) string2(xoverpoint+1:length(string2))];
                neustring2 = [string2(1:xoverpoint) string1(xoverpoint+1:length(string1))];
            else % sonst werden die Elternstrings einfach kopiert
                neustring1 = string1;
                neustring2 = string2;
            end
            
            kinder(xoverindex,:) = neustring1;
            kinder(xoverindex+1,:) = neustring2;
        end
    end

    function newbitstring = mutation(bitstring)
        for mutindex=1:uint8(elternzahl/2)
            bitstring1 = bitstring(mutindex,:);
            bitstring2 = bitstring(mutindex+1,:);
            
            if (rand < probmutation) % wenn Zufallszahl unter Mutationswahrscheinlichkeit
                mutbit = uint16(ceil(rand * length(bitstring1)));
                if strcmp(bitstring1(mutbit),'0')
                    bitstring1(mutbit) = '1';
                end
                if strcmp(bitstring1(mutbit),'1')
                    bitstring1(mutbit) = '0';
                end
            end % für bitstring1
            
            if (rand <= probmutation) % wenn Zufallszahl unter Mutationswahrscheinlichkeit
                mutbit = uint16(ceil(rand * length(bitstring2)));
                if strcmp(bitstring2(mutbit),'0')
                    bitstring2(mutbit) = '1';
                end
                if strcmp(bitstring2(mutbit),'1')
                    bitstring2(mutbit) = '0';
                end
            end % für bitstring2
            
            newbitstring(mutindex,:) = bitstring1;
            newbitstring(mutindex+1,:) = bitstring2;
        end
    end

end