1
0
mirror of https://github.com/fazo96/AIrium.git synced 2025-01-25 11:54:20 +01:00

fixed bugs, implemented brain rendering and creature selection (broken for now)

This commit is contained in:
Enrico Fasoli 2015-07-06 16:40:23 +02:00
parent 75146638bf
commit 124087d381
6 changed files with 980 additions and 858 deletions

View File

@ -7,13 +7,14 @@ 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.Creature;
import logic.Element; import logic.Element;
import logic.World; import logic.World;
public class Game extends ApplicationAdapter { public class Game extends ApplicationAdapter {
private static Game game; private static Game game;
ShapeRenderer shaper; ShapeRenderer renderer, overlayRenderer;
private World world; private World world;
private float cameraSpeed = 15; private float cameraSpeed = 15;
private BitmapFont font; private BitmapFont font;
@ -22,8 +23,10 @@ public class Game extends ApplicationAdapter {
@Override @Override
public void create() { public void create() {
game = this; game = this;
shaper = new ShapeRenderer(); renderer = new ShapeRenderer();
shaper.setAutoShapeType(true); renderer.setAutoShapeType(true);
overlayRenderer = new ShapeRenderer();
overlayRenderer.setAutoShapeType(true);
font = new BitmapFont(); font = new BitmapFont();
Thread worldThread = new Thread(world); Thread worldThread = new Thread(world);
worldThread.setName("Worker"); worldThread.setName("Worker");
@ -42,22 +45,22 @@ public class Game extends ApplicationAdapter {
world.newGen(false); world.newGen(false);
} }
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) { if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
shaper.translate(-cameraSpeed, 0, 0); renderer.translate(-cameraSpeed, 0, 0);
} }
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) { if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
shaper.translate(cameraSpeed, 0, 0); renderer.translate(cameraSpeed, 0, 0);
} }
if (Gdx.input.isKeyPressed(Input.Keys.UP)) { if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
shaper.translate(0, -cameraSpeed, 0); renderer.translate(0, -cameraSpeed, 0);
} }
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) { if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
shaper.translate(0, cameraSpeed, 0); renderer.translate(0, cameraSpeed, 0);
} }
if (Gdx.input.isKeyJustPressed(Input.Keys.PLUS)) { if (Gdx.input.isKeyJustPressed(Input.Keys.PLUS)) {
shaper.scale(0.5f, 0.5f, 1); renderer.scale(0.5f, 0.5f, 1);
} }
if (Gdx.input.isKeyJustPressed(Input.Keys.MINUS)) { if (Gdx.input.isKeyJustPressed(Input.Keys.MINUS)) {
shaper.scale(1.5f, 1.5f, 1); renderer.scale(1.5f, 1.5f, 1);
} }
if (Gdx.input.isKeyJustPressed(Input.Keys.P)) { if (Gdx.input.isKeyJustPressed(Input.Keys.P)) {
paused = !paused; paused = !paused;
@ -69,15 +72,23 @@ public class Game extends ApplicationAdapter {
world.setFpsLimit(60); world.setFpsLimit(60);
} }
} }
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
renderer.translate(Gdx.input.getDeltaX(), Gdx.input.getDeltaY() * -1, 0);
}
/*
// Broken for now
if(Gdx.input.isButtonPressed(Input.Buttons.LEFT)){
// TODO: project coordinates to world
world.selectCreatureAt(Gdx.input.getX(), Gdx.input.getY());
}*/
// Draw // Draw
Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shaper.setColor(1, 1, 1, 1); renderer.begin(ShapeRenderer.ShapeType.Line);
shaper.begin(ShapeRenderer.ShapeType.Line);
try { try {
for (Element e : world.getElements()) { for (Element e : world.getElements()) {
try { try {
e.render(shaper); e.render(renderer);
} catch (ArrayIndexOutOfBoundsException ex) { } catch (ArrayIndexOutOfBoundsException ex) {
// No idea why it happens, but it's rendering so meh // No idea why it happens, but it's rendering so meh
//Log.log(Log.ERROR, ex+""); //Log.log(Log.ERROR, ex+"");
@ -85,10 +96,21 @@ public class Game extends ApplicationAdapter {
} }
} catch (ConcurrentModificationException ex) { } catch (ConcurrentModificationException ex) {
} }
shaper.setColor(0.3f, 0.3f, 0.3f, 1); if (world.getSelectedCreature() != null) {
// There is a selection
Creature c = world.getSelectedCreature();
renderer.setColor(1, 1, 1, 1);
// 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 // draw borders
shaper.rect(0, 0, world.getWidth(), world.getHeight()); renderer.rect(0, 0, world.getWidth(), world.getHeight());
shaper.end(); renderer.end();
} }
public World getWorld() { public World getWorld() {

View File

@ -24,7 +24,7 @@ public class Vegetable extends Element {
@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);
} }
} }

View File

@ -9,6 +9,7 @@ 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.ConcurrentModificationException;
import java.util.Date; import java.util.Date;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -22,6 +23,7 @@ public class World implements Runnable {
private final int width, height, nPlants, creatPerGen; private final int width, height, nPlants, creatPerGen;
private int generation = 1; private int generation = 1;
private int fpsLimit = 60, fps = 0; private int fpsLimit = 60, fps = 0;
private Creature selected;
private final ArrayList<Element> elements; private final ArrayList<Element> elements;
private final ArrayList<Element> toAdd; private final ArrayList<Element> toAdd;
private final ArrayList<Creature> creatures; private final ArrayList<Creature> creatures;
@ -42,6 +44,7 @@ public class World implements Runnable {
deadPlants = new ArrayList(); deadPlants = new ArrayList();
graveyard = new ArrayList(); graveyard = new ArrayList();
fpsListeners = new ArrayList(); fpsListeners = new ArrayList();
selected = null;
newGen(true); newGen(true);
} }
@ -96,6 +99,10 @@ public class World implements Runnable {
} }
toAdd.clear(); toAdd.clear();
elements.removeAll(graveyard); elements.removeAll(graveyard);
if (selected != null && graveyard.contains(selected)) {
selected = null;
Log.log(Log.INFO, "Cleared selection");
}
elements.removeAll(deadPlants); elements.removeAll(deadPlants);
plants.removeAll(deadPlants); plants.removeAll(deadPlants);
deadPlants.clear(); deadPlants.clear();
@ -116,6 +123,10 @@ public class World implements Runnable {
elements.removeAll(creatures); elements.removeAll(creatures);
graveyard.addAll(creatures); graveyard.addAll(creatures);
creatures.clear(); creatures.clear();
if (selected != null) {
selected = null;
Log.log(Log.INFO, "Cleared selection");
}
Comparator creatureComp = new Comparator<Creature>() { Comparator creatureComp = new Comparator<Creature>() {
@Override @Override
@ -212,6 +223,19 @@ public class World implements Runnable {
public abstract void fpsChanged(int newValue); public abstract void fpsChanged(int newValue);
} }
public void selectCreatureAt(int x, int y) {
selected = null; // Clear selection
try {
for (Creature c : creatures) {
if (c.overlaps(x, y)) {
selected = c;
Log.log(Log.INFO, "Selected a creature");
}
}
} catch (ConcurrentModificationException ex) {
}
}
private void spawnVegetable() { private void spawnVegetable() {
spawn(false, null); spawn(false, null);
} }
@ -276,4 +300,12 @@ public class World implements Runnable {
return fps; return fps;
} }
public Creature getSelectedCreature() {
return selected;
}
public void selectCreature(Creature selected) {
this.selected = selected;
}
} }

View File

@ -1,5 +1,6 @@
package logic.neural; package logic.neural;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.mygdx.game.Log; import com.mygdx.game.Log;
/** /**
@ -81,6 +82,39 @@ public class Brain {
} }
} }
/**
* Draw this brain's status.
*
* @param s the ShapeRenderer to use for the drawing
*/
public void render(ShapeRenderer s) {
s.set(ShapeRenderer.ShapeType.Filled);
int neuronHeight = 0;
for (Neuron[] ns : neurons) {
if (ns.length > neuronHeight) {
neuronHeight = ns.length;
}
}
s.rect(0, 0, neurons.length * 50, neuronHeight * 30);
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
float nr = neurons[i][j].compute();
// Draw neuron links
float[] links = neurons[i][j].getInputs();
for (int f = 0; f < links.length; f++) {
s.setColor(links[f], links[f], links[f], 1);
s.line(i * 50, j * 30, (i - 1) * 50, f * 30);
}
// Draw neuron
s.setColor(1 - nr, nr, 0, 1);
s.set(ShapeRenderer.ShapeType.Filled);
s.circle(i * 50, j * 30, 15);
}
}
}
/** /**
* Give some input to the brain * Give some input to the brain
* *

View File

@ -55,11 +55,12 @@ public class Neuron {
if (isInputNeuron) { if (isInputNeuron) {
return output; return output;
} }
if(cache.hasCachedOutput()) return cache.getCachedOutput();
float a = bias * -1; // activation float a = bias * -1; // activation
for (int i = 0; i < weights.length; i++) { for (int i = 0; i < weights.length; i++) {
if (cache.has(i)) { if (cache.has(i)) {
try { try {
return cache.get(i); a += cache.get(i);
} catch (Exception ex) { } catch (Exception ex) {
// This should never happen // This should never happen
Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(Neuron.class.getName()).log(Level.SEVERE, null, ex);
@ -82,6 +83,23 @@ public class Neuron {
return mutatedWeights; return mutatedWeights;
} }
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) { public void setOutput(float output) {
isInputNeuron = true; isInputNeuron = true;
this.output = output; this.output = output;

View File

@ -8,6 +8,8 @@ package logic.neural;
public class NeuronCache { public class NeuronCache {
private float[] cache; private float[] cache;
private float cachedOutput;
private boolean cachedOutputValid;
private boolean[] validity; private boolean[] validity;
/** /**
@ -57,6 +59,18 @@ public class NeuronCache {
return validity[index]; return validity[index];
} }
public float getCachedOutput() {
return cachedOutput;
}
public boolean hasCachedOutput() {
return cachedOutputValid;
}
public void setCachedOutput(float cachedOutput) {
this.cachedOutput = cachedOutput;
}
/** /**
* Clears cache. * Clears cache.
*/ */
@ -64,5 +78,7 @@ public class NeuronCache {
for (int i = 0; i < cache.length; i++) { for (int i = 0; i < cache.length; i++) {
validity[i] = false; validity[i] = false;
} }
cachedOutputValid = false;
cachedOutput = 0;
} }
} }