\begin{matlab}
classdef bauagent < handle
%BAUAGENT Diese Klasse definiert die Eigenschaften des einzelnen Agenten
%   Hier werden die Eigenschaften festgelegt, die den einzelnen Agenten
%   ausmachen, der für den Bau der Wabenstruktur zuständig ist.
%   Für jeden einzelnen Agenten wird ein Objekt dieser Klasse erzeugt.

properties
Position % Vektor, welcher die Position des Agenten in
% X-Y(-Z)-Koordinaten enthält. Je nach Umgebung
% zwei- oder dreidimensional.
Ausrichtung % Blickrichtung des Agenten im Gradmaß (0 bis 360)
% Null entspricht Blick waagerecht nach rechts
RuleSet1
% Enthält die Ausgangsregeln, nach denen gebaut oder nicht
% gebaut wird. Hier werden nur Regeln festgelegt, mit
% denen begonnen wird, danach werden die Regeln in das
% Array \texttt{classifierset} kopiert und nur noch von dort
% ausgelesen und auch durch den GA verändert.
% Könnte auch aus Datei gelesen werden...
RuleSet2 % Regelsatz der zweiten Hierarchieebene
BuildCounter % Zählt die Anzahl der gesetzten Bausteine.
Abstand % Ein Abstandsvektor, der die absoluten Abstandswerte zu
% allen Zellenmittelpunkten enthält
Speed = 1; % relative Geschwindigkeit, mit der sich der Agent bewegt

Markierung % graphisches Objekt zur Positionsmarkierung
Index % enthält die laufende Nummer des Agenten
Farbe % die Farbe zur Markierung
classifierset % ein Array, in dem die Objekte vom Typ \texttt{classifier}
% abgelegt werden
numberofclassifiers % Anzahl der vorhandenen Regeln
matchset % Array in dem die Indizes aller passenden Klassifikatoren für einen Zeitschritt abgelegt werden
% - wird nach jedem Zeitschritt gelöscht
bucket % enthält den Index-Wert des letzten benutzten Klassifikators
% wird für den bucket-brigade-Algorithmus benötigt
classifierfitnessstring % enthält die Fitnessstrings aller Regeln im Gray code hintereinander
end

methods
% Konstruktor
function obj = bauagent(InitialPosition, indexNummer) 
global Grenzen;
global ring;

obj.Ausrichtung = 0;

% Definition erster Regelsatz
obj.RuleSet1 = ['BBBBBB1'; 'BBBBB01'; '0BBBBB1'; ...
                'B0BBBB1'; 'BB0BBB1'; 'BBB0BB1'; ...
		'BBBB0B1'; 'BBBB001'; '0BBBB01'; ...
		'00BBBB1'; 'B00BBB1'; 'BB00BB1'; ...
		'BBB00B1'; 'BBB0001'; '0BBB001'; ...
		'00BBB01'; '000BBB1'; 'B000BB1'; ...
		'BB000B1'; 'BB00001'; '0BB0001'; ...
     '00BB001'; '000BB01'; '0000BB1'; 'B0000B1'; ...
		'B000000'; '0B00000'; '00B0000'; ...
		'000B000'; '0000B00'; '00000B0'];

% Definition zweiter Regelsatz
obj.RuleSet2 = ['1100002'; '0110002'; '0011002'; ...
		'0001102'; '0000112'; '1000012'; ...
		'2110002'; '0211002'; '0021102'; ...
		'0002112'; '1000212'; '1100022'; ...
		'1120002'; '0112002'; '0011202'; ...
		'0001122'; '2000112'; '1200012'; ...
		'2112002'; '0211202'; '0021122'; ...
		'2002112'; '1200212'; '1120022'; ...
		'2120002'; '0212002'; '0021202'; ...
		'0002122'; '2000212'; '1200022'; ...
		'1200002'; '0120002'; '0012002'; ...
		'0001202'; '0000122'; '2000012'; ...
		'2100002'; '0210002'; '0021002'; ...
		'0002102'; '0000212'; '1000022'];

if (ring == 1) % falls sich die Simulation in der ersten Hierarchieebene befindet, wird der erste Regelsatz aktiviert...
 obj.updateclassifierset(obj.RuleSet1);
elseif (ring == 2) % ...sonst der zweite
 obj.updateclassifierset(obj.RuleSet2);
end

if nargin > 0
obj.Index = indexNummer; % übergibt den Index aus dem Vektor von Agenten als Eigenschaft
else
obj.Index = 0;
end

if (mod(obj.Index, 7) == 1)
obj.Farbe = 'blue';
elseif (mod(obj.Index, 7) == 2)
obj.Farbe = 'cyan';
elseif (mod(obj.Index, 7) == 3)
obj.Farbe = 'green';
elseif (mod(obj.Index, 7) == 4)
obj.Farbe = 'white';
elseif (mod(obj.Index, 7) == 5)
obj.Farbe = 'black';
elseif (mod(obj.Index, 7) == 6)
obj.Farbe = 'yellow';
else
obj.Farbe = 'magenta';
end

% Anzahl der gebauten Bausteine auf Null
obj.BuildCounter = 0;

if nargin > 0 % falls Positionswert übergeben wird, wird dieser benutzt..
obj.Position = InitialPosition;
else % ...ansonsten wird die Position auf 0,0 gesetzt
obj.Position(1,1) = 0;
obj.Position(1,2) = 0;
end

obj.Markierung = line(obj.Position(1), obj.Position(2), ...
1.1, 'Marker','.', 'MarkerFaceColor',obj.Farbe, ...
'Color',obj.Farbe );
 % aktuelle Position des Agenten wird graphisch markiert
end

function movebuild(agent) 
 % Diese Funktion wird einmal pro Zeitschritt für jeden Agenten aufgerufen
 % Die Umgebung wird verarbeitet und nach den Regeln des Agenten
 % entschieden, ob der Agent an der aktuellen Position eine Wabe baut
 % oder sich weiterbewegt.
global Wabengitter;
global Knotenzahl;
global Grenzen;
global raumplot;
global bebautezellen;
global animation;
global gadurchlauf;
global numberofgaruns;

% matchset muss gelöscht werden (empty matrix)
agent.matchset = [];

% dreht den Agenten in eine zufällige Richtung
agent.Ausrichtung = mod(agent.Ausrichtung + rand*360, 360);

% Index der Zelle, auf dessen Fläche sich der Agent befindet
Zelle = agent.indexPosition;
% Erzeugt in Nachbarn eine Liste der Indizes 
% der den Agenten unmittelbar umgebenden Zellen
Nachbarn = agent.nachbarn(Zelle); 

for nachbarZelle=1:6
builtFlag = 0;
validFlag = 1; % Initialisierung

bauPosition = Nachbarn(nachbarZelle);
 % setze potentielle Position für neuen Baustein auf einen
 % der sechs Nachbarzellen der aktuellen Position

if ( (bauPosition < 1) || (bauPosition > Knotenzahl) || ...
 % Grenzen des Gitters
 (Wabengitter(4,bauPosition) ~= 0) || ...
 % Bebauung der Zelle
 (Wabengitter(3,bauPosition) == 0) )
 % und ist die Wabe noch im Gitter?
 validFlag = 0;

end

 % erzeugt Liste der unmittelbaren Nachbarn der potentiellen Bauposition
bauNachbarn = agent.nachbarn(bauPosition); 
 % jeder Klassifikator wird auf Validität geprüft
for ruleNumber=1:agent.numberofclassifiers 
 % wenn sich die Position außerhalb des
 % Gitters befindet oder schon bebaut ist,
 % wird die nächste Position geprüft
 if (validFlag == 0)
 break; 
end

validFlag = 1;
 % jede Position der Regel wrd einzeln geprüft
for ruleCellNumber=1:6
if ( (validFlag == 0) || ...
(bauNachbarn(ruleCellNumber) > Knotenzahl) || ...
(bauNachbarn(ruleCellNumber) < 1) )
break; 
 % Wenn schon die letzte Stelle der Regel ungültig war,
 % braucht diese nicht mehr getestet zu werden.
 % Wenn die Position außerhalb des Gitters liegt, auch nicht
end

if ...
strcmp(agent.classifierset(ruleNumber). ...
       detector(ruleCellNumber),'B')
if ( Wabengitter(4,bauNachbarn(ruleCellNumber)) == 1 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 2 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 3 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 4 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 5 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 6 ...
  || Wabengitter(4,bauNachbarn(ruleCellNumber)) == 7 )
  validFlag = 1; 
  % wenn die Regel an dieser Stelle 'B' enthält,
  % wird validFlag 'true' gesetzt, falls ein
  % beliebiger Baustein (1-7) vorhanden ist.
else
 validFlag = 0;
end
elseif ...
strcmp(agent.classifierset(ruleNumber). ...
       detector(ruleCellNumber),'*')
validFlag = 1; 
 % enthält die Regel an der betreffenden Stelle ein '*'
 % so ist der Zustand der Zelle egal.
else
validFlag = ...
strcmp(num2str(Wabengitter(4,bauNachbarn(ruleCellNumber))), ...
 agent.classifierset(ruleNumber).detector(ruleCellNumber));
 % in allen anderen Fällen (genaue
 % Spezifikation in der Regel) muss
 % der Zelleninhalt exakt mit der
 % Regelposition übereinstimmen.
end

end

if (validFlag == 1)

agent.classifierset(ruleNumber).bid = ...
 % Rückkonvertierung von Gray
gray2bin(agent.classifierset(ruleNumber). ...
 strength,'fsk',256) ...
 * log2(agent.classifierset(ruleNumber).specificity + 1);
 % bid-Wert wird berechnet:
 % bid(r) = strength(r) * log2(specificity(r))

agent.matchset(length(agent.matchset)+1) = ruleNumber;
 % wenn die Bedingung des Klassifikators gültig ist,
 % wird seine Indexnummer in matchset übernommen

end

validFlag = 1; % sonst wird die nächste Regel nicht angewendet
end
 % An dieser Position sind alle Regeln ausgewertet und \texttt{matchset} sollte alle passenden Regeln enthalten


overallbid = 0;
cumulativebid = 0;
chosenrule = 0;

if ~isempty(agent.matchset)
% bei leerem \texttt{matchset} muss nichts ausgewählt werden
for clsf=1:length(agent.matchset)
overallbid = ...
 overallbid + agent.classifierset(agent.matchset(clsf)).bid;
end

for clsf=1:length(agent.matchset)
normalbid = ...
 agent.classifierset(agent.matchset(clsf)).bid / ...
  overallbid;
% Normalisierung des \texttt{bid}-Wertes auf Wert zwischen 0 und 1
% (Anteil an der Summe aller \texttt{bid}-Werte aller Regeln)
agent.classifierset(agent.matchset(clsf)).minfit = ...
 cumulativebid;
cumulativebid = cumulativebid + normalbid;
agent.classifierset(agent.matchset(clsf)).maxfit = ...
 cumulativebid;
% Unter- und Obergrenzen werden gesetzt
end

decision = rand;
for clsf=1:length(agent.matchset)
if ...
(decision>=agent.classifierset(agent.matchset(clsf)).minfit...
&&decision<=agent.classifierset(agent.matchset(clsf)).maxfit)
% Hier wird die zu verwendende Regel ausgewählt und ihr Index in \texttt{chosenrule} abgespeichert.
% workaround, um zu verhindern, dass jede regel unbedingt ausgewählt wird
if ( (rand * 255) <= agent.classifierset(agent.matchset(clsf)).strength )
    chosenrule = agent.matchset(clsf);
end
break;
end
end 
end

% Wenn eine Regel \texttt{chosenrule} ausgewählt wurde, muss nach
% dieser Regel gebaut werden:
if (chosenrule)
Wabengitter(4,bauPosition) = ...
 agent.classifierset(chosenrule).effector;
% *hier wird gebaut*

agent.bucket = chosenrule;
% benutzte Regel wird für (nicht implementierten) BBA zwischengespeichert

agent.BuildCounter = agent.BuildCounter + 1;
% Counter erhöhen

builtFlag = 1; % zeigt an, dass in diesem Zeitschritt gebaut wurde

if (gadurchlauf == numberofgaruns)
plotwabe(bauPosition, raumplot, Wabengitter(4,bauPosition));
% die gebaute Wabe wird graphisch markiert
% Parameter:
% bauPosition = Index der zu bebauenden
% Zelle
% raumplot = Handle des zu benutzenden
% Achsenobjekts
% Wabengitter(...) = Typ des Bausteins
% (definiert die Farbe)
end
% Update der Liste der Indizes aller bebauten Zellen
bebautezellen = find(Wabengitter(4,:));
end

if true(builtFlag)
builtFlag = 0;
break;
% Jeder Agent soll höchstens einmal pro Zeitschritt bauen.
end
end

% Bewegung in X-Richtung
agent.Position(1) = ...
 agent.Position(1) + cosd(agent.Ausrichtung * agent.Speed);
% Bewegung in Y-Richtung
agent.Position(2) = ...
 agent.Position(2) + sind(agent.Ausrichtung * agent.Speed);

zelle = agent.indexPosition;
% wenn Position unbebaut ist...
if ( (Wabengitter(4,zelle) == 0) || ...
(agent.Position(1) > Grenzen(1)) || ...
(agent.Position(1) < 0) ||...
(agent.Position(2) > Grenzen(2)) || ...
(agent.Position(2) < 0) ) 
% .. oder außerhalb des Gitters liegt
% ...dann mache Positionsänderung rückgängig
agent.Position(1) = agent.Position(1) + ...
cosd(mod((agent.Ausrichtung+180),360)*agent.Speed);
agent.Position(2) = agent.Position(2) + ...
sind(mod((agent.Ausrichtung+180),360)*agent.Speed);
end

if (gadurchlauf == numberofgaruns)
agent.Markierung=line(agent.Position(1),agent.Position(2), ...
 1.1, 'Marker','.', 'MarkerFaceColor',agent.Farbe, ...
 'Color',agent.Farbe, 'Parent',raumplot );
 % neue Markierung des Agenten wird gesetzt
end

if (animation)
 drawnow;
end 
% zeigt die animierte Bausequenz, wenn animation-flag gesetzt
end

function indexnummer = indexPosition(agent)
% Gibt für jeden Agenten den Index des Mittelpunkts derjenigen
% Wabe zurück, auf deren Fläche sich der Agent nach seinen
% Koordinaten befindet
global Wabengitter;
global MittelPunkte;

agent.Abstand = ...
 [abs(Wabengitter(1,MittelPunkte) - agent.Position(1)); ...
  abs(Wabengitter(2,MittelPunkte) - agent.Position(2))];
kandidatenX = find(agent.Abstand(1,:) <= 1);
kandidatenY = find(agent.Abstand(2,:) <= 1);

indexnummer = 0;
for kandX=1:length(kandidatenX)
for kandY=1:length(kandidatenY)
if (kandidatenX(kandX) == kandidatenY(kandY))
indexnummer = MittelPunkte(kandidatenX(kandX));
break;
end
end
if (indexnummer ~= 0)
break;
end
end

% Der folgende Code ist exakter, aber auch langsamer und wird nur verwendet,
% wenn der andere zu keinem Ergebnis kommt
%
if indexnummer == 0
agent.Abstand = sqrt((Wabengitter(1,MittelPunkte) - ...
 (agent.Position(1,1))).^2 + (Wabengitter(2,MittelPunkte) ...
 - (agent.Position(1,2))).^2 );
% erzeugt einen Vektor mit den euklidischen Abständen des Agenten
% von allen Wabenmittelpunkten:
% $D(X,Y) = sqrt{ (x1-y1)^{2} + (x2-y2)^{2} }$

[abstand, I] = min(agent.Abstand); 
% Gibt den Abstand zum nächstgelegenen
% Wabenmittelpunkt (abstand) und den dazugehörigen
% Index im Vektor Abstand (I) zurück
indexnummer = MittelPunkte(I); 
% Der nächste Mittelpunkt ist der
% mit Index I im Vektor Abstand, also ist sein
% Index im Array Wabengitter der Wert an Stelle i
% im Vektor Abstand
end
end

function updatefitness(agent)
% Kopiert den Gesamt-Fitnesstring in die Stärkewerte der
% einzelnen Regeln.
for regelindex=1:agent.numberofclassifiers
agent.classifierset(regelindex).fitnessstring = ...
agent.classifierfitnessstring(((regelindex-1)*8)+1: ...
 ((regelindex-1)*8)+8);
graystrength = ...
 bin2dec(agent.classifierset(regelindex).fitnessstring);
% Konvertierung von Binärstring in Dezimalzahl(Gray-codiert)
agent.classifierset(regelindex).strength = ...
 gray2bin(graystrength,'fsk',256);
% konvertiert von Gray nach Standard-Binär und speichert im \texttt{strength}-Feld
end
end

function updateclassifierset(agent, ruleset)
% Erzeugt aus den Regeldefinitionen in \texttt{ruleset} einen neuen
% Regelsatz für den Agenten und speichert diesen in
% \texttt{classifierset} ab. Wird beim Erzeugen des Agenten aufgerufen
% und danach bei jedem Wechsel in eine neue Hierarchieebene
% zum Wechsel zum neuen Regelsatz.
for rule=1:size(ruleset,1)
classifiers(rule) = ...
 classifier(ruleset(rule,:),uint8(rand*255));
end 
% Erzeugung des Regelsatzes - 
% Für jede Regel eine Zufallszahl zwischen
% 0 und 255, um mit 8-bit-Gray-Code arbeiten zu können

agent.classifierset = classifiers;
agent.numberofclassifiers = length(agent.classifierset);
agent.classifierfitnessstring = ...
 strcat(agent.classifierset.fitnessstring);
end

end

end
\end{matlab}
