diff --git a/.nb-gradle/profiles/private/aux-config b/.nb-gradle/profiles/private/aux-config index af2bccb..29b08c7 100644 --- a/.nb-gradle/profiles/private/aux-config +++ b/.nb-gradle/profiles/private/aux-config @@ -8,7 +8,14 @@ - + + file:/home/fazo/Documents/Git/AIrium/core/src/logic/neural/NeuralConnection.java + file:/home/fazo/Documents/Git/AIrium/core/src/com/mygdx/game/Game.java + file:/home/fazo/Documents/Git/AIrium/core/src/logic/neural/Neuron.java + file:/home/fazo/Documents/Git/AIrium/core/src/logic/Creature.java + file:/home/fazo/Documents/Git/AIrium/core/src/logic/neural/Brain.java + file:/home/fazo/Documents/Git/AIrium/core/src/logic/World.java + diff --git a/core/src/logic/Creature.java b/core/src/logic/Creature.java index a0a5786..4e19f8c 100644 --- a/core/src/logic/Creature.java +++ b/core/src/logic/Creature.java @@ -2,6 +2,7 @@ package logic; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.mygdx.game.Game; +import logic.neural.Brain; /** * A (hopefully) smart biological creature. @@ -11,8 +12,10 @@ import com.mygdx.game.Game; public class Creature extends Element { public static final int default_radius = 20; + public static final float max_speed = 3; - private float dir, speed, accel, sightRange, fov, fitness, rotSpeed; + private Brain brain; + private float dir, speed, sightRange, fov, fitness, rotSpeed; private float hp; private Sight sight; @@ -20,12 +23,12 @@ public class Creature extends Element { super(x, y, default_radius); dir = (float) (Math.random() * 2 * Math.PI); hp = 100; - speed = (float) Math.random() * 3; - rotSpeed = (float) Math.random() - 0.5f; - accel = 0f; + speed = 0;//(float) Math.random() * 3; + rotSpeed = 0;//(float) Math.random() - 0.5f; sightRange = 40; fov = (float) Math.PI / 2; fitness = 100; + brain = new Brain(3, 2, 1, 4); } @Override @@ -35,12 +38,11 @@ public class Creature extends Element { if (hp < 0) { Game.get().getWorld().getGraveyard().add(this); } - speed += accel; // apply acceleration - if (speed > 0) { - speed -= 0.001; // attrito + if (speed > max_speed) { + speed = max_speed; } - if (speed < 0) { - speed = 0; + if (speed < -max_speed) { + speed = -max_speed; } // apply speed float xMul = (float) Math.cos(dir), yMul = (float) Math.sin(dir); @@ -48,11 +50,38 @@ public class Creature extends Element { dir += rotSpeed; // try eating eat(); - fitness -= 0.1; + //fitness -= 0.1; if (dir > 2 * Math.PI) { dir -= 2 * Math.PI; } - sight = look(); + if (dir < 0) { + dir += 2 * Math.PI; + } + sight = look(); // take a look + // feed data to brain + float[] values = new float[3]; + // 0: type of sight + // 1: distance + // 2: angle + if (sight == null) { + values[0] = 0; + values[1] = 1; + values[2] = 0; + } else if (sight.getElement() instanceof Creature) { + values[0] = 1; + values[1] = sight.getDistance() / sightRange; + values[2] = sight.getAngle(); + } else { + values[0] = 0.5f; + values[1] = sight.getDistance() / sightRange; + values[2] = sight.getAngle(); + } + brain.input(values); + // compute behavior + float[] actions = brain.compute(); + System.out.println("Accel: " + actions[0] + " Rot: " + actions[1]); + speed = actions[0]*max_speed; + rotSpeed = actions[1] - 1f; } @Override @@ -116,7 +145,7 @@ public class Creature extends Element { for (Element e : Game.get().getWorld().getElements()) { if (e instanceof Vegetable && overlaps(e)) { e.setSize(e.getSize() - 0.1f); - hp += 0.1f; + hp ++; fitness++; if (hp > 100) { hp = 100; diff --git a/core/src/logic/World.java b/core/src/logic/World.java index b5f523e..2adc2bf 100644 --- a/core/src/logic/World.java +++ b/core/src/logic/World.java @@ -25,8 +25,8 @@ public class World { } public void update() { - while (elements.size() < 20) { - if (Math.random() < 0.2) { + while (elements.size() < 40) { + if (Math.random() < 0.4) { spawnCreature(); } else { spawnVegetable(); diff --git a/core/src/logic/neural/Brain.java b/core/src/logic/neural/Brain.java index c6ae108..0648aa4 100644 --- a/core/src/logic/neural/Brain.java +++ b/core/src/logic/neural/Brain.java @@ -1,9 +1,74 @@ package logic.neural; +import java.util.ArrayList; + /** * * @author fazo */ public class Brain { + + private ArrayList inputs, outputs, hidden; + + public Brain(int nInputs, int nOutputs, int hiddenLayers, int neuronsPerHiddenLayer) { + inputs = new ArrayList(nInputs); + outputs = new ArrayList(nOutputs); + hidden = new ArrayList(hiddenLayers * neuronsPerHiddenLayer); + // Create input neurons + for (int i = 0; i < nInputs; i++) { + inputs.add(new Neuron(0)); + } + // popiulate hidden layers + for (int i = 0; i < hiddenLayers; i++) { + for (int j = 0; j < neuronsPerHiddenLayer; j++) { + // create neuron + Neuron n = new Neuron(i + 1); + // add connections + for (Neuron s : inputs) { + n.getInputs().add(new NeuralConnection(randWeight(), s)); + } + hidden.add(n); + System.out.println("Adding Hidden Layer "+(i+1)+" Neuron "+j+" with "+inputs.size()+" inputs"); + } + } + // populate output layer + for (int i = 0; i < nOutputs; i++) { + // add neuron + Neuron n = new Neuron(hiddenLayers + 1); + int conn = 0; + for (Neuron s : hidden) { + // add connections where applicable + if (s.getLayer() == hiddenLayers) { + conn++; + n.getInputs().add(new NeuralConnection(randWeight(), s)); + } + } + System.out.println("Adding Output Layer Neuron "+i+" with "+conn+" inputs"); + outputs.add(n); + } + } + private float randWeight(){ + return (float) Math.random()*2-1f; + } + + public void input(float[] values) { + for (int i = 0; i < values.length; i++) { + inputs.get(i).setOutput(values[i]); + } + } + + public float[] compute() { + for (Neuron n : hidden) { + n.clearCachedValue(); + } + float[] res = new float[outputs.size()]; + for (int i=0;i inputs, outputs; + + private ArrayList inputs; + private float bias, output; + private boolean isInputNeuron; + private int layer; + private float cachedValue; + private boolean cachedValueValid = false; + + public Neuron(int layer) { + this.layer = layer; + inputs = new ArrayList(); + } + + public float compute() { + if (isInputNeuron) { + return output; + } + if (cachedValueValid) { + return cachedValue; + } + float a = bias * -1; // activation + for (NeuralConnection i : inputs) { + a += i.compute(); + } + System.out.println("Computed Value "+a+" for neuron"); + cachedValueValid = true; + // sigmoid function + cachedValue = (float) (1 / (1 + Math.pow(Math.E, a * -1))); + System.out.println("Computed Value "+cachedValue+" for neuron"); + return cachedValue; + } + + public void setOutput(float output) { + isInputNeuron = true; + this.output = output; + } + + public ArrayList getInputs() { + return inputs; + } + + public float getBias() { + return bias; + } + + public void setBias(float bias) { + this.bias = bias; + } + + public boolean isIsInputNeuron() { + return isInputNeuron; + } + + public int getLayer() { + return layer; + } + + public void setLayer(int layer) { + this.layer = layer; + } + + public void clearCachedValue() { + cachedValueValid = false; + for(NeuralConnection n : inputs) n.clearCachedValue(); + } + }