1
0
mirror of https://github.com/fazo96/AIrium.git synced 2025-01-09 09:29:53 +01:00

performance improvements and some javadocs

This commit is contained in:
Enrico Fasoli 2015-07-21 11:04:46 +02:00
parent abb3c96cdf
commit 70089e4379
7 changed files with 186 additions and 75 deletions

View File

@ -8,7 +8,7 @@ import java.util.logging.Logger;
import logic.neural.Brain;
/**
* A (hopefully) smart biological creature.
* A (hopefully) smart biological creature in the simulated world.
*
* @author fazo
*/
@ -24,6 +24,12 @@ public class Creature extends Element implements Runnable {
private Sight[] sights;
private Thread workerThread;
/**
* Create a creature with a random mind at given position in space
*
* @param x
* @param y
*/
public Creature(float x, float y) {
super(x, y, default_radius);
dir = (float) (Math.random() * 2 * Math.PI);
@ -157,13 +163,14 @@ public class Creature extends Element implements Runnable {
@Override
public void render(ShapeRenderer s) {
// Body
// Draw Body
s.setColor(1 - (hp / max_hp), hp / max_hp, 0, 1);
s.circle(getX(), getY(), getSize());
// Vision
// Prepare vision stuff
double relX = Math.cos(dir), relY = Math.sin(dir);
float c = 0;
float eyeX = (float) (relX * getSize() * 0.6f), eyeY = (float) (relY * getSize() * 0.6f);
// Draw Sight Lines
if (Game.get().getWorld().getOptions().getOrDefault("draw_sight_lines", 0f) > 0) {
for (Sight sight : sights) {
if (sight != null) {
@ -180,19 +187,21 @@ public class Creature extends Element implements Runnable {
}
}
}
// Draw eye
if (sights[0] == null && sights[1] == null) {
s.setColor(1, 1, 1, 1);
} else {
s.setColor(sights[1] == null ? 0 : 1, sights[0] == null ? 0 : 1, 0, 1);
}
s.circle(getX() + eyeX, getY() + eyeY, 3);
//FOV
// Draw FOV cone
float degrees = fov * 360f / (float) Math.PI;
float orient = dir * 180f / (float) Math.PI - degrees / 2;
if (Game.get().getWorld().getOptions().getOrDefault("draw_view_cones", 0f) > 0) {
s.setColor(0.3f, 0.3f, 0.3f, 1);
s.arc((float) eyeX + getX(), (float) eyeY + getY(), sightRange, orient, degrees);
}
// Draw damage/heal marks
if (hp < prevHp) {
// Damage mark
s.set(ShapeRenderer.ShapeType.Filled);
@ -205,11 +214,17 @@ public class Creature extends Element implements Runnable {
s.circle(getX(), getY(), 5);
}
s.set(ShapeRenderer.ShapeType.Line);
// Beak
// Draw Beak
s.setColor(beak / max_beak, 1 - beak / max_beak, 0, 1);
s.line((float) (relX * getSize() * 0.8f + getX()), (float) (relY * getSize() * 0.8f + getY()), (float) (relX * getSize() * (1.5f + beak / max_beak) + getX()), (float) (relY * getSize() * (1.5f + beak / max_beak) + getY()));
}
/**
* Store Sight information (what the creature sees) and eat/attack if
* applicable
*
* @return the sight information retrieved
*/
public Sight[] interactWithWorld() {
Sight[] newSights = new Sight[2];
// Try to see plant
@ -299,28 +314,21 @@ public class Creature extends Element implements Runnable {
return newSights;
}
public void eat() {
eating = false;
for (Element e : Game.get().getWorld().getPlants()) {
if (overlaps(e)) {
eating = true;
e.setSize(e.getSize() - 0.1f);
if (e.getSize() == 0) {
e.setSize(0);
}
hp++;
fitness++;
if (hp > max_hp) {
hp = max_hp;
}
}
}
}
/**
* Apply a modification to this creature's health. Can be negative.
*
* @param amount how much to heal/damage
*/
private void heal(float amount) {
hp += amount;
}
/**
* Praise this creature by increasing fitness. Can be negative to decrease
* fitness
*
* @param amount how much
*/
private void praise(float amount) {
fitness += amount;
}
@ -340,9 +348,11 @@ public class Creature extends Element implements Runnable {
public void startWorker() {
workerDone = false;
if (workerThread == null) {
// Create a new thread
workerThread = new Thread(this);
workerThread.start();
} else {
// Interrupt current thread, throwing it out of sleep
workerThread.interrupt();
}
}

View File

@ -1,13 +1,9 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
/**
* Represents a dynamic simulation element with a circular shape
*
* @author fazo
*/
@ -15,35 +11,89 @@ public abstract class Element {
private float x, y, size;
/**
* Create an element at given position with given radius. Elements have a
* circular shape.
*
* @param x the x position
* @param y the y position
* @param size the element body radius
*/
public Element(float x, float y, float size) {
this.x = x;
this.y = y;
this.size = size;
}
/**
* Calculate the distance between the circumference of this element and
* another one's, taking into account the size. This means that two elements
* whose circumference is touching will have a distance of 0, and two
* overlapping elements will have a negative distance
*
* @param e the element whose distance from this one will be checked
* @return the distance from the element. It's 0 if they are just touching,
* negative if they are colliding and positive if they are not
*/
public float distanceFrom(Element e) {
return (float) Math.sqrt(Math.pow(e.x - x, 2) + Math.pow(e.y - y, 2)) - getSize() - e.getSize();
}
/**
* Checks if this element overlaps another one, causing a collision
*
* @param e the element which position will be checked to see if it overlaps
* this one
* @return true if the elements are overlapping, causing a collision
*/
public boolean overlaps(Element e) {
return distanceFrom(e) < 0;
}
/**
* Checks if this element overlaps a circular object, causing a collision
*
* @param x the position of the center of the circular object
* @param y the position of the center of the circular object
* @param radius the radius of the circular object
* @return true if the object overlaps this one
*/
public boolean overlaps(float x, float y, float radius) {
return (float) Math.sqrt(Math.pow(x - this.x, 2) + Math.pow(y - this.y, 2)) < getSize() + radius;
}
/**
* Check if a point in space occupies the same space as this object
*
* @param x the x position of the point to check
* @param y the y position of the point to check
* @return true if the point is part of this object's area
*/
public boolean overlaps(float x, float y) {
return overlaps(x, y, 1);
}
/**
* Translate this object is space
*
* @param deltaX x translation
* @param deltaY y translation
*/
public void move(float deltaX, float deltaY) {
x += deltaX;
y += deltaY;
}
/**
* Run one iteration of this object's logic
*/
public abstract void update();
/**
* Draw this object
*
* @param s the ShapeRenderer used to draw this object
*/
public abstract void render(ShapeRenderer s);
public float getX() {

View File

@ -1,15 +1,12 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
/**
* Stores a sight of an element, as seen from a creature's eye
*
* @author fazo
*/
public class Sight {
private Element seen;
private float distance, angle;
@ -30,5 +27,5 @@ public class Sight {
public float getAngle() {
return angle;
}
}

View File

@ -1,15 +1,10 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.mygdx.game.Game;
/**
*
* Represents a small plant-like vegetable with circular shape
* @author fazo
*/
public class Vegetable extends Element {

View File

@ -1,8 +1,3 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic;
import com.mygdx.game.Game;
@ -18,6 +13,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class represents an instance of a simulation, its world and its
* configuration
*
* @author fazo
*/
@ -40,6 +37,12 @@ public class World implements Runnable {
private final ArrayList<Vegetable> deadPlants;
private final ArrayList<Listener> listeners;
/**
* Create a new World. Can be customized with given options.
*
* @param options customization options. Can be null. See the
* "reloadOptions" function for possible options
*/
public World(Map<String, Float> options) {
if (options == null) {
this.options = new HashMap<String, Float>();
@ -240,6 +243,10 @@ public class World implements Runnable {
}
}
/**
* Applies current options. Uses default alternatives if options are not
* provided
*/
public void reloadOptions() {
width = Math.round(options.getOrDefault("world_width", 2000f));
height = Math.round(options.getOrDefault("world_height", 2000f));
@ -262,6 +269,14 @@ public class World implements Runnable {
mutationFactor = options.getOrDefault("nMutationFactor", 1f);
}
/**
* Spawn a new random element in the world, at a random position.
*
* @param isCreature true if you want to spawn a creature
* @param brainMap the brain configuration. Used if spawning a creature. If
* null, a random mind will be created
* @return the spawned element
*/
private Element spawn(boolean isCreature, float[][][] brainMap) {
int x, y, r;
boolean overlaps = false;
@ -301,6 +316,13 @@ public class World implements Runnable {
}
}
/**
* Sets currently select creature to the first creature that overlaps given
* coordinates
*
* @param x the x coordinate of the creature you want to select
* @param y the x coordinate of the creature you want to select
*/
public void selectCreatureAt(int x, int y) {
selected = null; // Clear selection
try {
@ -314,6 +336,12 @@ public class World implements Runnable {
}
}
/**
* Fire an event
*
* @param eventCode the event code. Look at the Listener class for event
* codes.
*/
public void fire(int eventCode) {
for (Listener f : listeners) {
f.on(eventCode);

View File

@ -83,7 +83,7 @@ public class Brain {
}
/**
* Draw this brain's status.
* Draw this brain's status to the screen.
*
* @param s the ShapeRenderer to use for the drawing
*/
@ -224,6 +224,17 @@ public class Brain {
}
}
/**
* Combine this brain with another one's map to get an offspring. The brains
* must have identical neuron configuration. There are huge amounts of
* combinations, so you can call multiple times to get different children
* from the same parents
*
* @param map the brain to "breed" with this one
* @return a child brain from the two brains
* @throws Exception if the brains don't have identical neuron and layer
* numbers
*/
public float[][][] breed(float[][][] map) throws Exception {
float[][][] res = new float[neurons.length - 1][][];
if (map.length != neurons.length - 1) {
@ -237,9 +248,6 @@ public class Brain {
}
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
{
// j = 8 not valid for neurons[i][j]. investigate why.
//System.out.println(i+" "+j+" | "+neurons[i].length+" "+res[i-1].length+" "+neurons[i][j].getWeights().length);
//System.out.println(neurons[i].length +" has to be > "+j);
res[i - 1][j] = new float[neurons[i][j].getWeights().length];
if (map[i - 1][j].length != neurons[i][j].getWeights().length) {
throw new Exception("incompatible brains");

View File

@ -1,8 +1,3 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package logic.neural;
import com.mygdx.game.Log;
@ -10,6 +5,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A neuron in some brain. See Brain class.
*
* @author fazo
*/
@ -22,10 +18,27 @@ public class Neuron {
private int layer;
private Brain brain;
/**
* Create a neuron with given brain, bias, and at the given layer with 0
* being the input layer, with random weights
*
* @param layer the layer in which this neuron is positioned
* @param bias the bias of this neuron
* @param brain the brain which contains this neuron
*/
public Neuron(int layer, float bias, Brain brain) {
this(layer, bias, brain, null);
}
/**
* Create a neuron with given brain, bias, and at the given layer with 0
* being the input layer, with given weights
*
* @param layer the layer in which this neuron is positioned
* @param bias the bias of this neuron
* @param brain the brain which contains this neuron
* @param weights the weights to use to configure this neuron
*/
public Neuron(int layer, float bias, Brain brain, float[] weights) {
this.brain = brain;
this.layer = layer;
@ -37,6 +50,9 @@ public class Neuron {
cache = new NeuronCache(this.weights.length);
}
/**
* Randomize the weights of this neuron
*/
private void scramble() {
// init weights
if (layer > 0) {
@ -51,6 +67,14 @@ public class Neuron {
}
}
/**
* Compute the output of this neuron using the previous layer. Does nothing
* with input neurons. Uses a cache to store the output until it is
* invalidated by using the clearCache function. This function is recursive,
* meaning it will calculate all necessary neuron outputs to get this one.
*
* @return the output of this neuron.
*/
public float compute() {
if (isInputNeuron) {
return output;
@ -67,16 +91,27 @@ public class Neuron {
// This should never happen
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
Neuron n = brain.getNeurons()[layer - 1][i];
float v = n.compute() * weights[i];
a += v;
cache.put(i, v);
}
Neuron n = brain.getNeurons()[layer - 1][i];
a += n.compute() * weights[i];
}
// sigmoid function
float res = (float) (1 / (1 + Math.pow(Math.E, a * -1)));
cache.setCachedOutput(res);
Log.log(Log.DEBUG, "Computed Value " + res + " for neuron");
return res;
}
/**
* Get a copy of the weights, with a mutation
*
* @param mutationProbability controls how many weights actually mutates
* @param mutationFactor controls how much weights mutate
* @return the new weights
*/
public float[] getMutatedWeights(float mutationProbability, float mutationFactor) {
float[] mutatedWeights = new float[weights.length];
for (int i = 0; i < weights.length; i++) {
@ -90,27 +125,10 @@ public class Neuron {
}
/**
* Broken, doesn't work for some reason.
* Use this to manually set the output of input neurons
*
* @return
* @param output the output you want to set
*/
public float[] getInputs() {
float inputs[] = new float[weights.length];
for (int i = 0; i < inputs.length; i++) {
if (cache.has(i)) {
try {
inputs[i] = cache.get(i);
} catch (Exception ex) {
// Shouldnt happen
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
inputs[i] = 0;
}
}
return inputs;
}
public void setOutput(float output) {
isInputNeuron = true;
this.output = output;
@ -140,6 +158,11 @@ public class Neuron {
return weights;
}
/**
* Change the neuron weights
*
* @param weights the new weights to put
*/
public void setWeights(float[] weights) {
this.weights = weights;
// Changing the neuron makes the cache invalid