1
0
mirror of https://github.com/fazo96/AIrium.git synced 2025-01-24 11:44:21 +01:00

Fix some bugs

This commit is contained in:
Enrico Fasoli 2015-07-06 20:23:42 +02:00
parent 7ee5c502c4
commit 61454f7979
4 changed files with 380 additions and 332 deletions

View File

@ -42,7 +42,7 @@ public class Game extends ApplicationAdapter {
public void render() { public void render() {
// Controls // Controls
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) { if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) {
world.newGen(false); world.launchNewGen();
} }
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) { if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
renderer.translate(-cameraSpeed, 0, 0); renderer.translate(-cameraSpeed, 0, 0);

View File

@ -1,326 +1,361 @@
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;
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;
import logic.neural.Brain; import logic.neural.Brain;
/** /**
* A (hopefully) smart biological creature. * A (hopefully) smart biological creature.
* *
* @author fazo * @author fazo
*/ */
public class Creature extends Element { public class Creature extends Element implements Runnable {
public static final int default_radius = 20, maxHp = 100; public static final int default_radius = 20, maxHp = 100;
public static final float max_speed = 3, max_beak = default_radius / 4; public static final float max_speed = 3, max_beak = default_radius / 4;
private Brain brain; private Brain brain;
private float dir, hp, prevHp, speed, sightRange, fov, fitness, rotSpeed, beak; private float dir, hp, prevHp, speed, sightRange, fov, fitness, rotSpeed, beak;
private boolean eating = false, killing = false; private boolean eating = false, killing = false, workerDone = false;
private Sight[] sights; private Sight[] sights;
private Thread workerThread;
public Creature(float x, float y) {
super(x, y, default_radius); public Creature(float x, float y) {
dir = (float) (Math.random() * 2 * Math.PI); super(x, y, default_radius);
hp = maxHp; dir = (float) (Math.random() * 2 * Math.PI);
prevHp = hp; hp = maxHp;
speed = 0;//(float) Math.random() * 3; prevHp = hp;
rotSpeed = 0;//(float) Math.random() - 0.5f; speed = 0;//(float) Math.random() * 3;
sightRange = 100; rotSpeed = 0;//(float) Math.random() - 0.5f;
fov = (float) Math.PI / 2.5f; sightRange = 100;
fitness = 0; fov = (float) Math.PI / 2.5f;
brain = new Brain(10, 5, 2, 10); fitness = 0;
sights = new Sight[2]; brain = new Brain(9, 5, 2, 10);
} sights = new Sight[2];
}
@Override
public void update() { @Override
// apply hunger public void run() {
hp -= 0.5f; for (;;) {
prevHp = hp; if (workerDone) {
if (hp < 0) { // Dead try {
Game.get().getWorld().getGraveyard().add(this); Thread.sleep(1000);
Vegetable carcass = new Vegetable(getX(), getY()); } catch (InterruptedException ex) {
carcass.setSize(getSize()); }
//carcass.setDecayRate(0.01f); } else {
Game.get().getWorld().add(carcass); update();
return; workerDone = true;
} }
if (speed > max_speed) { }
speed = max_speed; }
}
if (speed < -max_speed) { @Override
speed = -max_speed; public void update() {
} // apply hunger
// apply speed hp -= 0.5f;
float xMul = (float) Math.cos(dir), yMul = (float) Math.sin(dir); prevHp = hp;
move(xMul * speed, yMul * speed); if (hp < 0) { // Dead
if (getX() < 0) { Game.get().getWorld().getGraveyard().add(this);
setX(Game.get().getWorld().getWidth() + getX()); Vegetable carcass = new Vegetable(getX(), getY());
} carcass.setSize(getSize());
if (getY() < 0) { //carcass.setDecayRate(0.01f);
setY(Game.get().getWorld().getHeight() + getY()); Game.get().getWorld().add(carcass);
} return;
if (getX() > Game.get().getWorld().getWidth()) { }
setX(getX() - Game.get().getWorld().getWidth()); if (speed > max_speed) {
} speed = max_speed;
if (getY() > Game.get().getWorld().getHeight()) { }
setY(getY() - Game.get().getWorld().getHeight()); if (speed < -max_speed) {
} speed = -max_speed;
dir += rotSpeed; }
//fitness -= 0.1; // apply speed
if (dir > 2 * Math.PI) { float xMul = (float) Math.cos(dir), yMul = (float) Math.sin(dir);
dir -= 2 * Math.PI; move(xMul * speed, yMul * speed);
} if (getX() < 0) {
if (dir < 0) { setX(Game.get().getWorld().getWidth() + getX());
dir += 2 * Math.PI; }
} if (getY() < 0) {
// read from sensors and interact with world setY(Game.get().getWorld().getHeight() + getY());
sights = interactWithWorld(); }
// feed data to brain if (getX() > Game.get().getWorld().getWidth()) {
float[] values = new float[brain.getNeurons()[0].length]; setX(getX() - Game.get().getWorld().getWidth());
// VIEW: PLANTS }
// 0: sight(v): see food? if (getY() > Game.get().getWorld().getHeight()) {
// 1: sight(v): distance setY(getY() - Game.get().getWorld().getHeight());
// 2: sight(v): angle }
// 3: sight(v): size dir += rotSpeed;
// VIEW: CREATS //fitness -= 0.1;
// 4: sight(c): see food? if (dir > 2 * Math.PI) {
// 5: sight(c): distance dir -= 2 * Math.PI;
// 6: sight(c): angle }
// 8: sight(c): hunger if (dir < 0) {
// 7: sight(c): beak dir += 2 * Math.PI;
// OTHER: }
// 9: food sensor // read from sensors and interact with world
int viewSensors = 4; sights = interactWithWorld();
for (int i = 0; i < sights.length; i++) { // feed data to brain
int mul = i * viewSensors; float[] values = new float[brain.getNeurons()[0].length];
if (sights[i] == null || sights[i].getElement() == null) { // VIEW: PLANTS
// See nothing // 0: sight(v): see food?
values[0 + mul] = 0; // 1: sight(v): distance
values[1 + mul] = 0; // 2: sight(v): angle
values[2 + mul] = 0; // 3: sight(v): size
values[3 + mul] = 0; // VIEW: CREATS
} else { // 4: sight(c): see food?
// See something // 5: sight(c): distance
values[1 + mul] = sights[i].getDistance() / sightRange; // 6: sight(c): angle
values[2 + mul] = sights[i].getAngle(); // 8: sight(c): hunger
if (sights[i].getElement() instanceof Vegetable) { // 7: sight(c): beak
values[0 + mul] = 1f; // OTHER:
values[3 + mul] = sights[i].getElement().getSize() / default_radius; // 8: food sensor
} else { int viewSensors = 4;
values[0 + mul] = 1f; for (int i = 0; i < sights.length; i++) {
values[3 + mul] = maxHp - ((Creature) sights[i].getElement()).getHp() / maxHp; int mul = i * viewSensors;
values[3 + mul] = ((Creature) sights[i].getElement()).getBeak() / max_beak; if (sights[i] == null || sights[i].getElement() == null) {
} // See nothing
} values[0 + mul] = 0;
} values[1 + mul] = 0;
values[9] = eating ? 1 : 0; values[2 + mul] = 0;
// compute behavior values[3 + mul] = 0;
float[] actions = null; } else {
try { // See something
actions = brain.compute(values); values[1 + mul] = sights[i].getDistance() / sightRange;
} catch (Exception ex) { values[2 + mul] = sights[i].getAngle();
// Should not happen if (sights[i].getElement() instanceof Vegetable) {
Logger.getLogger(Creature.class.getName()).log(Level.SEVERE, null, ex); values[0 + mul] = 1f;
} values[3 + mul] = sights[i].getElement().getSize() / default_radius;
Log.log(Log.DEBUG, "Accel: " + actions[0] + " RotClock: " + actions[1] + " RotAntiClock: " + actions[2] + " Beak: " + actions[3]); } else {
speed = (actions[0] * 2 - actions[4] / 2) * max_speed; values[0 + mul] = 1f;
rotSpeed = actions[1] - actions[2]; values[3 + mul] = maxHp - ((Creature) sights[i].getElement()).getHp() / maxHp;
beak = actions[3] * max_beak; values[3 + mul] = ((Creature) sights[i].getElement()).getBeak() / max_beak;
if (beak > max_beak) { }
beak = max_beak; }
} else if (beak < 0) { }
beak = 0; values[8] = eating || killing ? 1 : 0;
} System.out.println(values[8]);
} // compute behavior
float[] actions = null;
@Override try {
public void render(ShapeRenderer s) { actions = brain.compute(values);
// Body } catch (Exception ex) {
s.setColor(1 - (hp / maxHp), hp / maxHp, 0, 1); // Should not happen
s.circle(getX(), getY(), getSize()); Logger.getLogger(Creature.class.getName()).log(Level.SEVERE, null, ex);
// Vision }
double relX = Math.cos(dir), relY = Math.sin(dir); Log.log(Log.DEBUG, "Accel: " + actions[0] + " RotClock: " + actions[1] + " RotAntiClock: " + actions[2] + " Beak: " + actions[3]);
float c = 0; speed = (actions[0] * 2 - actions[4] / 2) * max_speed;
float eyeX = (float) (relX * getSize() * 0.6f), eyeY = (float) (relY * getSize() * 0.6f); rotSpeed = actions[1] - actions[2];
for (Sight sight : sights) { beak = actions[3] * max_beak;
if (sight != null) { if (beak > max_beak) {
c = sight.getDistance() / sightRange * 2 + sightRange; beak = max_beak;
} else { } else if (beak < 0) {
} beak = 0;
if (sight != null) { }
if (sight.getElement() instanceof Creature) { }
s.setColor(c, 0, 0, 1);
} else if (sight.getElement() instanceof Vegetable) { @Override
s.setColor(0, c, 0, 1); public void render(ShapeRenderer s) {
} // Body
s.line(eyeX + getX(), getY() + eyeY, sight.getElement().getX(), sight.getElement().getY()); s.setColor(1 - (hp / maxHp), hp / maxHp, 0, 1);
} s.circle(getX(), getY(), getSize());
} // Vision
if (sights[0] == null && sights[1] == null) { double relX = Math.cos(dir), relY = Math.sin(dir);
s.setColor(1, 1, 1, 1); float c = 0;
} else { float eyeX = (float) (relX * getSize() * 0.6f), eyeY = (float) (relY * getSize() * 0.6f);
s.setColor(sights[1] == null ? 0 : 1, sights[0] == null ? 0 : 1, 0, 1); for (Sight sight : sights) {
} if (sight != null) {
s.circle(getX() + eyeX, getY() + eyeY, 3); c = sight.getDistance() / sightRange * 2 + sightRange;
//FOV } else {
float degrees = fov * 180f / (float) Math.PI; }
float orient = dir * 180f / (float) Math.PI - degrees / 2; if (sight != null) {
s.setColor(0.3f, 0.3f, 0.3f, 1); if (sight.getElement() instanceof Creature) {
s.arc((float) eyeX + getX(), (float) eyeY + getY(), sightRange, orient, degrees); s.setColor(c, 0, 0, 1);
if (hp < prevHp) { } else if (sight.getElement() instanceof Vegetable) {
// Damage mark s.setColor(0, c, 0, 1);
s.set(ShapeRenderer.ShapeType.Filled); }
s.setColor(1, 0, 0, 1); s.line(eyeX + getX(), getY() + eyeY, sight.getElement().getX(), sight.getElement().getY());
s.circle(getX(), getY(), 5); }
} else if (killing || eating) { }
// Heal mark if (sights[0] == null && sights[1] == null) {
s.set(ShapeRenderer.ShapeType.Filled); s.setColor(1, 1, 1, 1);
s.setColor(0, 1, 0, 1); } else {
s.circle(getX(), getY(), 5); s.setColor(sights[1] == null ? 0 : 1, sights[0] == null ? 0 : 1, 0, 1);
} }
s.set(ShapeRenderer.ShapeType.Line); s.circle(getX() + eyeX, getY() + eyeY, 3);
// Beak //FOV
s.setColor(beak / max_beak, 1 - beak / max_beak, 0, 1); float degrees = fov * 180f / (float) Math.PI;
s.line((float) (relX * getSize() * 0.8f + getX()), (float) (relY * getSize() * 0.8f + getY()), (float) (relX * getSize() * (1.5f + beak / max_beak) + getX()), (float) (relY * getSize() * (1.5f + beak / max_beak) + getY())); float orient = dir * 180f / (float) Math.PI - degrees / 2;
} s.setColor(0.3f, 0.3f, 0.3f, 1);
s.arc((float) eyeX + getX(), (float) eyeY + getY(), sightRange, orient, degrees);
public Sight[] interactWithWorld() { if (hp < prevHp) {
Sight[] newSights = new Sight[2]; // Damage mark
// Try to see plant s.set(ShapeRenderer.ShapeType.Filled);
Element seen = null; s.setColor(1, 0, 0, 1);
float dist = 0, angle = 0, ndir = dir - (float) Math.PI; s.circle(getX(), getY(), 5);
killing = false; } else if (killing || eating) {
eating = false; // Heal mark
for (Element e : Game.get().getWorld().getPlants()) { s.set(ShapeRenderer.ShapeType.Filled);
float tempDist = distanceFrom(e); s.setColor(0, 1, 0, 1);
if (tempDist > sightRange) { s.circle(getX(), getY(), 5);
continue; }
} s.set(ShapeRenderer.ShapeType.Line);
//Log.log(Log.DEBUG,"TempDist "+tempDist+" SightRange "+sightRange); // Beak
float relAngle = (float) (Math.atan2(getY() - e.getY(), getX() - e.getX())); s.setColor(beak / max_beak, 1 - beak / max_beak, 0, 1);
if (tempDist < dist || seen == null) { s.line((float) (relX * getSize() * 0.8f + getX()), (float) (relY * getSize() * 0.8f + getY()), (float) (relX * getSize() * (1.5f + beak / max_beak) + getX()), (float) (relY * getSize() * (1.5f + beak / max_beak) + getY()));
// Check if Visible }
if (Math.abs(relAngle - ndir) < fov) {
// Visible public Sight[] interactWithWorld() {
seen = e; Sight[] newSights = new Sight[2];
angle = relAngle - ndir; // Try to see plant
dist = tempDist; Element seen = null;
} float dist = 0, angle = 0, ndir = dir - (float) Math.PI;
//Log.log(Log.DEBUG,"RelAngle "+relAngle+" Dir "+ndir); eating = false;
} for (Element e : Game.get().getWorld().getPlants()) {
// Check if eatable float tempDist = distanceFrom(e);
if (tempDist < 0) { if (tempDist > sightRange) {
// Eat continue;
eating = true; }
e.setSize(e.getSize() - 0.1f); //Log.log(Log.DEBUG,"TempDist "+tempDist+" SightRange "+sightRange);
if (e.getSize() == 0) { float relAngle = (float) (Math.atan2(getY() - e.getY(), getX() - e.getX()));
e.setSize(0); if (tempDist < dist || seen == null) {
} // Check if Visible
hp++; if (Math.abs(relAngle - ndir) < fov) {
fitness++; // Visible
if (hp > maxHp) { seen = e;
hp = maxHp; angle = relAngle - ndir;
} dist = tempDist;
} }
} //Log.log(Log.DEBUG,"RelAngle "+relAngle+" Dir "+ndir);
if (seen != null) { }
newSights[0] = new Sight(seen, dist, angle); // Check if eatable
} if (tempDist < 0) {
// Try to see creature // Eat
seen = null; eating = true;
dist = 0; e.setSize(e.getSize() - 0.1f);
angle = 0; if (e.getSize() == 0) {
ndir = dir - (float) Math.PI; e.setSize(0);
killing = false; }
eating = false; hp++;
for (Element e : Game.get().getWorld().getCreatures()) { fitness++;
if (e == this) { if (hp > maxHp) {
continue; hp = maxHp;
} }
float tempDist = distanceFrom(e); }
if (tempDist > sightRange) { }
continue; if (seen != null) {
} newSights[0] = new Sight(seen, dist, angle);
//Log.log(Log.DEBUG,"TempDist "+tempDist+" SightRange "+sightRange); }
float relAngle = (float) (Math.atan2(getY() - e.getY(), getX() - e.getX())); // Try to see creature
if (tempDist < dist || seen == null) { seen = null;
// Check if Visible dist = 0;
if (Math.abs(relAngle - ndir) < fov) { angle = 0;
// Visible ndir = dir - (float) Math.PI;
seen = e; killing = false;
angle = relAngle - ndir; for (Element e : Game.get().getWorld().getCreatures()) {
dist = tempDist; if (e == this) {
} continue;
//Log.log(Log.DEBUG,"RelAngle "+relAngle+" Dir "+ndir); }
} float tempDist = distanceFrom(e);
// Check if attackable if (tempDist > sightRange) {
if (e instanceof Creature && beak > 5 && tempDist < beak * 1.5f && Math.abs(relAngle - ndir) < (float) Math.PI / 10f) { continue;
// Attacking! }
hp++; //Log.log(Log.DEBUG,"TempDist "+tempDist+" SightRange "+sightRange);
fitness++; float relAngle = (float) (Math.atan2(getY() - e.getY(), getX() - e.getX()));
if (hp > maxHp) { if (tempDist < dist || seen == null) {
hp = maxHp; // Check if Visible
} if (Math.abs(relAngle - ndir) < fov) {
killing = true; // Visible
Creature c = (Creature) e; seen = e;
c.setHp(c.getHp() - 0.2f); angle = relAngle - ndir;
} dist = tempDist;
} }
if (seen != null) { //Log.log(Log.DEBUG,"RelAngle "+relAngle+" Dir "+ndir);
newSights[1] = new Sight(seen, dist, angle); }
} // Check if attackable
return newSights; if (e instanceof Creature && beak > 5 && tempDist < beak * 1.5f && Math.abs(relAngle - ndir) < (float) Math.PI / 10f) {
} // Attacking!
hp++;
public void eat() { fitness++;
eating = false; if (hp > maxHp) {
for (Element e : Game.get().getWorld().getPlants()) { hp = maxHp;
if (overlaps(e)) { }
eating = true; killing = true;
e.setSize(e.getSize() - 0.1f); Creature c = (Creature) e;
if (e.getSize() == 0) { c.setHp(c.getHp() - 0.2f);
e.setSize(0); }
} }
hp++; if (seen != null) {
fitness++; newSights[1] = new Sight(seen, dist, angle);
if (hp > maxHp) { }
hp = maxHp; return newSights;
} }
}
} public void eat() {
} eating = false;
for (Element e : Game.get().getWorld().getPlants()) {
public Brain getBrain() { if (overlaps(e)) {
return brain; eating = true;
} e.setSize(e.getSize() - 0.1f);
if (e.getSize() == 0) {
public void setDirection(float dir) { e.setSize(0);
this.dir = dir; }
} hp++;
fitness++;
public float getFitness() { if (hp > maxHp) {
return fitness; hp = maxHp;
} }
}
public void reset() { }
fitness = 0; }
hp = maxHp;
} /**
* Check if the Worker thread has finished its current iteration
public float getBeak() { *
return beak; * @return true if worker thread has finished its current iteration
} */
public boolean isWorkerDone() {
public float getHp() { return workerDone;
return hp; }
}
/**
public void setHp(float hp) { * Command the Worker thread to start another iteration.
this.hp = hp; */
} public void startWorker() {
} workerDone = false;
if (workerThread == null) {
workerThread = new Thread(this);
workerThread.start();
}
}
public Brain getBrain() {
return brain;
}
public void setDirection(float dir) {
this.dir = dir;
}
public float getFitness() {
return fitness;
}
public void reset() {
fitness = 0;
hp = maxHp;
}
public float getBeak() {
return beak;
}
public float getHp() {
return hp;
}
public void setHp(float hp) {
this.hp = hp;
}
}

View File

@ -23,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 boolean multithreading = false, cmdLaunchNewGen = false;
private int fpsLimit = 60, fps = 0; private int fpsLimit = 60, fps = 0;
private Creature selected; private Creature selected;
private final ArrayList<Element> elements; private final ArrayList<Element> elements;
@ -108,6 +109,10 @@ public class World implements Runnable {
if (creatures.removeAll(graveyard)) { if (creatures.removeAll(graveyard)) {
fire(Listener.CREATURE_LIST_CHANGED); fire(Listener.CREATURE_LIST_CHANGED);
} }
if (cmdLaunchNewGen) {
newGen(false);
cmdLaunchNewGen = false;
}
if (creatures.isEmpty()) { if (creatures.isEmpty()) {
// All dead, next gen // All dead, next gen
newGen(false); newGen(false);
@ -120,7 +125,7 @@ public class World implements Runnable {
} }
} }
public void newGen(boolean restart) { private void newGen(boolean restart) {
elements.removeAll(creatures); elements.removeAll(creatures);
graveyard.addAll(creatures); graveyard.addAll(creatures);
creatures.clear(); creatures.clear();
@ -233,7 +238,7 @@ public class World implements Runnable {
} }
} }
public void fire(int eventCode) { private void fire(int eventCode) {
for (Listener f : listeners) { for (Listener f : listeners) {
f.on(eventCode); f.on(eventCode);
} }
@ -311,4 +316,15 @@ public class World implements Runnable {
this.selected = selected; this.selected = selected;
} }
public boolean isMultithreading() {
return multithreading;
}
public void setMultithreading(boolean multithreading) {
this.multithreading = multithreading;
}
public void launchNewGen() {
cmdLaunchNewGen = true;
}
} }

View File

@ -11,10 +11,7 @@ import com.mygdx.game.Game;
import com.mygdx.game.Listener; import com.mygdx.game.Listener;
import com.mygdx.game.Log; import com.mygdx.game.Log;
import com.mygdx.game.Log.LogListener; import com.mygdx.game.Log.LogListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import logic.World;
/** /**
* *