Benutzer-Werkzeuge

Webseiten-Werkzeuge


ss19:finaler_code

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
ss19:finaler_code [2019/08/20 21:24]
leanderh
ss19:finaler_code [2019/08/20 23:57] (aktuell)
leanderh
Zeile 518: Zeile 518:
     return new_playerl     return new_playerl
 </​code>​ </​code>​
 +Ganz zum Schluss jedes Durchlaufs werden die Graphen für die Statistiken erstellt:
 +<​code>​
 +#Plotten der Graphen
 +b=a*2
  
 +plt.figure(b)
 +plt.subplot(221)
 +speed_statistic.plot(list_generation_number,​b)
 +plt.subplot(222)
 +size_statistic.plot(list_generation_number,​b)
 +plt.subplot(223)
 +sight_statistic.plot(list_generation_number,​b)
 +plt.subplot(224)
 +fat_statistic.plot(list_generation_number,​b)
 +
 +b+=1
 +plt.figure(b)
 +plt.subplot(221)
 +survived_steps_statistic.plot(list_generation_number,​b)
 +plt.subplot(222)
 +plt.plot(list_generation_number,​list_menge)
 +plt.subplot(223)
 +plt.plot(list_generation_number,​list_object_menge)
 +
 +a+=1
 +</​code>​\\
 +\\
 +Die Kreaturen, auch Player genannt, haben eine Vielzahl von Eigenschaften,​ die auch von der Version des Codes abhängt. Die wichtigsten sind sicher der Status (am Leben oder nicht), die Position (x und y), die Größe (angegeben als eine halbe Seitenlänge),​ die Sichtweite (sight), die Geschwindigkeit (Speed), die Ausdauer (Stamina) und der Winkel (angle). Darüber hinaus gibt es aber noch weitere Eigenschaften. Betrachte dafür die Initialisierungsfunktion für Spieler mit neuronalem Netz:
 +<​code>​
 +#​Initialisiert den Spieler
 +def __init__(self,​x,​y,​speed,​name):​
 +    self.x=x #x-Pos
 +    self.y=y #y-Pos
 +    self.size = 1 #Größe
 +    self.sight = 10 #Sichtweite
 +    self.fat = 0    #Die Dicke der Fettschicht der Kreaturen
 +    self.speed = 1 #​Geschwindigkeit
 +    self.inner_temprature = 0  #Die Innere Temperatur der Kreaturen
 +    self.skin_temprature = 0   #Die Hauttemperatur der Kreaturen
 +    self.status = True #Aktiv oder nicht
 +    self.chunkpos = [0,0] #Speichert die Position des Chunks
 +    self.stamina = START_STAMINA #Ausdauer
 +    self.angle = 0 #​Laufwinkel ​     ​
 +    self.anglechange = 45  ​
 +    self.rect=pygame.Rect(int(self.x)-int(self.size),​int(self.y)-int(self.size),​self.size*2,​self.size*2)#​Rechteck in Pygame ​       ​
 +    self.name=name #ID        ​
 +    self.vx = 0#??
 +    self.vy = 0
 +    self.status = True
 +    self.alive = True  #ist die Kreatur noch am Leben?
 +    self.viewpoint = 0
 +    self.survived_steps = 0  #Anzahl der überlebten Schritte
 +    self.sichtfeld = 0      #die Abdeckung des Sichtfeldes
 +    self.viewangle = 60    #Der Winkel des Sichtfeldes
 +    self.v = False
 +    self.world = None
 +    self.past = [[],​[],​[]] ​ #Die "​Erinnerung"​ der Kreatur an die letzten Schritte
 +    self.model = make_model()
 +    self.dist = []
 +    self.minangle = 0
 +    self.minspeed = 0
 +    self.maxangle = 0
 +    self.maxspeed = 0
 +    self.fit = 0
 +    self.color = (27,27,27)
 +    self.collected_food = 0   #​Menge des eingesammelten Futters
 +    self.total_stamina = 0
 +</​code>​
 +Alle Kreaturen haben die Mutation() Funktion. In dieser werden einige Eigenschaften festgelegt, bzw. verändert. Wenn die Kreaturen zur ersten Generation gehören, werden die Eigenschaften neu festgelegt:
 +<​code>​
 +if initial:
 +    self.speed = random.randint(1,​2)
 +    self.size ​ = random.randint(3,​6)
 +    self.sight = random.randint(10,​30)*self.size
 +    self.angle = random.randint(0,​360)
 +    self.fat ​  = random.randint(0,​max(int(self.size/​2),​1))
 +    self.memory_length = random.randint(0,​5)
 +</​code>​
 +Wenn die Kreaturen nicht zur ersten Generation gehören, sind sie ja Kopien von Kreaturen der vorherigen Generation. Es sind also auch diese Eigenschaften übernommen worden. Diese werden also nur leicht verändert:
 +<​code>​
 +else:
 +    self.speed = self.speed+random.choice(range(-9,​10))*0.01
 +    self.sight = self.sight+random.choice(range(-9,​10))*0.01*self.sight
 +    self.size ​ = self.size+random.choice(range(-9,​10))*0.02
 +    self.fat ​  = self.fat+random.choice(range(-9,​10))*0.01
 +    self.memory_length = self.memory_length+random.choice([-1,​0,​1])
 +    if self.fat <0:
 +        self.fat==0
 +    elif self.fat >​self.size/​2: ​ #​Fettschicht kann nicht dicker sein als ein viertel der Größe
 +        self.fat=self.fat/​2
 +</​code>​
 +Zum Schluss werden noch einige Eigenschaften festgelegt, die in jeder Generation neu gesetzt werden:
 +<​code>​
 +self.death=random.randint(1,​MAX_DEATH)
 +self.stamina = START_STAMINA+self.fat*self.size
 +self.max_stamina = self.stamina*STAMINA_KOEFFIZIENT
 +</​code>​
 +Nur die Kreaturen, deren Bewegung auf Basis von Polynomen abläuft, haben die Funktion movement_parameters(). Über diese werden die Polynome, auf denen ja die ganze Reaktion beruht, erstellt bzw. verändert, je nachdem ob die Kreatur aus der ersten Generation ist oder nicht. Dies geschieht über die Klasse der Polynome, auf die ich gleich noch eingehe. Außerdem wird noch die aktuelle Länge des jeweiligen Polynoms an die Kreatur zurückgegeben. ​
 +<​code>​
 + def movement_parameters(self,​initial=False):​
 +        ​
 +        if initial: ​ #wählt eine Länge l des Polynoms und erzeugt dieses
 +            l=random.randint(2,​10)
 +       #Die eingaben für die erstellung des Polynoms sind Länge, Minimale und Maximale Potenz, Minimale und Maximale Koeffizienten,​
 +       #​Minimale und Maximale Ausgabe des Polynoms sowie die Liste der Möglichen Eingaben in das Polynom und die aktuellen Länge des
 +       #​Gedächtnisses für evtl. tiefere Erinnerungspolynome
 +            self.poly_move=polynom(l,​0,​10,​0,​10,​0,​10,​["​m","​s"​],​self.memory_length)
 +            l=random.randint(2,​10)
 +            self.poly_angle=polynom(l,​0,​10,​0,​10,​0,​10,​["​m","​s"​],​self.memory_length)
 +            l=random.randint(2,​10)
 +            self.poly_input=polynom(l,​0,​10,​0,​10,​0,​10,​["​f","​u","​k"​],​0,​True)
 +        else:  #verändert das Polynom in Länge und dem Wert der Koeffizienten
 +            self.poly_move.change_poly
 +            self.poly_angle.change_poly
 +            self.poly_input.change_poly
 +                ​
 +        self.complexity_movement = self.poly_move.length
 +        self.complexity_angle = self.poly_angle.length
 +</​code>​
 +Hingegen alle Kreaturen haben die Funktionen looking_forward() und sightcheck(). Diese sind für das "nach vorne schauen"​ der Kreaturen. Diese geben also die Abdeckung des Sichtfeldes zurück. Aufgerufen werden diese Funktionen über die move() Funktion. Diese bewegt die Kreatur. Hier wird zunächst die Stamina der Kreatur geprüft. Liegt diese über dem maximalen Staminawert,​ den die Kreatur annehmen kann und der bei der Mutation festgelegt wird, so wird er auf diesen zurückgesetzt. Dann wird unter info eine Liste von allen Objekten gespeichert,​ die möglicherweise in Reichweite sind. Diese Liste wird dann in Looking_forward() eingegeben und unter sichtfeld wird dann zurückgegeben,​ wie sehr das Sichtfeld der Kreatur mit Futter bedeckt ist.
 +<​code>​
 +#bewegt den Spieler
 +def move(self):
 +    #​Überprüfe,​ ob die Maximale Stamina überschritten wird und verringere die Stamina, sollte dem so sein
 +    if self.stamina>​self.max_stamina:​
 +        self.stamina=self.max_stamina
 +
 +    angle_old = self.angle
 +    info = self.world.get_info(self)
 +
 +    if len(info)!=0:​
 +        self.looking_forward(info)
 +        a = self.sichtfeld
 +    else:
 +        a=0
 +
 +    if self.survived_steps>​self.death:​
 +            self.alive=False
 +</​code>​
 +Nun gibt es drei Möglichkeiten:​\\
 +1. Hat die Kreatur schon mehr Schritte überlebt als die Variable death, welche unter mutate() festgelegt wurde groß ist, so stirbt die Kreatur. Dies begrenzt die maximal mögliche Schrittzahl der Kreatur. ​
 +<​code>​
 +if self.survived_steps>​self.death:​
 +        self.alive=False
 +</​code>​
 +2. Ist dies nicht der Fall und hat die Kreatur genügend Stamina für den nächsten Schritt, so wird dieser durchgeführt. Dieser Teil ist wieder von der Version abhängig. Im Falle neuronaler Netze wird eine Liste von Werten (inputlist),​ bestehend aus dem sichtfeld und der Erinnerung der Kreatur erstellt und durch das neuronale Netz geprüft. Dieses gibt dann die entsprechenden Reaktionen für die Veränderung des Winkels und der Schrittweite zurück.
 +<​code>​
 +elif self.stamina >= self.speed+self.size**2+self.sight*0.25:​
 +
 +    inputlist = []
 +    inputlist.append(a)
 +    if len(self.past[0])>​4:​
 +        for i in self.past[0][-5:​]:​
 +            inputlist.append(1/​(i+1))
 +        for i in self.past[1][-5:​]:​
 +            inputlist.append(1/​(i+1))
 +        for i in self.past[2][-5:​]:​
 +            inputlist.append(1/​(i+1))
 +    else:
 +        for i in range(15):
 +            inputlist.append(0)
 +    #​print(len(inputlist))
 +    #​print(inputlist)
 +    #​print(len(inputlist))
 +    #​print(inputlist)
 +    new_angle , new_speed = self.model.calc(inputlist)
 +</​code>​
 +Dann wird diese neue Veränderung zum Gedächtnis hinzugefügt und die Veränderung des x und y Wertes der Kreatur errechnet. ​
 +<​code>​
 +self.angle += new_angle
 +            self.angle = self.angle%360
 +            self.speed += new_speed
 +            self.speed = min(max(0, self.speed),​3)
 +            self.past[0].append(self.angle/​360)
 +            self.past[1].append(self.speed/​10)
 +            self.past[2].append(a/​300)
 +
 +            ​
 +            self.x+=self.speed*math.cos(self.angle/​180*math.pi)
 +            self.y+=self.speed*math.sin(self.angle/​180*math.pi)
 +            self.rect=pygame.Rect(int(self.x)-int(self.size),​int(self.y)-int(self.size),​self.size*2,​self.size*2)
 +</​code>​
 +Zum Schluss wird der durch den Schritt erlittene Energieverlust errechnet. Dieser besteht aus einem Grundverlust,​ den jede Kreatur jede Runde erleidet (MIN_STAMINA_LOSS_PER_STEP),​ dem Verlust durch die Temperatur der Umgebung, welche sich aus dem gesamten Umfang der Kreatur multipliziert mit dem Temperaturunterschied zwischen der Hauttemperatur der Kreatur und der Umgebungstemperatur errechnet und dem Verlust an Energie durch die Eigenschaften der Kreatur.
 +<​code>​
 +## Es ist wichtig, eine Modellierungs zu machen, die nicht
 +## schon ein eindeutiges Evolutionsergebnis nahelegt.
 +## Schneller zu gehen, sollte mehr Energie kosten.
 +## Ein größeres Areal abzufressen sollte eine Energie ​
 +## kosten, die proportional zum Areal ist.        ​
 +self.skin_temprature=self.inner_temprature-self.fat
 +
 +#Der Energieverlust durch Temperatur ergibt sich aus dem Umfang, also dem freiligenden Teil des Körpers
 +energy_loss_through_temprature=(self.skin_temprature-TEMPRATURE)*8*self.size  ​
 +
 +if energy_loss_through_temprature<​0:​
 +    energy_loss_through_temprature=0
 +
 +#Der Stamina-verlust an sich:
 +
 +self.stamina -=  MIN_STAMINA_LOSS_PER_STEP+0.5*(abs(self.speed)**2+self.size)
 +self.survived_steps += 1 
 +#​self.stamina -= 1*(5+energy_loss_through_temprature+0.1*(self.speed+0.5*self.size**2+self.sight*0.2) )
 +self.total_stamina += self.stamina
 +#​self.survived_steps += 1 
 +</​code>​
 +3. Hat die Kreatur nicht genügend Energie, so stirbt sie. 
 +<​code>​
 +else:
 +        self.fit = self.total_stamina #​self.survived_steps ​
 +        self.alive = False
 +</​code>​
 +Kreaturen mit Polynomen haben nun noch die Klasse Polynom. Jedes Polynom hat einige Eigenschaften. ​
 +<​code>​
 +length=0 ​    #​Startlänge des Polynoms
 +poly=[] ​     #Das Polynom an sich
 +min_potenz=0 #minimale Startpotenz
 +max_potenz=0 #maximale Startpotenz
 +min_koeffizient=0 ​ #min Startkoeffizient
 +max_koeffizient=0 ​ #max Startkoeffizient
 +min_output=0 ​ #min Ausgabe
 +max_output=0 ​ #max Ausgabe
 +</​code>​
 +Darüber hinaus hat jedes Polynom noch eine Liste (poly), in welchem das Polynom an sich gespeichert ist. Wird ein Polynom Initialisiert,​ so wird es auch direkt erstellt. Dafür werden der Länge des Polynoms entsprechend viele Monome bzw. Listen erstellt. In diesen ist dann jeweils der Koeffizient (a), die Potenz(b) und die Art des Inputs in das Monom (c) gespeichert. Letzteres könnte eine Zahl oder eine Liste sein. Liegt eine Liste vor, so nennen wir das ein Erinnerungspolynom,​ weil hier die Erinnerung der Kreatur eingesetzt wird. In diesem Fall wird für dieses Erinnerungspolynom als eigenes Polynom in dem Polynom erstellt. Dieses bezeichnen wir auch als Deep Poly und markieren das entsprechend durch die Variable deep_poly. Dadurch wird gekennzeichnet,​ dass in diesem Polynom keine weiteren Polynome stecken.
 +<​code>​
 +def __init__(self,​l,​minp,​maxp,​mink,​maxk,​mino,​maxo,​dif_monos,​memory_length,​deep_poly=False):​
 +    self.length=l
 +    self.min_potenz=minp
 +    self.max_potenz=maxp
 +    self.min_koeffizient=mink
 +    self.max_koeffizient=maxk
 +    self.min_output=mino
 +    self.max_output=maxo
 +    self.poly=[]
 +    self.deep_poly=deep_poly ​ #ist das Polynom ein tiefes Polynom, also ein Polynom zur einbindung der Erinnerung
 +
 +    n=0
 +    while n<​self.length: ​ #erstellen des Polynoms
 +        a=random.choice(range(self.min_koeffizient,​self.max_koeffizient+1))
 +        b=random.choice(range(self.min_potenz,​self.max_potenz+1))
 +        c=random.choice(dif_monos) #c legt die Art des Inputs fest mit hier mr=Erinnerung der Schrittweite,​ ma=Erinnerung der Winkeländerung und mf=Erinnerung der Futterabdeckung
 +        if self.deep_poly==False and c=="​m": ​ #Für Erinnerungspolynome
 +            new_poly=polynom(memory_length,​0,​10,​0,​10,​0,​1,​["​mf","​mr","​ma"​],​0,​True) #​Erinnerungspolynom wird erstellt
 +            self.poly.append([a,​b,​c,​new_poly])
 +        else:
 +            self.poly.append([a,​b,​c,​[]])
 +        n+=1
 +</​code>​
 +Jedes Polynom hat die Möglichkeit,​ sich zu verändern, dies geschieht durch die Funktion change_poly(). Diese wird immer dann genutzt, wenn eine neue Generation gebildet wird. Hierbei wird zunächst evtl. die Länge des Polynoms geändert. Ist dies der Fall wird, wenn nötig, eine entsprechende Anzahl an Monomen hinzugefügt und das Polynom neu gemischt.  ​
 +<​code>​
 +def change_poly(self,​memory_length): ​ #Änderung, also Mutation des Polynoms
 +    n=0
 +    self.A=[]
 +    if self.length<​2:​
 +        self.length=random.choice(range(0,​2))+self.length
 +    else:
 +        self.length=random.choice(range(-1,​2))+self.length
 +    while self.length>​len(self.poly):​
 +        a=random.choice(range(self.min_koeffizient,​self.max_koeffizient+1))
 +        b=random.choice(range(self.min_potenz,​self.max_potenz+1))
 +        c=random.choice(dif_monos_list) #c legt die Art des Inputs fest mit hier mr=Erinnerung der Schrittweite,​ ma=Erinnerung der Winkeländerung und mf=Erinnerung der Futterabdeckung
 +        if self.deep_poly==False and c==1:  #Für Erinnerungspolynome
 +            new_poly=polynom(memory_length,​0,​10,​0,​10,​0,​1,​3,​0,​True) #​Erinnerungspolynom wird erstellt
 +            self.poly.append([a,​b,​c,​new_poly])
 +        else:
 +            self.poly.append([a,​b,​c,​[]])
 +    random.shuffle(self.poly)
 +</​code>​
 +Danach wird so lange ein Monom aus der Liste genommen und vom Koeffizienten und der Potenz her leicht verändert und dem neuen Polynom hinzugefügt,​ bis die angestrebte Länge des Polynoms erreicht ist. dieses neue Polynom wird dann zu Stammpolynom.
 +<​code>​
 +while n<​self.length:​
 +    mutation=random.choice(range(0,​MUTATIONSWAHRSCHEINLICHKEIT_VERHALTEN))
 +    if mutation==0:​
 +        a=random.choice(range(-5,​6))
 +        b=random.choice(range(-9,​10))
 +        aneu=(self.poly[n][0])+0.01*a
 +        bneu=(self.poly[n][1])+0.01*b
 +        self.A.append([aneu,​bneu,​self.poly[n][2],​self.poly[n][3]])
 +    else:
 +        self.A.append(self.poly[n])
 +
 +    n+=1  ​
 +self.poly=self.A
 +</​code>​
 +Zum Schluss gibt es noch zwei Reaktionsfunktionen,​ eine für Polynome mit möglichen Unterpolynomen (reaction()) und eine für Polynome definitiv ohne Unterpolynome (Deep_reactio()). \\
 +Für reaction() werden die Eingaben Sichtfeld(s) und die Liste der Erinnerungen (Memory) benötigt. Es wird die Liste der Monome durchgegangen. Je nach Wert von c wird entweder s eingesetzt oder es wird eine Deep_reaction() mit der Liste der Erinnerungen ausgelöst.
 +<​code>​
 +def reaction(self,​s,​memory): ​ #Reaktion der Kreatur auf einen bestimmten Umstand für das normale Polynom
 +    c=0
 +
 +    for el in self.poly:
 +
 +        if el[0]<0:
 +            a=random.randint(0,​10)
 +        else:
 +            a=el[0]
 +
 +        if el[1]<0:
 +            b=random.randint(0,​10)
 +        else:
 +            b=el[1]
 +
 +        if el[2]=="​s":​
 +            x=s
 +        elif el[2]=="​m":​
 +            x=el[3].deep_reaction(memory)
 +</​code>​
 +Es wird dann der Wert von c an den bei der Initialisierung angegebenen Maximalwert des Outputs angepasst und c ausgegeben.
 +<​code>​
 +    c=c+a*x**b
 +#​c=c/​len(poly)
 +while c>​self.max_output:​
 +    c=self.max_output-(c-self.max_output)
 +while c<​self.min_output:​
 +    c=c+(self.min_output-c)
 +
 +return c    ​
 +</​code>​
 +Die Deep_reaction() läuft ähnlich ab, es werden aber nicht mehr Monome betrachtet als die Erinnerung lang ist. Von dem Wert c hängt es ab, welcher Teil der Erinnerung betrachtet wird: Die Veränderung des Winkels, die Schrittweite oder die Eingabe. ​
 +<​code>​
 +def deep_reaction(self,​l): ​ #Reaktion für erinnerungspolynome
 +    if self.deep_poly:​
 +        c=0
 +        N=min(len(self.poly),​len(l))
 +        n=0
 +
 +        while n<N:
 +
 +            if self.poly[n][0]<​0:​
 +                a=random.randint(0,​10)
 +            else:
 +                a=self.poly[n][0]
 +
 +            if self.poly[n][1]<​0:​
 +                b=random.randint(0,​10)
 +            else:
 +                b=self.poly[n][1]
 +
 +            if self.poly[n][2]=="​mr":​
 +                x=l[n][0]
 +
 +            elif self.poly[n][2]=="​ma":​
 +                x=l[n][1] ​   ​
 +
 +            elif self.poly[n][2]=="​mf":​
 +                x=l[n][2]
 +
 +            c=c+a*x**b
 +            n+=1
 +        #​c=c/​len(poly)
 +        while c>​self.max_output:​
 +            c=self.max_output-(c-self.max_output)
 +        while c<​self.min_output:​
 +            c=c+(self.min_output-c)
 +
 +        return c    ​
 +
 +    else:
 +        return 0
 +</​code>​
 +Zum Schluss gibt es noch die Funktion show_poly() welche dazu da ist, zum Schluss des map codes, wenn das Polynom der am längsten überlebenden Kreatur betrachtet werden soll, das ganze Polynom mit all seien unterpolynomen zu zeigen.
 +<​code>​
 +def show(self): ​ #zeigt das Polynom als ganzes, mit allen tieferen Polynomen
 +    self.A = []
 +    for p in self.poly:
 +        if p[2]=="​s":​
 +            self.A.append(p)
 +        if p[2]=="​m":​
 +            deep=p[3].poly
 +            self.A.append([p[0],​p[1],​p[2],​deep])
 +</​code>​
  
  
ss19/finaler_code.1566329059.txt.gz · Zuletzt geändert: 2019/08/20 21:24 von leanderh