From 3d43a4ce68840232808e11bba6dac2ecd9177853 Mon Sep 17 00:00:00 2001 From: Enrico Fasoli Date: Thu, 2 Jul 2015 12:38:56 +0200 Subject: [PATCH] implemented first shitty genetic algorithm --- core/src/com/mygdx/game/Game.java | 2 +- core/src/com/mygdx/game/Log.java | 2 +- core/src/logic/Creature.java | 7 ++- core/src/logic/World.java | 79 +++++++++++++++++++++---------- core/src/logic/neural/Brain.java | 61 ++++++++++++++++++------ 5 files changed, 108 insertions(+), 43 deletions(-) diff --git a/core/src/com/mygdx/game/Game.java b/core/src/com/mygdx/game/Game.java index 86e95cc..b4b5ae5 100644 --- a/core/src/com/mygdx/game/Game.java +++ b/core/src/com/mygdx/game/Game.java @@ -32,7 +32,7 @@ public class Game extends ApplicationAdapter { public void render() { // Input if(Gdx.input.isKeyJustPressed(Input.Keys.SPACE)){ - world.newGen(); + world.newGen(false); } // Update world.update(); diff --git a/core/src/com/mygdx/game/Log.java b/core/src/com/mygdx/game/Log.java index 5b1f62b..cc74048 100644 --- a/core/src/com/mygdx/game/Log.java +++ b/core/src/com/mygdx/game/Log.java @@ -15,7 +15,7 @@ public class Log { public static final int INFO = 1; public static final int DEBUG = 2; - private static int logLevel = 0; + private static int logLevel = 1; public static void log(int level, String msg) { if (level <= logLevel) { diff --git a/core/src/logic/Creature.java b/core/src/logic/Creature.java index 61c4d70..a53e52d 100644 --- a/core/src/logic/Creature.java +++ b/core/src/logic/Creature.java @@ -28,7 +28,7 @@ public class Creature extends Element { rotSpeed = 0;//(float) Math.random() - 0.5f; sightRange = 60; fov = (float) Math.PI / 1.5f; - fitness = 100; + fitness = 0; brain = new Brain(3, 2, 2, 8); } @@ -177,4 +177,9 @@ public class Creature extends Element { return fitness; } + public void reset(){ + fitness = 0; + hp = 100; + } + } diff --git a/core/src/logic/World.java b/core/src/logic/World.java index edabb66..0ad7cbf 100644 --- a/core/src/logic/World.java +++ b/core/src/logic/World.java @@ -8,6 +8,8 @@ package logic; import com.mygdx.game.Log; import java.util.ArrayList; import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; import logic.neural.Brain; /** @@ -17,7 +19,7 @@ import logic.neural.Brain; public class World { public static final int creatPerGen = 10; - private int width, height; + private int width, height, generation = 0; public ArrayList elements; public ArrayList creatures; public ArrayList graveyard; @@ -32,7 +34,7 @@ public class World { plants = new ArrayList(); deadPlants = new ArrayList(); graveyard = new ArrayList(); - newGen(); + newGen(true); } public void update() { @@ -43,7 +45,7 @@ public class World { deadPlants.clear(); if (creatures.isEmpty()) { // All dead, next gen - newGen(); + newGen(false); } while (plants.size() < 50) { spawnVegetable(); @@ -53,43 +55,65 @@ public class World { } } - public void newGen() { + public void newGen(boolean restart) { elements.removeAll(creatures); - creatures.clear(); + graveyard.addAll(creatures); Comparator creatureComp = new Comparator() { @Override public int compare(Creature t, Creature t1) { - if (t.getFitness() < t1.getFitness()) { - return -1; - } else if (t.getFitness() > t1.getFitness()) { - return 1; - } - return 0; + // put the highest fitness first (sort in reverse) + return (int) (t1.getFitness() - t.getFitness() ); + /*if (t.getFitness() < t1.getFitness()) { + return -1; + } else if (t.getFitness() > t1.getFitness()) { + return 1; + } + return 0;*/ } }; - if (graveyard.size() == 0) { // First gen + if (graveyard.isEmpty() || restart) { // First gen + generation = 0; for (int i = 0; i < creatPerGen; i++) { spawnCreature(); } - } else { // Mutate previous gen - //graveyard.sort(creatureComp); - int x = 0; + } else { // Evolve previous gen + // Calculate avg fitness + float avgFitness = 0; for (Creature c : graveyard) { - c.getBrain().remap(c.getBrain().getMutatedMap(3f)); - if (x < creatPerGen) { - c.setHp(100); - creatures.add(c); - elements.add(c); - } else { - break; + avgFitness += c.getFitness(); + } + avgFitness = avgFitness / graveyard.size(); + Log.log(Log.INFO, "Gen " + generation + " done. Avg fitness: " + avgFitness); + // Start evolution + graveyard.sort(creatureComp); + for (int i = 0; i < creatPerGen / 2; i++) { + Creature c = graveyard.get(i); + c.reset(); + // Mutate + if (i != 0) { + try { + // create a child + float[][][] mind = c.getBrain().breed(graveyard.get(i - 1).getBrain().getMap()); + // spawn it + spawnCreature(mind); + } catch (Exception ex) { + // Should never happen + Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex); + } } + // Mutate parent + c.getBrain().remap(c.getBrain().getMutatedMap(0.1f)); + // Add it back in + creatures.add(c); + elements.add(c); } graveyard.clear(); + generation++; } } - private void spawn(boolean isCreature, Brain brain) { + private void spawn(boolean isCreature, float[][][] brainMap) { int x, y, r; boolean overlaps = false; if (isCreature) { @@ -108,12 +132,15 @@ public class World { } } while (overlaps); if (isCreature) { - Log.log(Log.INFO,"New Creat: " + x + " " + y); + Log.log(Log.INFO, "New Creat: " + x + " " + y); Creature c = new Creature(x, y); + if (brainMap != null) { + c.getBrain().remap(brainMap); + } elements.add(c); creatures.add(c); } else { - Log.log(Log.INFO,"New Veg: " + x + " " + y); + Log.log(Log.INFO, "New Veg: " + x + " " + y); Vegetable v = new Vegetable(x, y); elements.add(v); plants.add(v); @@ -128,7 +155,7 @@ public class World { spawn(true, null); } - private void spawnCreature(Brain b) { + private void spawnCreature(float[][][] b) { spawn(true, b); } diff --git a/core/src/logic/neural/Brain.java b/core/src/logic/neural/Brain.java index acee184..1cbffd2 100644 --- a/core/src/logic/neural/Brain.java +++ b/core/src/logic/neural/Brain.java @@ -56,10 +56,11 @@ public class Brain { 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]); + // skip input layer + if (neurons[i+1][j] == null) { + neurons[i+1][j] = new Neuron(j, bias, this, brainMap[i][j]); } else { - neurons[i][j].setWeights(brainMap[i][j]); + neurons[i+1][j].setWeights(brainMap[i][j]); } } } @@ -126,15 +127,13 @@ public class Brain { * 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 + float[][][] res = new float[neurons.length-1][][]; + for (int i = 1; i < neurons.length; i++) // layers (skip input layer) { - for (int j = 0; i < neurons[i].length; j++) // neurons per layer + res[i-1] = new float[neurons[i].length][]; + for (int j = 0; j < neurons[i].length; j++) // neurons per layer { - if (neurons[i][j] == null) { - continue; - } - res[i][j] = neurons[i][j].getWeights(); + res[i-1][j] = neurons[i][j].getWeights(); } } return res; @@ -147,13 +146,47 @@ public class Brain { * @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 + float[][][] res = new float[neurons.length-1][][]; + for (int i = 1; i < neurons.length; i++) // layers (skip input layer) { - res[i] = new float[neurons[i].length][]; + res[i-1] = new float[neurons[i].length][]; for (int j = 0; j < neurons[i].length; j++) // neurons per layer { - res[i][j] = neurons[i][j].mutate(mutationFactor); + res[i-1][j] = neurons[i][j].mutate(mutationFactor); + } + } + return res; + } + + public float[][][] breed(float[][][] map) throws Exception { + float[][][] res = new float[neurons.length-1][][]; + if (map.length != neurons.length-1) { + throw new Exception("incompatible brains"); + } + for (int i = 1; i < neurons.length; i++) // layers (skip input layer) + { + res[i-1] = new float[neurons[i].length][]; + if (map[i-1].length != neurons[i].length) { + throw new Exception("incompatible brains"); + } + 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"); + } + for (int z = 0; z < neurons[i][j].getWeights().length; z++) // each weight + { + // Combine the two weights + if (Math.random() < 0.5) { + res[i-1][j][z] = map[i-1][j][z]; + } else { + res[i-1][j][z] = neurons[i][j].getWeights()[z]; + } + } } } return res;