1
0
mirror of https://github.com/fazo96/AIrium.git synced 2025-01-10 09:34:20 +01:00

improved API and docs

This commit is contained in:
Enrico Fasoli 2015-08-18 21:59:58 +02:00
parent 4528f787cb
commit 7650ff44eb
6 changed files with 111 additions and 43 deletions

View File

@ -9,9 +9,16 @@ package com.mygdx.game;
*
* @author Fazo
*/
public interface Listener {
public abstract class Listener {
public static int FPS_CHANGED = 0, CREATURE_LIST_CHANGED = 1, PAUSED_OR_RESUMED = 2;
public void on(int event);
public void pollAndHandleEvents() {
if(Game.get() == null || Game.get().getWorld() == null) return;
while(Game.get().getWorld().getEventQueue().size() > 0) {
on(Game.get().getWorld().getEventQueue().poll());
}
}
public abstract void on(int event);
}

View File

@ -10,9 +10,12 @@ import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import logic.creatures.Beak;
import logic.creatures.Eye;
import logic.creatures.Movement;
import logic.creatures.Torso;
@ -34,6 +37,7 @@ public class World implements Runnable {
private Map<String, Float> options;
private long ticksSinceGenStart = 0, maximumTicksPerGen = 0;
private Creature selected;
private Queue<Integer> events = new LinkedList<>();
private final Comparator creatureComp;
private final ArrayList<Element> elements;
private final ArrayList<Element> toAdd;
@ -41,7 +45,6 @@ public class World implements Runnable {
private final ArrayList<Creature> graveyard;
private final ArrayList<Vegetable> plants;
private final ArrayList<Vegetable> deadPlants;
private final ArrayList<Listener> listeners;
/**
* Create a new World. Can be customized with given options.
@ -56,13 +59,13 @@ public class World implements Runnable {
this.options = options;
}
reloadOptions();
events = new LinkedList<>();
elements = new ArrayList();
creatures = new ArrayList();
toAdd = new ArrayList();
plants = new ArrayList();
deadPlants = new ArrayList();
graveyard = new ArrayList();
listeners = new ArrayList();
selected = null;
creatureComp = new Comparator<Creature>() {
@ -333,7 +336,15 @@ public class World implements Runnable {
} while (overlaps && i++ < 20);
if (isCreature) {
Log.log(Log.DEBUG, "New Creat: " + x + " " + y);
Creature c = new Creature(x, y);
Creature c = new Creature(x, y) {
@Override
public void buildBody() {
addBodyPart(new Beak(0, this));
addBodyPart(new Eye(5, 0, this));
addBodyPart(new Movement(this));
}
};
if (brainMap != null) {
c.getBrain().remap(brainMap);
}
@ -380,11 +391,13 @@ public class World implements Runnable {
*/
public void fire(int eventCode) {
Log.log(Log.DEBUG, "Firing Event. Code: " + eventCode);
for (Listener f : listeners) {
f.on(eventCode);
}
events.add(eventCode);
}
public Queue<Integer> getEventQueue(){
return events;
}
public void spawnVegetable() {
spawn(false, null);
}
@ -409,10 +422,6 @@ public class World implements Runnable {
return generation;
}
public void addListener(Listener f) {
listeners.add(f);
}
public void add(Element e) {
toAdd.add(e);
}

View File

@ -4,6 +4,8 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import logic.Element;
/**
* A body part. Used in creatures. Extend this class to create custom body
* parts.
*
* @author fazo
*/
@ -14,6 +16,16 @@ public abstract class BodyPart {
protected float outputs[];
protected Creature creature;
/**
* Create an instance of a Body Part.
*
* @param inputNeuronsUsed how many input neurons it'll need
* @param outputNeuronsUsed how many output neurons it'll need
* @param angle the angle relative to the center of the creature
* @param distFromCenter how distance from the center of the creature is
* this body part
* @param creature the creature that owns this body part
*/
public BodyPart(int inputNeuronsUsed, int outputNeuronsUsed, float angle, float distFromCenter, Creature creature) {
this.inputNeuronsUsed = inputNeuronsUsed;
this.angle = angle;
@ -23,14 +35,18 @@ public abstract class BodyPart {
}
/**
* Prepare data to be sent to the brain
* Prepare data to be sent to the brain. This is called once every frame,
* before the interactions with other elements.
*
* @return the data to send to the brain, must be inputNeuronsUsed long
*/
public abstract float[] act();
/**
* Interact with another element
* Interact with another element. This will be called every time the body
* part has a chance to interact with another element. act() will be called
* once every frame, before all the interactions. readFromBrain will be
* called once every frame, after all the interactions.
*
* @param e the Element (creature or plant)
* @param distance the distance
@ -39,29 +55,56 @@ public abstract class BodyPart {
public abstract void interactWithElement(Element e, float distance, float relAngle);
/**
* Receive some data from the brain
* Receive some data from the brain. This is called once every frame, after
* interactions with other elements.
*
* @param data the data received from the brain, will be outputNeuronsUsed
* long
*/
public abstract void readFromBrain(float data[]);
/**
* This will be called when the
*
* @param s the ShapeRenderer used to draw this body part.
* @param relX the X position of this bodypart relative to the center its
* creature
* @param relY the Y position of this bodypart relative to the center its
* creature
*/
protected abstract void draw(ShapeRenderer s, float relX, float relY);
public void render(ShapeRenderer s) {
/**
* Prepares data and calls draw
*
* @param s the ShapeRenderer used to draw this body part.
*/
public final void render(ShapeRenderer s) {
double relX = Math.cos(creature.getDirection() + angle) * creature.getTorso().getRadius() * distFromCenter;
double relY = Math.sin(creature.getDirection() + angle) * creature.getTorso().getRadius() * distFromCenter;
draw(s, (float) relX, (float) relY);
}
/**
*
* @return how many input neurons are used by this body part
*/
public int getInputNeuronsUsed() {
return inputNeuronsUsed;
}
/**
*
* @return how many output neurons are used by this body part
*/
public int getOutputNeuronsUsed() {
return outputs.length;
}
/**
*
* @return the angle of this bodypart relative to the center of the creature
*/
public float getAngle() {
return angle;
}

View File

@ -10,11 +10,14 @@ import logic.Vegetable;
import logic.neural.Brain;
/**
* A (hopefully) smart biological creature in the simulated world.
* A (hopefully) smart biological creature in the simulated world. It is
* initialized with a set of body parts. Every creature has a brain which gets
* automatically wired to the body parts. Only creatures with matching brain
* structures can breed for now.
*
* @author fazo
*/
public class Creature extends Element implements Runnable {
public abstract class Creature extends Element implements Runnable {
public static int brain_hidden_layers = 2, brain_hidden_neurons = 10;
public static float corpseDecayRate = 0, pointsForEatingPlants = 1f, pointsForAttacking = 2f, hpForAttacking = 1f, hpForEatingPlants = 1f;
@ -25,7 +28,6 @@ public class Creature extends Element implements Runnable {
private final ArrayList<BodyPart> bodyParts;
private float dir, fitness = 0;
private boolean workerDone = false, killWorker = false;
private Sight[] sights;
private Thread workerThread;
/**
@ -39,11 +41,8 @@ public class Creature extends Element implements Runnable {
dir = (float) (Math.random() * 2 * Math.PI);
bodyParts = new ArrayList<BodyPart>();
bodyParts.add(torso = new Torso(this));
bodyParts.add(new Beak(0, this));
bodyParts.add(new Eye(5, 0, this));
bodyParts.add(new Movement(this));
buildBody();
brain = new Brain(howManyInputNeurons(), howManyOutputNeurons(), brain_hidden_layers, brain_hidden_neurons);
sights = new Sight[2];
}
@Override
@ -196,13 +195,24 @@ public class Creature extends Element implements Runnable {
for (BodyPart b : bodyParts) {
if (b instanceof Beak) {
beaks++;
danger += (((Beak)b).getLength() - Beak.min_length) / Beak.max_length;
danger += (((Beak) b).getLength() - Beak.min_length) / Beak.max_length;
}
}
if(beaks == 0) return 0;
if (beaks == 0) {
return 0;
}
return danger / beaks;
}
/**
* Compose this creature's body using the addBodyPart function.
*/
public abstract void buildBody();
public void addBodyPart(BodyPart p) {
bodyParts.add(p);
}
public Brain getBrain() {
return brain;
}

View File

@ -115,7 +115,9 @@ public class Brain {
*/
public void input(float[] values) throws Exception {
if (values.length != neurons[0].length) {
throw new Exception("Not enough or too many inputs");
throw new Exception("Brain has " + neurons[0].length
+ " input neurons," + " but was supplied with "
+ values.length + " inputs");
}
for (int i = 0; i < values.length; i++) {
neurons[0][i].setOutput(values[i]);

View File

@ -41,13 +41,14 @@ import logic.World;
*
* @author fazo
*/
public class GUI extends javax.swing.JFrame implements LogListener, Listener {
public class GUI extends javax.swing.JFrame implements LogListener {
private Game game;
private World world;
private LwjglApplication app;
private boolean shouldUpdateGUI = false;
private final Thread guiUpdater;
private final Listener listener;
private Map<String, Float> options;
private boolean updatingSliders = false, updatingTable = false;
@ -59,6 +60,13 @@ public class GUI extends javax.swing.JFrame implements LogListener, Listener {
currentFpsLimit.setText("" + fpsLimitSlider.getValue());
setLocationRelativeTo(null); // Center the window
Log.addListener(this);
listener = new Listener() {
@Override
public void on(int event) {
updateGUI();
}
};
options = new HashMap<String, Float>();
world = new World(options);
updateSettingsUI();
@ -90,14 +98,10 @@ public class GUI extends javax.swing.JFrame implements LogListener, Listener {
@Override
public void run() {
for (;;) {
if (shouldUpdateGUI) {
updateGUI();
shouldUpdateGUI = false;
} else {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
}
listener.pollAndHandleEvents();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
}
@ -885,7 +889,6 @@ public class GUI extends javax.swing.JFrame implements LogListener, Listener {
app = new LwjglApplication(game = new Game(world), config);
startButton.setText("Restart");
pauseButton.setEnabled(true);
world.addListener(this);
setCreatureList();
}
updateGUI();
@ -900,12 +903,6 @@ public class GUI extends javax.swing.JFrame implements LogListener, Listener {
setScrollBarToTheBottom();
}
@Override
public void on(int event) {
shouldUpdateGUI = true;
guiUpdater.interrupt();
}
public void enableControlButtons(boolean yn) {
pauseButton.setEnabled(yn);
pauseMenuButton.setEnabled(yn);