mirror of
https://github.com/fazo96/AIrium.git
synced 2025-01-10 09:34:20 +01:00
fixed bugs, implemented brain rendering and creature selection (broken for now)
This commit is contained in:
parent
75146638bf
commit
124087d381
@ -1,105 +1,127 @@
|
|||||||
package com.mygdx.game;
|
package com.mygdx.game;
|
||||||
|
|
||||||
import com.badlogic.gdx.ApplicationAdapter;
|
import com.badlogic.gdx.ApplicationAdapter;
|
||||||
import com.badlogic.gdx.Gdx;
|
import com.badlogic.gdx.Gdx;
|
||||||
import com.badlogic.gdx.Input;
|
import com.badlogic.gdx.Input;
|
||||||
import com.badlogic.gdx.graphics.GL20;
|
import com.badlogic.gdx.graphics.GL20;
|
||||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||||
import java.util.ConcurrentModificationException;
|
import java.util.ConcurrentModificationException;
|
||||||
import logic.Element;
|
import logic.Creature;
|
||||||
import logic.World;
|
import logic.Element;
|
||||||
|
import logic.World;
|
||||||
public class Game extends ApplicationAdapter {
|
|
||||||
|
public class Game extends ApplicationAdapter {
|
||||||
private static Game game;
|
|
||||||
ShapeRenderer shaper;
|
private static Game game;
|
||||||
private World world;
|
ShapeRenderer renderer, overlayRenderer;
|
||||||
private float cameraSpeed = 15;
|
private World world;
|
||||||
private BitmapFont font;
|
private float cameraSpeed = 15;
|
||||||
private boolean paused = false;
|
private BitmapFont font;
|
||||||
|
private boolean paused = false;
|
||||||
@Override
|
|
||||||
public void create() {
|
@Override
|
||||||
game = this;
|
public void create() {
|
||||||
shaper = new ShapeRenderer();
|
game = this;
|
||||||
shaper.setAutoShapeType(true);
|
renderer = new ShapeRenderer();
|
||||||
font = new BitmapFont();
|
renderer.setAutoShapeType(true);
|
||||||
Thread worldThread = new Thread(world);
|
overlayRenderer = new ShapeRenderer();
|
||||||
worldThread.setName("Worker");
|
overlayRenderer.setAutoShapeType(true);
|
||||||
worldThread.setPriority(Thread.MAX_PRIORITY);
|
font = new BitmapFont();
|
||||||
worldThread.start();
|
Thread worldThread = new Thread(world);
|
||||||
}
|
worldThread.setName("Worker");
|
||||||
|
worldThread.setPriority(Thread.MAX_PRIORITY);
|
||||||
public Game() {
|
worldThread.start();
|
||||||
world = new World(2500, 2500);
|
}
|
||||||
}
|
|
||||||
|
public Game() {
|
||||||
@Override
|
world = new World(2500, 2500);
|
||||||
public void render() {
|
}
|
||||||
// Controls
|
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) {
|
@Override
|
||||||
world.newGen(false);
|
public void render() {
|
||||||
}
|
// Controls
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) {
|
||||||
shaper.translate(-cameraSpeed, 0, 0);
|
world.newGen(false);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
|
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
|
||||||
shaper.translate(cameraSpeed, 0, 0);
|
renderer.translate(-cameraSpeed, 0, 0);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
|
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
|
||||||
shaper.translate(0, -cameraSpeed, 0);
|
renderer.translate(cameraSpeed, 0, 0);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
|
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
|
||||||
shaper.translate(0, cameraSpeed, 0);
|
renderer.translate(0, -cameraSpeed, 0);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.PLUS)) {
|
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
|
||||||
shaper.scale(0.5f, 0.5f, 1);
|
renderer.translate(0, cameraSpeed, 0);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.MINUS)) {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.PLUS)) {
|
||||||
shaper.scale(1.5f, 1.5f, 1);
|
renderer.scale(0.5f, 0.5f, 1);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.P)) {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.MINUS)) {
|
||||||
paused = !paused;
|
renderer.scale(1.5f, 1.5f, 1);
|
||||||
}
|
}
|
||||||
if (Gdx.input.isKeyJustPressed(Input.Keys.L)) {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.P)) {
|
||||||
if (world.getFpsLimit() == 60) {
|
paused = !paused;
|
||||||
world.setFpsLimit(0);
|
}
|
||||||
} else {
|
if (Gdx.input.isKeyJustPressed(Input.Keys.L)) {
|
||||||
world.setFpsLimit(60);
|
if (world.getFpsLimit() == 60) {
|
||||||
}
|
world.setFpsLimit(0);
|
||||||
}
|
} else {
|
||||||
// Draw
|
world.setFpsLimit(60);
|
||||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
}
|
||||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
}
|
||||||
shaper.setColor(1, 1, 1, 1);
|
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
|
||||||
shaper.begin(ShapeRenderer.ShapeType.Line);
|
renderer.translate(Gdx.input.getDeltaX(), Gdx.input.getDeltaY() * -1, 0);
|
||||||
try {
|
}
|
||||||
for (Element e : world.getElements()) {
|
/*
|
||||||
try {
|
// Broken for now
|
||||||
e.render(shaper);
|
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
|
||||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
// TODO: project coordinates to world
|
||||||
// No idea why it happens, but it's rendering so meh
|
world.selectCreatureAt(Gdx.input.getX(), Gdx.input.getY());
|
||||||
//Log.log(Log.ERROR, ex+"");
|
}*/
|
||||||
}
|
// Draw
|
||||||
}
|
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||||
} catch (ConcurrentModificationException ex) {
|
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
||||||
}
|
renderer.begin(ShapeRenderer.ShapeType.Line);
|
||||||
shaper.setColor(0.3f, 0.3f, 0.3f, 1);
|
try {
|
||||||
// draw borders
|
for (Element e : world.getElements()) {
|
||||||
shaper.rect(0, 0, world.getWidth(), world.getHeight());
|
try {
|
||||||
shaper.end();
|
e.render(renderer);
|
||||||
}
|
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||||
|
// No idea why it happens, but it's rendering so meh
|
||||||
public World getWorld() {
|
//Log.log(Log.ERROR, ex+"");
|
||||||
return world;
|
}
|
||||||
}
|
}
|
||||||
|
} catch (ConcurrentModificationException ex) {
|
||||||
public static Game get() {
|
}
|
||||||
return game;
|
if (world.getSelectedCreature() != null) {
|
||||||
}
|
// There is a selection
|
||||||
|
Creature c = world.getSelectedCreature();
|
||||||
public boolean isPaused() {
|
renderer.setColor(1, 1, 1, 1);
|
||||||
return paused;
|
// Draw selection rectangle
|
||||||
}
|
renderer.rect(c.getX() - c.getSize() / 2, c.getY() - c.getSize() / 2, c.getX() + c.getSize() / 2, c.getY() + c.getSize() / 2);
|
||||||
}
|
// Draw brain
|
||||||
|
overlayRenderer.begin();
|
||||||
|
c.getBrain().render(overlayRenderer);
|
||||||
|
overlayRenderer.end();
|
||||||
|
}
|
||||||
|
renderer.setColor(0.3f, 0.3f, 0.3f, 1);
|
||||||
|
// draw borders
|
||||||
|
renderer.rect(0, 0, world.getWidth(), world.getHeight());
|
||||||
|
renderer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public World getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Game get() {
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPaused() {
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package logic;
|
package logic;
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||||
import com.mygdx.game.Game;
|
import com.mygdx.game.Game;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author fazo
|
* @author fazo
|
||||||
*/
|
*/
|
||||||
public class Vegetable extends Element {
|
public class Vegetable extends Element {
|
||||||
|
|
||||||
public static final int default_radius = 5;
|
public static final int default_radius = 5;
|
||||||
private float decayRate = 0;
|
private float decayRate = 0;
|
||||||
|
|
||||||
public Vegetable(float x, float y) {
|
public Vegetable(float x, float y) {
|
||||||
super(x, y, default_radius);
|
super(x, y, default_radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
setSize(getSize()-decayRate);
|
setSize(getSize()-decayRate);
|
||||||
if (getSize() <= 0) {
|
if (getSize() <= 2) {
|
||||||
Game.get().getWorld().getDeadPlants().add(this);
|
Game.get().getWorld().getDeadPlants().add(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(ShapeRenderer s) {
|
public void render(ShapeRenderer s) {
|
||||||
s.setColor(1, 1, 1, 1);
|
s.setColor(1, 1, 1, 1);
|
||||||
s.circle(getX(), getY(), getSize());
|
s.circle(getX(), getY(), getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getDecayRate() {
|
public float getDecayRate() {
|
||||||
return decayRate;
|
return decayRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecayRate(float decayRate) {
|
public void setDecayRate(float decayRate) {
|
||||||
this.decayRate = decayRate;
|
this.decayRate = decayRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,279 +1,311 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package logic;
|
package logic;
|
||||||
|
|
||||||
import com.mygdx.game.Game;
|
import com.mygdx.game.Game;
|
||||||
import com.mygdx.game.Log;
|
import com.mygdx.game.Log;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.ConcurrentModificationException;
|
||||||
import java.util.logging.Level;
|
import java.util.Date;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
/**
|
|
||||||
*
|
/**
|
||||||
* @author fazo
|
*
|
||||||
*/
|
* @author fazo
|
||||||
public class World implements Runnable {
|
*/
|
||||||
|
public class World implements Runnable {
|
||||||
private final int width, height, nPlants, creatPerGen;
|
|
||||||
private int generation = 1;
|
private final int width, height, nPlants, creatPerGen;
|
||||||
private int fpsLimit = 60, fps = 0;
|
private int generation = 1;
|
||||||
private final ArrayList<Element> elements;
|
private int fpsLimit = 60, fps = 0;
|
||||||
private final ArrayList<Element> toAdd;
|
private Creature selected;
|
||||||
private final ArrayList<Creature> creatures;
|
private final ArrayList<Element> elements;
|
||||||
private final ArrayList<Creature> graveyard;
|
private final ArrayList<Element> toAdd;
|
||||||
private final ArrayList<Vegetable> plants;
|
private final ArrayList<Creature> creatures;
|
||||||
private final ArrayList<Vegetable> deadPlants;
|
private final ArrayList<Creature> graveyard;
|
||||||
private final ArrayList<FpsListener> fpsListeners;
|
private final ArrayList<Vegetable> plants;
|
||||||
|
private final ArrayList<Vegetable> deadPlants;
|
||||||
public World(int width, int height) {
|
private final ArrayList<FpsListener> fpsListeners;
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
public World(int width, int height) {
|
||||||
elements = new ArrayList();
|
this.width = width;
|
||||||
creatures = new ArrayList();
|
this.height = height;
|
||||||
toAdd = new ArrayList();
|
elements = new ArrayList();
|
||||||
creatPerGen = Math.min(Math.round(width * height / 20000), 50);
|
creatures = new ArrayList();
|
||||||
nPlants = Math.round(width * height / 5500);
|
toAdd = new ArrayList();
|
||||||
plants = new ArrayList();
|
creatPerGen = Math.min(Math.round(width * height / 20000), 50);
|
||||||
deadPlants = new ArrayList();
|
nPlants = Math.round(width * height / 5500);
|
||||||
graveyard = new ArrayList();
|
plants = new ArrayList();
|
||||||
fpsListeners = new ArrayList();
|
deadPlants = new ArrayList();
|
||||||
newGen(true);
|
graveyard = new ArrayList();
|
||||||
}
|
fpsListeners = new ArrayList();
|
||||||
|
selected = null;
|
||||||
@Override
|
newGen(true);
|
||||||
public void run() {
|
}
|
||||||
Date d, timekeeper = new Date();
|
|
||||||
long time;
|
@Override
|
||||||
int target, frames = 0;
|
public void run() {
|
||||||
for (;;) {
|
Date d, timekeeper = new Date();
|
||||||
if (!Game.get().isPaused()) {
|
long time;
|
||||||
d = new Date();
|
int target, frames = 0;
|
||||||
update();
|
for (;;) {
|
||||||
frames++;
|
if (!Game.get().isPaused()) {
|
||||||
Date now = new Date();
|
d = new Date();
|
||||||
if (now.getTime() - timekeeper.getTime() > 1000) {
|
update();
|
||||||
fps = frames;
|
frames++;
|
||||||
frames = 0;
|
Date now = new Date();
|
||||||
for (FpsListener f : fpsListeners) {
|
if (now.getTime() - timekeeper.getTime() > 1000) {
|
||||||
f.fpsChanged(fps);
|
fps = frames;
|
||||||
}
|
frames = 0;
|
||||||
timekeeper = new Date();
|
for (FpsListener f : fpsListeners) {
|
||||||
}
|
f.fpsChanged(fps);
|
||||||
if (fpsLimit > 0) {
|
}
|
||||||
time = now.getTime() - d.getTime();
|
timekeeper = new Date();
|
||||||
target = 1000 / fpsLimit;
|
}
|
||||||
if (time < target) {
|
if (fpsLimit > 0) {
|
||||||
try {
|
time = now.getTime() - d.getTime();
|
||||||
Thread.sleep((long) (target - time));
|
target = 1000 / fpsLimit;
|
||||||
} catch (InterruptedException ex) {
|
if (time < target) {
|
||||||
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
try {
|
||||||
}
|
Thread.sleep((long) (target - time));
|
||||||
}
|
} catch (InterruptedException ex) {
|
||||||
}
|
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
} else {
|
}
|
||||||
try {
|
}
|
||||||
Thread.sleep(100);
|
}
|
||||||
} catch (InterruptedException ex) {
|
} else {
|
||||||
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
try {
|
||||||
}
|
Thread.sleep(100);
|
||||||
}
|
} catch (InterruptedException ex) {
|
||||||
}
|
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public void update() {
|
}
|
||||||
for (Element e : toAdd) {
|
}
|
||||||
elements.add(e);
|
|
||||||
if (e instanceof Creature) {
|
public void update() {
|
||||||
creatures.add((Creature) e);
|
for (Element e : toAdd) {
|
||||||
} else if (e instanceof Vegetable) {
|
elements.add(e);
|
||||||
plants.add((Vegetable) e);
|
if (e instanceof Creature) {
|
||||||
}
|
creatures.add((Creature) e);
|
||||||
}
|
} else if (e instanceof Vegetable) {
|
||||||
toAdd.clear();
|
plants.add((Vegetable) e);
|
||||||
elements.removeAll(graveyard);
|
}
|
||||||
elements.removeAll(deadPlants);
|
}
|
||||||
plants.removeAll(deadPlants);
|
toAdd.clear();
|
||||||
deadPlants.clear();
|
elements.removeAll(graveyard);
|
||||||
creatures.removeAll(graveyard);
|
if (selected != null && graveyard.contains(selected)) {
|
||||||
if (creatures.isEmpty()) {
|
selected = null;
|
||||||
// All dead, next gen
|
Log.log(Log.INFO, "Cleared selection");
|
||||||
newGen(false);
|
}
|
||||||
}
|
elements.removeAll(deadPlants);
|
||||||
while (plants.size() < nPlants) {
|
plants.removeAll(deadPlants);
|
||||||
spawnVegetable();
|
deadPlants.clear();
|
||||||
}
|
creatures.removeAll(graveyard);
|
||||||
for (Element e : elements) {
|
if (creatures.isEmpty()) {
|
||||||
e.update();
|
// All dead, next gen
|
||||||
}
|
newGen(false);
|
||||||
}
|
}
|
||||||
|
while (plants.size() < nPlants) {
|
||||||
public void newGen(boolean restart) {
|
spawnVegetable();
|
||||||
elements.removeAll(creatures);
|
}
|
||||||
graveyard.addAll(creatures);
|
for (Element e : elements) {
|
||||||
creatures.clear();
|
e.update();
|
||||||
Comparator creatureComp = new Comparator<Creature>() {
|
}
|
||||||
|
}
|
||||||
@Override
|
|
||||||
public int compare(Creature t, Creature t1) {
|
public void newGen(boolean restart) {
|
||||||
// put the highest fitness first (sort in reverse)
|
elements.removeAll(creatures);
|
||||||
return (int) (t1.getFitness() - t.getFitness());
|
graveyard.addAll(creatures);
|
||||||
}
|
creatures.clear();
|
||||||
};
|
if (selected != null) {
|
||||||
if (graveyard.isEmpty() || restart) { // First gen
|
selected = null;
|
||||||
generation = 1;
|
Log.log(Log.INFO, "Cleared selection");
|
||||||
Log.log(Log.INFO, "Starting from generation 1: spawning " + creatPerGen + " creatures.");
|
}
|
||||||
for (int i = 0; i < creatPerGen; i++) {
|
Comparator creatureComp = new Comparator<Creature>() {
|
||||||
spawnCreature();
|
|
||||||
}
|
@Override
|
||||||
} else { // Evolve previous gen
|
public int compare(Creature t, Creature t1) {
|
||||||
graveyard.sort(creatureComp); // sort by fitness
|
// put the highest fitness first (sort in reverse)
|
||||||
// Prepare best agent list
|
return (int) (t1.getFitness() - t.getFitness());
|
||||||
int topSize = (int) Math.round(graveyard.size() * 0.05f);
|
}
|
||||||
Creature[] top = new Creature[topSize];
|
};
|
||||||
// Calculate avg fitness and prepare best agent list
|
if (graveyard.isEmpty() || restart) { // First gen
|
||||||
float avgFitness = 0;
|
generation = 1;
|
||||||
for (int i = 0; i < graveyard.size(); i++) {
|
Log.log(Log.INFO, "Starting from generation 1: spawning " + creatPerGen + " creatures.");
|
||||||
Creature c = graveyard.get(i);
|
for (int i = 0; i < creatPerGen; i++) {
|
||||||
if (i < topSize) {
|
spawnCreature();
|
||||||
top[i] = graveyard.get(i);
|
}
|
||||||
Log.log(Log.INFO, "Gen " + generation + " Top " + (i + 1) + ": " + c.getFitness());
|
} else { // Evolve previous gen
|
||||||
}
|
graveyard.sort(creatureComp); // sort by fitness
|
||||||
avgFitness += c.getFitness();
|
// Prepare best agent list
|
||||||
}
|
int topSize = (int) Math.round(graveyard.size() * 0.05f);
|
||||||
avgFitness = avgFitness / graveyard.size();
|
Creature[] top = new Creature[topSize];
|
||||||
Log.log(Log.INFO, "Gen " + generation + " done. Avg fitness: " + avgFitness);
|
// Calculate avg fitness and prepare best agent list
|
||||||
// Generate children
|
float avgFitness = 0;
|
||||||
for (Creature c : graveyard) {
|
for (int i = 0; i < graveyard.size(); i++) {
|
||||||
int first = (int) Math.floor(Math.random() * topSize);
|
Creature c = graveyard.get(i);
|
||||||
int sec = first;
|
if (i < topSize) {
|
||||||
while (sec == first) {
|
top[i] = graveyard.get(i);
|
||||||
sec = (int) Math.floor(Math.random() * topSize);
|
Log.log(Log.INFO, "Gen " + generation + " Top " + (i + 1) + ": " + c.getFitness());
|
||||||
}
|
}
|
||||||
float[][][] n = null;
|
avgFitness += c.getFitness();
|
||||||
try {
|
}
|
||||||
n = top[first].getBrain().breed(top[sec].getBrain().getMap());
|
avgFitness = avgFitness / graveyard.size();
|
||||||
} catch (Exception ex) {
|
Log.log(Log.INFO, "Gen " + generation + " done. Avg fitness: " + avgFitness);
|
||||||
// Should not happen
|
// Generate children
|
||||||
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
for (Creature c : graveyard) {
|
||||||
}
|
int first = (int) Math.floor(Math.random() * topSize);
|
||||||
Creature ne = spawnCreature(n);
|
int sec = first;
|
||||||
ne.getBrain().mutate(0.05f); // mutate children
|
while (sec == first) {
|
||||||
}
|
sec = (int) Math.floor(Math.random() * topSize);
|
||||||
graveyard.clear();
|
}
|
||||||
generation++;
|
float[][][] n = null;
|
||||||
}
|
try {
|
||||||
}
|
n = top[first].getBrain().breed(top[sec].getBrain().getMap());
|
||||||
|
} catch (Exception ex) {
|
||||||
private Element spawn(boolean isCreature, float[][][] brainMap) {
|
// Should not happen
|
||||||
int x, y, r;
|
Logger.getLogger(World.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
boolean overlaps = false;
|
}
|
||||||
if (isCreature) {
|
Creature ne = spawnCreature(n);
|
||||||
r = Creature.default_radius;
|
ne.getBrain().mutate(0.05f); // mutate children
|
||||||
} else {
|
}
|
||||||
r = Vegetable.default_radius;
|
graveyard.clear();
|
||||||
}
|
generation++;
|
||||||
do {
|
}
|
||||||
overlaps = false;
|
}
|
||||||
x = (int) (Math.random() * width);
|
|
||||||
y = (int) (Math.random() * height);
|
private Element spawn(boolean isCreature, float[][][] brainMap) {
|
||||||
for (Element e : elements) {
|
int x, y, r;
|
||||||
if (e.overlaps(x, y, r)) {
|
boolean overlaps = false;
|
||||||
overlaps = true;
|
if (isCreature) {
|
||||||
}
|
r = Creature.default_radius;
|
||||||
}
|
} else {
|
||||||
} while (overlaps);
|
r = Vegetable.default_radius;
|
||||||
if (isCreature) {
|
}
|
||||||
Log.log(Log.DEBUG, "New Creat: " + x + " " + y);
|
do {
|
||||||
Creature c = new Creature(x, y);
|
overlaps = false;
|
||||||
if (brainMap != null) {
|
x = (int) (Math.random() * width);
|
||||||
c.getBrain().remap(brainMap);
|
y = (int) (Math.random() * height);
|
||||||
}
|
for (Element e : elements) {
|
||||||
//add(c);
|
if (e.overlaps(x, y, r)) {
|
||||||
elements.add(c);
|
overlaps = true;
|
||||||
creatures.add(c);
|
}
|
||||||
return c;
|
}
|
||||||
} else {
|
} while (overlaps);
|
||||||
Log.log(Log.DEBUG, "New Veg: " + x + " " + y);
|
if (isCreature) {
|
||||||
Vegetable v = new Vegetable(x, y);
|
Log.log(Log.DEBUG, "New Creat: " + x + " " + y);
|
||||||
//add(v);
|
Creature c = new Creature(x, y);
|
||||||
elements.add(v);
|
if (brainMap != null) {
|
||||||
plants.add(v);
|
c.getBrain().remap(brainMap);
|
||||||
return v;
|
}
|
||||||
}
|
//add(c);
|
||||||
}
|
elements.add(c);
|
||||||
|
creatures.add(c);
|
||||||
public interface FpsListener {
|
return c;
|
||||||
|
} else {
|
||||||
public abstract void fpsChanged(int newValue);
|
Log.log(Log.DEBUG, "New Veg: " + x + " " + y);
|
||||||
}
|
Vegetable v = new Vegetable(x, y);
|
||||||
|
//add(v);
|
||||||
private void spawnVegetable() {
|
elements.add(v);
|
||||||
spawn(false, null);
|
plants.add(v);
|
||||||
}
|
return v;
|
||||||
|
}
|
||||||
private Creature spawnCreature() {
|
}
|
||||||
return (Creature) spawn(true, null);
|
|
||||||
}
|
public interface FpsListener {
|
||||||
|
|
||||||
private Creature spawnCreature(float[][][] b) {
|
public abstract void fpsChanged(int newValue);
|
||||||
return (Creature) spawn(true, b);
|
}
|
||||||
}
|
|
||||||
|
public void selectCreatureAt(int x, int y) {
|
||||||
public int getWidth() {
|
selected = null; // Clear selection
|
||||||
return width;
|
try {
|
||||||
}
|
for (Creature c : creatures) {
|
||||||
|
if (c.overlaps(x, y)) {
|
||||||
public int getHeight() {
|
selected = c;
|
||||||
return height;
|
Log.log(Log.INFO, "Selected a creature");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public int getGeneration() {
|
} catch (ConcurrentModificationException ex) {
|
||||||
return generation;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFpsListener(FpsListener f) {
|
private void spawnVegetable() {
|
||||||
fpsListeners.add(f);
|
spawn(false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Element e) {
|
private Creature spawnCreature() {
|
||||||
toAdd.add(e);
|
return (Creature) spawn(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Element> getElements() {
|
private Creature spawnCreature(float[][][] b) {
|
||||||
return elements;
|
return (Creature) spawn(true, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Creature> getGraveyard() {
|
public int getWidth() {
|
||||||
return graveyard;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Vegetable> getDeadPlants() {
|
public int getHeight() {
|
||||||
return deadPlants;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Creature> getCreatures() {
|
public int getGeneration() {
|
||||||
return creatures;
|
return generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Vegetable> getPlants() {
|
public void addFpsListener(FpsListener f) {
|
||||||
return plants;
|
fpsListeners.add(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getFpsLimit() {
|
public void add(Element e) {
|
||||||
return fpsLimit;
|
toAdd.add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFpsLimit(int fpsLimit) {
|
public ArrayList<Element> getElements() {
|
||||||
this.fpsLimit = fpsLimit;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getFps() {
|
public ArrayList<Creature> getGraveyard() {
|
||||||
return fps;
|
return graveyard;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public ArrayList<Vegetable> getDeadPlants() {
|
||||||
|
return deadPlants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Creature> getCreatures() {
|
||||||
|
return creatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Vegetable> getPlants() {
|
||||||
|
return plants;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFpsLimit() {
|
||||||
|
return fpsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFpsLimit(int fpsLimit) {
|
||||||
|
this.fpsLimit = fpsLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getFps() {
|
||||||
|
return fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Creature getSelectedCreature() {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectCreature(Creature selected) {
|
||||||
|
this.selected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,237 +1,271 @@
|
|||||||
package logic.neural;
|
package logic.neural;
|
||||||
|
|
||||||
import com.mygdx.game.Log;
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||||
|
import com.mygdx.game.Log;
|
||||||
/**
|
|
||||||
* Represents a virtual brain
|
/**
|
||||||
*
|
* Represents a virtual brain
|
||||||
* @author fazo
|
*
|
||||||
*/
|
* @author fazo
|
||||||
public class Brain {
|
*/
|
||||||
|
public class Brain {
|
||||||
public static final float bias = 0.5f;
|
|
||||||
private Neuron[][] neurons;
|
public static final float bias = 0.5f;
|
||||||
|
private Neuron[][] neurons;
|
||||||
/**
|
|
||||||
* Create a new brain with a random map (mind) with given number of 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 nInputs the number of input neurons (at least 1)
|
||||||
* @param hiddenLayers how many hidden layers of neurons (at least 1)
|
* @param nOutputs the number of output neurons (at least 1)
|
||||||
* @param neuronsPerHiddenLayer how many neurons per hidden layer (at least
|
* @param hiddenLayers how many hidden layers of neurons (at least 1)
|
||||||
* 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
|
public Brain(int nInputs, int nOutputs, int hiddenLayers, int neuronsPerHiddenLayer) {
|
||||||
neurons = new Neuron[hiddenLayers + 2][];
|
// Prepare brain map
|
||||||
neurons[0] = new Neuron[nInputs];
|
neurons = new Neuron[hiddenLayers + 2][];
|
||||||
neurons[hiddenLayers + 1] = new Neuron[nOutputs];
|
neurons[0] = new Neuron[nInputs];
|
||||||
for (int i = 0; i < hiddenLayers; i++) {
|
neurons[hiddenLayers + 1] = new Neuron[nOutputs];
|
||||||
neurons[i + 1] = new Neuron[neuronsPerHiddenLayer];
|
for (int i = 0; i < hiddenLayers; i++) {
|
||||||
}
|
neurons[i + 1] = new Neuron[neuronsPerHiddenLayer];
|
||||||
// Randomize brain
|
}
|
||||||
initialize();
|
// Randomize brain
|
||||||
}
|
initialize();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Create a new brain using given brain map (mind)
|
/**
|
||||||
*
|
* Create a new brain using given brain map (mind)
|
||||||
* @param brainMap the brain map (mind) to use
|
*
|
||||||
*/
|
* @param brainMap the brain map (mind) to use
|
||||||
public Brain(float[][][] brainMap) {
|
*/
|
||||||
neurons = new Neuron[brainMap.length][];
|
public Brain(float[][][] brainMap) {
|
||||||
for (int i = 0; i < brainMap.length; i++) { // for each layer
|
neurons = new Neuron[brainMap.length][];
|
||||||
neurons[i] = new Neuron[brainMap[i].length];
|
for (int i = 0; i < brainMap.length; i++) { // for each layer
|
||||||
for (int j = 0; j < brainMap[i].length; j++) { // for each neuron
|
neurons[i] = new Neuron[brainMap[i].length];
|
||||||
neurons[i][j] = new Neuron(i, bias, this, brainMap[i][j]);
|
for (int j = 0; j < brainMap[i].length; j++) { // for each neuron
|
||||||
}
|
neurons[i][j] = new Neuron(i, bias, this, brainMap[i][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Apply a new brain map (mind) to this brain
|
/**
|
||||||
*
|
* Apply a new brain map (mind) to this brain
|
||||||
* @param brainMap the new brain map to apply
|
*
|
||||||
*/
|
* @param brainMap the new brain map to apply
|
||||||
public void remap(float[][][] brainMap) {
|
*/
|
||||||
for (int i = 0; i < brainMap.length; i++) { // for each layer
|
public void remap(float[][][] brainMap) {
|
||||||
for (int j = 0; j < brainMap[i].length; j++) { // for each neuron
|
for (int i = 0; i < brainMap.length; i++) { // for each layer
|
||||||
// skip input layer
|
for (int j = 0; j < brainMap[i].length; j++) { // for each neuron
|
||||||
if (neurons[i + 1][j] == null) {
|
// skip input layer
|
||||||
neurons[i + 1][j] = new Neuron(j, bias, this, brainMap[i][j]);
|
if (neurons[i + 1][j] == null) {
|
||||||
} else {
|
neurons[i + 1][j] = new Neuron(j, bias, this, brainMap[i][j]);
|
||||||
neurons[i + 1][j].setWeights(brainMap[i][j]);
|
} else {
|
||||||
}
|
neurons[i + 1][j].setWeights(brainMap[i][j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Populate the brain with brand new random neurons
|
/**
|
||||||
*/
|
* Populate the brain with brand new random neurons
|
||||||
private void initialize() {
|
*/
|
||||||
// init hidden layers
|
private void initialize() {
|
||||||
for (int i = 0; i < neurons.length; i++) {
|
// init hidden layers
|
||||||
for (int j = 0; j < neurons[i].length; j++) {
|
for (int i = 0; i < neurons.length; i++) {
|
||||||
// create neuron
|
for (int j = 0; j < neurons[i].length; j++) {
|
||||||
Neuron n = new Neuron(i, bias, this);
|
// create neuron
|
||||||
neurons[i][j] = n;
|
Neuron n = new Neuron(i, bias, this);
|
||||||
Log.log(Log.DEBUG, "Adding Layer " + (i + 1) + " Neuron " + (j + 1));
|
neurons[i][j] = n;
|
||||||
}
|
Log.log(Log.DEBUG, "Adding Layer " + (i + 1) + " Neuron " + (j + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Give some input to the brain
|
/**
|
||||||
*
|
* Draw this brain's status.
|
||||||
* @param values the array of input. Its length must match the number of
|
*
|
||||||
* input neurons of this brain
|
* @param s the ShapeRenderer to use for the drawing
|
||||||
* @throws Exception if the number of inputs given differs from the number
|
*/
|
||||||
* of input neurons of this brain
|
public void render(ShapeRenderer s) {
|
||||||
*/
|
s.set(ShapeRenderer.ShapeType.Filled);
|
||||||
public void input(float[] values) throws Exception {
|
int neuronHeight = 0;
|
||||||
if (values.length != neurons[0].length) {
|
for (Neuron[] ns : neurons) {
|
||||||
throw new Exception("Not enough or too many inputs");
|
if (ns.length > neuronHeight) {
|
||||||
}
|
neuronHeight = ns.length;
|
||||||
for (int i = 0; i < values.length; i++) {
|
}
|
||||||
neurons[0][i].setOutput(values[i]);
|
}
|
||||||
}
|
s.rect(0, 0, neurons.length * 50, neuronHeight * 30);
|
||||||
clearCache();
|
for (int i = 0; i < neurons.length; i++) {
|
||||||
}
|
//s.set(ShapeRenderer.ShapeType.Line);
|
||||||
|
for (int j = 0; j < neurons[i].length; j++) {
|
||||||
/**
|
// get neuron result first so cache system can kick in and save some calculations
|
||||||
* Compute output of the brain starting from given input
|
float nr = neurons[i][j].compute();
|
||||||
*
|
// Draw neuron links
|
||||||
* @return an array as long as the number of output neurons, containing the
|
float[] links = neurons[i][j].getInputs();
|
||||||
* result
|
for (int f = 0; f < links.length; f++) {
|
||||||
*/
|
s.setColor(links[f], links[f], links[f], 1);
|
||||||
public float[] compute() {
|
s.line(i * 50, j * 30, (i - 1) * 50, f * 30);
|
||||||
//clearCache(); // unnecessary if already called when changing inputs
|
}
|
||||||
float[] res = new float[neurons[neurons.length - 1].length];
|
// Draw neuron
|
||||||
for (int i = 0; i < neurons[neurons.length - 1].length; i++) {
|
s.setColor(1 - nr, nr, 0, 1);
|
||||||
res[i] = neurons[neurons.length - 1][i].compute();
|
s.set(ShapeRenderer.ShapeType.Filled);
|
||||||
}
|
s.circle(i * 50, j * 30, 15);
|
||||||
return res;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Input some values (see input function) and then compute the results.
|
/**
|
||||||
*
|
* Give some input to the brain
|
||||||
* @param values
|
*
|
||||||
* @return the results of the neural network
|
* @param values the array of input. Its length must match the number of
|
||||||
* @throws Exception if the number of inputs given differs from the number
|
* input neurons of this brain
|
||||||
* of input neurons of this brain
|
* @throws Exception if the number of inputs given differs from the number
|
||||||
*/
|
* of input neurons of this brain
|
||||||
public float[] compute(float[] values) throws Exception {
|
*/
|
||||||
input(values);
|
public void input(float[] values) throws Exception {
|
||||||
return compute();
|
if (values.length != neurons[0].length) {
|
||||||
}
|
throw new Exception("Not enough or too many inputs");
|
||||||
|
}
|
||||||
/**
|
for (int i = 0; i < values.length; i++) {
|
||||||
* Get a brainMap that represents this brain's mind
|
neurons[0][i].setOutput(values[i]);
|
||||||
*
|
}
|
||||||
* @return a tridimensional floating point number array representing a full
|
clearCache();
|
||||||
* mind
|
}
|
||||||
*/
|
|
||||||
public float[][][] getMap() {
|
/**
|
||||||
float[][][] res = new float[neurons.length - 1][][];
|
* Compute output of the brain starting from given input
|
||||||
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
*
|
||||||
{
|
* @return an array as long as the number of output neurons, containing the
|
||||||
res[i - 1] = new float[neurons[i].length][];
|
* result
|
||||||
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
*/
|
||||||
{
|
public float[] compute() {
|
||||||
res[i - 1][j] = neurons[i][j].getWeights();
|
//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++) {
|
||||||
return res;
|
res[i] = neurons[neurons.length - 1][i].compute();
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
/**
|
}
|
||||||
* Get a map of this brain's mind.. with a mutation
|
|
||||||
*
|
/**
|
||||||
* @param mutationFactor the higher this number, the bigger the mutation
|
* Input some values (see input function) and then compute the results.
|
||||||
* @return a mutated brain map of this brain's mind
|
*
|
||||||
*/
|
* @param values
|
||||||
public float[][][] getMutatedMap(float mutationFactor) {
|
* @return the results of the neural network
|
||||||
float[][][] res = new float[neurons.length - 1][][];
|
* @throws Exception if the number of inputs given differs from the number
|
||||||
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
* of input neurons of this brain
|
||||||
{
|
*/
|
||||||
res[i - 1] = new float[neurons[i].length][];
|
public float[] compute(float[] values) throws Exception {
|
||||||
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
input(values);
|
||||||
{
|
return compute();
|
||||||
res[i - 1][j] = neurons[i][j].mutate(mutationFactor);
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
return res;
|
* Get a brainMap that represents this brain's mind
|
||||||
}
|
*
|
||||||
|
* @return a tridimensional floating point number array representing a full
|
||||||
/**
|
* mind
|
||||||
* Apply a mutation to this brain
|
*/
|
||||||
*
|
public float[][][] getMap() {
|
||||||
* @param mutationFactor the higher this number, the bigger the mutation
|
float[][][] res = new float[neurons.length - 1][][];
|
||||||
*/
|
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
||||||
public void mutate(float mutationFactor) {
|
{
|
||||||
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
res[i - 1] = new float[neurons[i].length][];
|
||||||
{
|
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
||||||
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
{
|
||||||
{
|
res[i - 1][j] = neurons[i][j].getWeights();
|
||||||
neurons[i][j].setWeights(neurons[i][j].mutate(mutationFactor));
|
}
|
||||||
}
|
}
|
||||||
}
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[][][] breed(float[][][] map) throws Exception {
|
/**
|
||||||
float[][][] res = new float[neurons.length - 1][][];
|
* Get a map of this brain's mind.. with a mutation
|
||||||
if (map.length != neurons.length - 1) {
|
*
|
||||||
throw new Exception("incompatible brains");
|
* @param mutationFactor the higher this number, the bigger the mutation
|
||||||
}
|
* @return a mutated brain map of this brain's mind
|
||||||
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
*/
|
||||||
{
|
public float[][][] getMutatedMap(float mutationFactor) {
|
||||||
res[i - 1] = new float[neurons[i].length][];
|
float[][][] res = new float[neurons.length - 1][][];
|
||||||
if (map[i - 1].length != neurons[i].length) {
|
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
||||||
throw new Exception("incompatible brains");
|
{
|
||||||
}
|
res[i - 1] = new float[neurons[i].length][];
|
||||||
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
||||||
{
|
{
|
||||||
// j = 8 not valid for neurons[i][j]. investigate why.
|
res[i - 1][j] = neurons[i][j].mutate(mutationFactor);
|
||||||
//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];
|
return res;
|
||||||
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
|
* Apply a mutation to this brain
|
||||||
{
|
*
|
||||||
// Combine the two weights
|
* @param mutationFactor the higher this number, the bigger the mutation
|
||||||
if (Math.random() < 0.5) {
|
*/
|
||||||
res[i - 1][j][z] = map[i - 1][j][z];
|
public void mutate(float mutationFactor) {
|
||||||
} else {
|
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
||||||
res[i - 1][j][z] = neurons[i][j].getWeights()[z];
|
{
|
||||||
}
|
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
|
||||||
}
|
{
|
||||||
}
|
neurons[i][j].setWeights(neurons[i][j].mutate(mutationFactor));
|
||||||
}
|
}
|
||||||
return res;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public float[][][] breed(float[][][] map) throws Exception {
|
||||||
* Empties the neurons' cache. Needs to be called after changing brain
|
float[][][] res = new float[neurons.length - 1][][];
|
||||||
* inputs or before computing the result.
|
if (map.length != neurons.length - 1) {
|
||||||
*/
|
throw new Exception("incompatible brains");
|
||||||
private void clearCache() {
|
}
|
||||||
for (int i = 1; i < neurons.length; i++) {
|
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
|
||||||
for (int j = 0; j < neurons[i].length; j++) {
|
{
|
||||||
neurons[i][j].clearCache();
|
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
|
||||||
/**
|
{
|
||||||
* Returns an array with pointers to all this brain's neurons.
|
// 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);
|
||||||
* @return bidimensional array with first index representing the layer.
|
//System.out.println(neurons[i].length +" has to be > "+j);
|
||||||
*/
|
res[i - 1][j] = new float[neurons[i][j].getWeights().length];
|
||||||
public Neuron[][] getNeurons() {
|
if (map[i - 1][j].length != neurons[i][j].getWeights().length) {
|
||||||
return neurons;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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++) {
|
||||||
|
neurons[i][j].clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,124 +1,142 @@
|
|||||||
/*
|
/*
|
||||||
* To change this license header, choose License Headers in Project Properties.
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
* To change this template file, choose Tools | Templates
|
* To change this template file, choose Tools | Templates
|
||||||
* and open the template in the editor.
|
* and open the template in the editor.
|
||||||
*/
|
*/
|
||||||
package logic.neural;
|
package logic.neural;
|
||||||
|
|
||||||
import com.mygdx.game.Log;
|
import com.mygdx.game.Log;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author fazo
|
* @author fazo
|
||||||
*/
|
*/
|
||||||
public class Neuron {
|
public class Neuron {
|
||||||
|
|
||||||
private float[] weights;
|
private float[] weights;
|
||||||
private NeuronCache cache;
|
private NeuronCache cache;
|
||||||
private float bias, output;
|
private float bias, output;
|
||||||
private boolean isInputNeuron;
|
private boolean isInputNeuron;
|
||||||
private int layer;
|
private int layer;
|
||||||
private Brain brain;
|
private Brain brain;
|
||||||
|
|
||||||
public Neuron(int layer, float bias, Brain brain) {
|
public Neuron(int layer, float bias, Brain brain) {
|
||||||
this(layer, bias, brain, null);
|
this(layer, bias, brain, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Neuron(int layer, float bias, Brain brain, float[] weights) {
|
public Neuron(int layer, float bias, Brain brain, float[] weights) {
|
||||||
this.brain = brain;
|
this.brain = brain;
|
||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
if (weights == null) {
|
if (weights == null) {
|
||||||
scramble();
|
scramble();
|
||||||
} else {
|
} else {
|
||||||
this.weights = weights;
|
this.weights = weights;
|
||||||
}
|
}
|
||||||
cache = new NeuronCache(this.weights.length);
|
cache = new NeuronCache(this.weights.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scramble() {
|
private void scramble() {
|
||||||
// init weights
|
// init weights
|
||||||
if (layer > 0) {
|
if (layer > 0) {
|
||||||
weights = new float[brain.getNeurons()[layer - 1].length];
|
weights = new float[brain.getNeurons()[layer - 1].length];
|
||||||
} else { // layer 0
|
} else { // layer 0
|
||||||
isInputNeuron = true;
|
isInputNeuron = true;
|
||||||
weights = new float[0];
|
weights = new float[0];
|
||||||
}
|
}
|
||||||
// Put random weights
|
// Put random weights
|
||||||
for (int i = 0; i < weights.length; i++) {
|
for (int i = 0; i < weights.length; i++) {
|
||||||
weights[i] = (float) (Math.random() * 5 - 2.5f);
|
weights[i] = (float) (Math.random() * 5 - 2.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float compute() {
|
public float compute() {
|
||||||
if (isInputNeuron) {
|
if (isInputNeuron) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
float a = bias * -1; // activation
|
if(cache.hasCachedOutput()) return cache.getCachedOutput();
|
||||||
for (int i = 0; i < weights.length; i++) {
|
float a = bias * -1; // activation
|
||||||
if (cache.has(i)) {
|
for (int i = 0; i < weights.length; i++) {
|
||||||
try {
|
if (cache.has(i)) {
|
||||||
return cache.get(i);
|
try {
|
||||||
} catch (Exception ex) {
|
a += cache.get(i);
|
||||||
// This should never happen
|
} catch (Exception ex) {
|
||||||
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
|
// This should never happen
|
||||||
}
|
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
}
|
}
|
||||||
Neuron n = brain.getNeurons()[layer - 1][i];
|
}
|
||||||
a += n.compute() * weights[i];
|
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)));
|
// sigmoid function
|
||||||
Log.log(Log.DEBUG, "Computed Value " + res + " for neuron");
|
float res = (float) (1 / (1 + Math.pow(Math.E, a * -1)));
|
||||||
return res;
|
Log.log(Log.DEBUG, "Computed Value " + res + " for neuron");
|
||||||
}
|
return res;
|
||||||
|
}
|
||||||
public float[] mutate(float mutationFactor) {
|
|
||||||
float[] mutatedWeights = new float[weights.length];
|
public float[] mutate(float mutationFactor) {
|
||||||
for (int i = 0; i < weights.length; i++) {
|
float[] mutatedWeights = new float[weights.length];
|
||||||
mutatedWeights[i] = weights[i] + mutationFactor - mutationFactor / 2;
|
for (int i = 0; i < weights.length; i++) {
|
||||||
}
|
mutatedWeights[i] = weights[i] + mutationFactor - mutationFactor / 2;
|
||||||
return mutatedWeights;
|
}
|
||||||
}
|
return mutatedWeights;
|
||||||
|
}
|
||||||
public void setOutput(float output) {
|
|
||||||
isInputNeuron = true;
|
public float[] getInputs() {
|
||||||
this.output = output;
|
float inputs[] = new float[weights.length];
|
||||||
}
|
for (int i = 0; i < inputs.length; i++) {
|
||||||
|
if (cache.has(i)) {
|
||||||
public float getBias() {
|
try {
|
||||||
return bias;
|
inputs[i] = cache.get(i);
|
||||||
}
|
} catch (Exception ex) {
|
||||||
|
// Shouldnt happen
|
||||||
public void setBias(float bias) {
|
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
this.bias = bias;
|
}
|
||||||
}
|
} else {
|
||||||
|
inputs[i] = 0;
|
||||||
public boolean isInputNeuron() {
|
}
|
||||||
return isInputNeuron;
|
}
|
||||||
}
|
return inputs;
|
||||||
|
}
|
||||||
public int getLayer() {
|
|
||||||
return layer;
|
public void setOutput(float output) {
|
||||||
}
|
isInputNeuron = true;
|
||||||
|
this.output = output;
|
||||||
public void setLayer(int layer) {
|
}
|
||||||
this.layer = layer;
|
|
||||||
}
|
public float getBias() {
|
||||||
|
return bias;
|
||||||
public float[] getWeights() {
|
}
|
||||||
return weights;
|
|
||||||
}
|
public void setBias(float bias) {
|
||||||
|
this.bias = bias;
|
||||||
public void setWeights(float[] weights) {
|
}
|
||||||
this.weights = weights;
|
|
||||||
// Changing the neuron makes the cache invalid
|
public boolean isInputNeuron() {
|
||||||
clearCache();
|
return isInputNeuron;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache() {
|
public int getLayer() {
|
||||||
cache.clear();
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public void setLayer(int layer) {
|
||||||
|
this.layer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] getWeights() {
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWeights(float[] weights) {
|
||||||
|
this.weights = weights;
|
||||||
|
// Changing the neuron makes the cache invalid
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache() {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,68 +1,84 @@
|
|||||||
package logic.neural;
|
package logic.neural;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used by neurons to cache inputs for faster NN evaluation performance.
|
* Used by neurons to cache inputs for faster NN evaluation performance.
|
||||||
*
|
*
|
||||||
* @author fazo
|
* @author fazo
|
||||||
*/
|
*/
|
||||||
public class NeuronCache {
|
public class NeuronCache {
|
||||||
|
|
||||||
private float[] cache;
|
private float[] cache;
|
||||||
private boolean[] validity;
|
private float cachedOutput;
|
||||||
|
private boolean cachedOutputValid;
|
||||||
/**
|
private boolean[] validity;
|
||||||
* Create a new empty input cache with given size.
|
|
||||||
*
|
/**
|
||||||
* @param size how many inputs the requiring neuron has.
|
* Create a new empty input cache with given size.
|
||||||
*/
|
*
|
||||||
public NeuronCache(int size) {
|
* @param size how many inputs the requiring neuron has.
|
||||||
cache = new float[size];
|
*/
|
||||||
validity = new boolean[size];
|
public NeuronCache(int size) {
|
||||||
clear();
|
cache = new float[size];
|
||||||
}
|
validity = new boolean[size];
|
||||||
|
clear();
|
||||||
/**
|
}
|
||||||
* Put a value in the cache.
|
|
||||||
*
|
/**
|
||||||
* @param index the index of the value
|
* Put a value in the cache.
|
||||||
* @param value the value itself
|
*
|
||||||
*/
|
* @param index the index of the value
|
||||||
public void put(int index, float value) {
|
* @param value the value itself
|
||||||
validity[index] = true;
|
*/
|
||||||
cache[index] = value;
|
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
|
* Read a value from the cache.
|
||||||
* @return the value required
|
*
|
||||||
* @throws Exception if value not stored or declared invalid
|
* @param index the index of the value
|
||||||
*/
|
* @return the value required
|
||||||
public float get(int index) throws Exception {
|
* @throws Exception if value not stored or declared invalid
|
||||||
if (validity[index]) {
|
*/
|
||||||
return cache[index];
|
public float get(int index) throws Exception {
|
||||||
} else {
|
if (validity[index]) {
|
||||||
throw new Exception("Value not present");
|
return cache[index];
|
||||||
}
|
} else {
|
||||||
}
|
throw new Exception("Value not present");
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Returns true if required value is present and valid in the cache.
|
|
||||||
*
|
/**
|
||||||
* @param index which value to check
|
* Returns true if required value is present and valid in the cache.
|
||||||
* @return true if has given value
|
*
|
||||||
*/
|
* @param index which value to check
|
||||||
public boolean has(int index) {
|
* @return true if has given value
|
||||||
return validity[index];
|
*/
|
||||||
}
|
public boolean has(int index) {
|
||||||
|
return validity[index];
|
||||||
/**
|
}
|
||||||
* Clears cache.
|
|
||||||
*/
|
public float getCachedOutput() {
|
||||||
public void clear() {
|
return cachedOutput;
|
||||||
for (int i = 0; i < cache.length; i++) {
|
}
|
||||||
validity[i] = false;
|
|
||||||
}
|
public boolean hasCachedOutput() {
|
||||||
}
|
return cachedOutputValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setCachedOutput(float cachedOutput) {
|
||||||
|
this.cachedOutput = cachedOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears cache.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
for (int i = 0; i < cache.length; i++) {
|
||||||
|
validity[i] = false;
|
||||||
|
}
|
||||||
|
cachedOutputValid = false;
|
||||||
|
cachedOutput = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user