diff --git a/.nb-gradle/profiles/private/aux-config b/.nb-gradle/profiles/private/aux-config index 3e5ac38..af2bccb 100644 --- a/.nb-gradle/profiles/private/aux-config +++ b/.nb-gradle/profiles/private/aux-config @@ -4,14 +4,7 @@ - - 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/World.java b/core/src/logic/World.java index b1cc1f1..54f6b1b 100644 --- a/core/src/logic/World.java +++ b/core/src/logic/World.java @@ -76,7 +76,7 @@ public class World { //graveyard.sort(creatureComp); int x = 0; for (Creature c : graveyard) { - c.getBrain().mutate(5f); + c.getBrain().getMutatedCopy(5f); if (x < creatPerGen) { c.setHp(100); creatures.add(c); diff --git a/core/src/logic/neural/Brain.java b/core/src/logic/neural/Brain.java index 7ed552c..acee184 100644 --- a/core/src/logic/neural/Brain.java +++ b/core/src/logic/neural/Brain.java @@ -1,9 +1,9 @@ package logic.neural; import com.mygdx.game.Log; -import java.util.ArrayList; /** + * Represents a virtual brain * * @author fazo */ @@ -12,73 +12,119 @@ public class Brain { public static final float bias = 0.5f; private Neuron[][] neurons; + /** + * Create a new brain with a random map (mind) with given number of neurons + * + * @param nInputs the number of input neurons (at least 1) + * @param nOutputs the number of output neurons (at least 1) + * @param hiddenLayers how many hidden layers of neurons (at least 1) + * @param neuronsPerHiddenLayer how many neurons per hidden layer (at least + * 1) + */ public Brain(int nInputs, int nOutputs, int hiddenLayers, int neuronsPerHiddenLayer) { + // Prepare brain map neurons = new Neuron[hiddenLayers + 2][]; - populate(nInputs, nOutputs, hiddenLayers, neuronsPerHiddenLayer); - } - - private void populate(int nInputs, int nOutputs, int hiddenLayers, int neuronsPerHiddenLayer) { - // Create input neurons neurons[0] = new Neuron[nInputs]; - for (int i = 0; i < nInputs; i++) { - neurons[0][i] = new Neuron(0, bias, this); - Log.log(Log.DEBUG, "Adding Input Layer Neuron " + (i + 1)); - } - // popiulate hidden layers + neurons[hiddenLayers + 1] = new Neuron[nOutputs]; for (int i = 0; i < hiddenLayers; i++) { neurons[i + 1] = new Neuron[neuronsPerHiddenLayer]; - for (int j = 0; j < neuronsPerHiddenLayer; j++) { - // create neuron - Neuron n = new Neuron(i + 1, bias, this); - neurons[i + 1][j] = n; - Log.log(Log.DEBUG, "Adding Hidden Layer " + (i + 1) + " Neuron " + (j + 1)); + } + // Randomize brain + initialize(); + } + + /** + * Create a new brain using given brain map (mind) + * + * @param brainMap the brain map (mind) to use + */ + public Brain(float[][][] brainMap) { + neurons = new Neuron[brainMap.length][]; + for (int i = 0; i < brainMap.length; i++) { // for each layer + neurons[i] = new Neuron[brainMap[i].length]; + for (int j = 0; j < brainMap[i].length; j++) { // for each neuron + neurons[i][j] = new Neuron(i, bias, this, brainMap[i][j]); } } - // populate output layer - neurons[hiddenLayers + 1] = new Neuron[nOutputs]; - for (int i = 0; i < nOutputs; i++) { - // add neuron - Neuron n = new Neuron(hiddenLayers + 1, bias, this); - neurons[hiddenLayers + 1][i] = n; - Log.log(Log.DEBUG, "Adding Output Layer Neuron " + (i + 1)); + } + + /** + * Apply a new brain map (mind) to this brain + * + * @param brainMap the new brain map to apply + */ + public void remap(float[][][] brainMap) { + for (int i = 0; i < brainMap.length; i++) { // for each layer + for (int j = 0; j < brainMap[i].length; j++) { // for each neuron + if (neurons[i][j] == null) { + neurons[i][j] = new Neuron(j, bias, this, brainMap[i][j]); + } else { + neurons[i][j].setWeights(brainMap[i][j]); + } + } } } - private float randWeight() { - return (float) Math.random() * 5 - 2.5f; + /** + * Populate the brain with brand new random neurons + */ + private void initialize() { + // init hidden layers + for (int i = 0; i < neurons.length; i++) { + for (int j = 0; j < neurons[i].length; j++) { + // create neuron + Neuron n = new Neuron(i, bias, this); + neurons[i][j] = n; + Log.log(Log.DEBUG, "Adding Layer " + (i + 1) + " Neuron " + (j + 1)); + } + } } + /** + * Give some input to the brain + * + * @param values the array of input. Its length must match the number of + * input neurons of this brain + */ public void input(float[] values) { for (int i = 0; i < values.length; i++) { neurons[0][i].setOutput(values[i]); } + clearCache(); } + /** + * Compute output of the brain starting from given input + * + * @return an array as long as the number of output neurons, containing the + * result + */ public float[] compute() { - clearCache(); + //clearCache(); // unnecessary if already called when changing inputs float[] res = new float[neurons[neurons.length - 1].length]; for (int i = 0; i < neurons[neurons.length - 1].length; i++) { - Neuron n = neurons[neurons.length - 1][i]; - if (n != null) { - res[i] = n.compute(); - } + res[i] = neurons[neurons.length - 1][i].compute(); } return res; } - public void map(float[][][] map) { - // Populate with new neurons - for (int j = 0; j < map.length; j++) { - for (int i = 0; i < map[j].length; i++) { - if (map[j] == null || map[i] == null) { - continue; - } - neurons[j][i] = new Neuron(j, bias, this); - neurons[j][i].setWeights(map[j][i]); - } - } + /** + * Input some values (see input function) and then compute the results. + * + * @param values + * @return the results of the neural network + */ + public float[] compute(float[] values) { + input(values); + return compute(); } + /** + * Get a brainMap that represents this brain's mind + * + * @return a tridimensional floating point number array representing a full + * mind + */ public float[][][] getMap() { float[][][] res = new float[neurons.length][neurons[1].length][neurons[1].length]; for (int i = 0; i < neurons.length; i++) // layers @@ -94,7 +140,13 @@ public class Brain { return res; } - public float[][][] mutate(float mutationFactor) { + /** + * Get a map of this brain's mind.. with a mutation + * + * @param mutationFactor the highest this number, the bigger the mutation + * @return a mutated brain map of this brain's mind + */ + public float[][][] getMutatedMap(float mutationFactor) { float[][][] res = new float[neurons.length][][]; for (int i = 0; i < neurons.length; i++) // layers { @@ -107,6 +159,10 @@ public class Brain { return res; } + /** + * Empties the neurons' cache. Needs to be called after changing brain + * inputs or before computing the result. + */ private void clearCache() { for (int i = 1; i < neurons.length; i++) { for (int j = 0; j < neurons[i].length; j++) { @@ -115,6 +171,11 @@ public class Brain { } } + /** + * Returns an array with pointers to all this brain's neurons. + * + * @return bidimensional array with first index representing the layer. + */ public Neuron[][] getNeurons() { return neurons; } diff --git a/core/src/logic/neural/Neuron.java b/core/src/logic/neural/Neuron.java index 5e7dcf0..3e4a44b 100644 --- a/core/src/logic/neural/Neuron.java +++ b/core/src/logic/neural/Neuron.java @@ -57,8 +57,6 @@ public class Neuron { } float a = bias * -1; // activation for (int i = 0; i < weights.length; i++) { - //if(brain == null) System.out.println("BRAINS NULL"); else if(brain.getNeurons() == null) System.out.println("NEURONS NULL"); - //System.out.println(Arrays.toString(brain.getNeurons())); if (cache.has(i)) { try { return cache.get(i); @@ -97,7 +95,7 @@ public class Neuron { this.bias = bias; } - public boolean isIsInputNeuron() { + public boolean isInputNeuron() { return isInputNeuron; } @@ -115,6 +113,8 @@ public class Neuron { public void setWeights(float[] weights) { this.weights = weights; + // Changing the neuron makes the cache invalid + clearCache(); } public void clearCache() { diff --git a/core/src/logic/neural/NeuronCache.java b/core/src/logic/neural/NeuronCache.java index 456b195..4ba1454 100644 --- a/core/src/logic/neural/NeuronCache.java +++ b/core/src/logic/neural/NeuronCache.java @@ -1,11 +1,7 @@ -/* - * 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; /** + * Used by neurons to cache inputs for faster NN evaluation performance. * * @author fazo */ @@ -14,17 +10,35 @@ public class NeuronCache { private float[] cache; private boolean[] validity; + /** + * Create a new empty input cache with given size. + * + * @param size how many inputs the requiring neuron has. + */ public NeuronCache(int size) { cache = new float[size]; validity = new boolean[size]; clear(); } + /** + * Put a value in the cache. + * + * @param index the index of the value + * @param value the value itself + */ public void put(int index, float value) { validity[index] = true; cache[index] = value; } + /** + * Read a value from the cache. + * + * @param index the index of the value + * @return the value required + * @throws Exception if value not stored or declared invalid + */ public float get(int index) throws Exception { if (validity[index]) { return cache[index]; @@ -33,10 +47,19 @@ public class NeuronCache { } } + /** + * Returns true if required value is present and valid in the cache. + * + * @param index which value to check + * @return true if has given value + */ public boolean has(int index) { return validity[index]; } + /** + * Clears cache. + */ public void clear() { for (int i = 0; i < cache.length; i++) { validity[i] = false;