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

added stuffffff and genetic algo

This commit is contained in:
Enrico Fasoli 2015-07-02 13:51:44 +02:00
parent 3d43a4ce68
commit 54e5dbb30f
6 changed files with 166 additions and 71 deletions

View File

@ -0,0 +1,51 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mygdx.game;
/**
*
* @author fazo
*/
public class Camera {
private int x, y, speed;
public Camera() {
x = 0;
y = 0;
speed = 5;
}
public void translate(int deltaX, int deltaY) {
x += deltaX;
y += deltaY;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
}

View File

@ -4,8 +4,6 @@ import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import logic.Element;
import logic.World;
@ -13,39 +11,49 @@ import logic.World;
public class Game extends ApplicationAdapter {
private static Game game;
SpriteBatch batch;
ShapeRenderer shaper;
Texture img;
private Camera camera;
private World world;
@Override
public void create() {
game = this;
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
world = new World(640, 480);
world = new World(1920, 1080);
shaper = new ShapeRenderer();
camera = new Camera();
//shaper.setAutoShapeType(true);
}
@Override
public void render() {
// Input
if(Gdx.input.isKeyJustPressed(Input.Keys.SPACE)){
// Controls
if (Gdx.input.isKeyJustPressed(Input.Keys.SPACE)) {
world.newGen(false);
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
camera.translate(-camera.getSpeed(), 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
camera.translate(1+camera.getSpeed(), 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
camera.translate(0, -camera.getSpeed());
}
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
camera.translate(0, camera.getSpeed());
}
// Update
world.update();
// Draw
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
/*batch.begin();
batch.draw(img, 0, 0);
batch.end();*/
shaper.setColor(1, 1, 1, 1);
shaper.begin(ShapeRenderer.ShapeType.Line);
//shaper.circle(640, 480, 100);
for(Element e: world.getElements()) e.render(shaper);
for (Element e : world.getElements()) {
e.render(shaper);
}
shaper.setColor(0.3f, 0.3f, 0.3f, 1);
shaper.rect(camera.getX(), camera.getY(), world.getWidth(), world.getHeight());
shaper.end();
}
@ -53,6 +61,10 @@ public class Game extends ApplicationAdapter {
return world;
}
public Camera getCamera() {
return camera;
}
public static Game get() {
return game;
}

View File

@ -3,6 +3,8 @@ package logic;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.mygdx.game.Game;
import com.mygdx.game.Log;
import java.util.logging.Level;
import java.util.logging.Logger;
import logic.neural.Brain;
/**
@ -16,8 +18,8 @@ public class Creature extends Element {
public static final float max_speed = 3;
private Brain brain;
private float dir, speed, sightRange, fov, fitness, rotSpeed;
private float hp;
private float dir, hp, speed, sightRange, fov, fitness, rotSpeed;
private boolean eating = false;
private Sight sight;
public Creature(float x, float y) {
@ -29,7 +31,7 @@ public class Creature extends Element {
sightRange = 60;
fov = (float) Math.PI / 1.5f;
fitness = 0;
brain = new Brain(3, 2, 2, 8);
brain = new Brain(4, 3, 1, 6);
}
@Override
@ -48,12 +50,18 @@ public class Creature extends Element {
// apply speed
float xMul = (float) Math.cos(dir), yMul = (float) Math.sin(dir);
move(xMul * speed, yMul * speed);
if(getX() < 0) setX(0);
if(getY() < 0) setX(0);
if(getX() > Game.get().getWorld().getWidth())
setX(Game.get().getWorld().getWidth());
if(getY() > Game.get().getWorld().getHeight())
setY(Game.get().getWorld().getHeight());
if (getX() < 0) {
setX(Game.get().getWorld().getWidth()+getX());
}
if (getY() < 0) {
setX(Game.get().getWorld().getHeight()+getY());
}
if (getX() > Game.get().getWorld().getWidth()) {
setX(getX()-Game.get().getWorld().getWidth());
}
if (getY() > Game.get().getWorld().getHeight()) {
setY(getY()-Game.get().getWorld().getHeight());
}
dir += rotSpeed;
// try eating
eat();
@ -66,29 +74,36 @@ public class Creature extends Element {
}
sight = look(); // take a look
// feed data to brain
float[] values = new float[3];
// 0: type of sight
float[] values = new float[4];
// 0: see food
// 1: distance
// 2: angle
if (sight == null) {
// 3: food sensor
if (sight == null || sight.getElement() == null) {
values[0] = 0;
values[1] = 1;
values[1] = 0;
values[2] = 0;
} else if (sight.getElement() instanceof Creature) {
values[0] = 1;
} else if (sight.getElement() instanceof Vegetable) {
values[1] = 1;
values[1] = sight.getDistance() / sightRange;
values[2] = sight.getAngle();
} else {
values[0] = 0.5f;
values[0] = 0f;
values[1] = sight.getDistance() / sightRange;
values[2] = sight.getAngle();
}
brain.input(values);
values[3] = eating ? 1 : 0;
try {
brain.input(values);
} catch (Exception ex) {
// Should not happen
Logger.getLogger(Creature.class.getName()).log(Level.SEVERE, null, ex);
}
// compute behavior
float[] actions = brain.compute();
Log.log(Log.DEBUG,"Accel: " + actions[0] + " Rot: " + actions[1]);
speed = actions[0]*max_speed;
rotSpeed = actions[1]/10;
Log.log(Log.DEBUG, "Accel: " + actions[0] + " RotClock: " + actions[1] + " RotAntiClock: " + actions[2]);
speed = actions[0] * max_speed;
rotSpeed = actions[1] - actions[2];
}
public void setHp(float hp) {
@ -99,25 +114,24 @@ public class Creature extends Element {
public void render(ShapeRenderer s) {
// Body
s.setColor(1 - (hp / 100), hp / 100, 0, 1);
s.circle(getX(), getY(), getSize());
s.circle(getX() + Game.get().getCamera().getX(), getY() + Game.get().getCamera().getY(), getSize());
// Eye
double relX = Math.cos(dir) * getSize(), relY = Math.sin(dir) * getSize();
s.setColor(1, 1, 1, 1);
if (sight != null) {
float c = sight.getDistance() / sightRange*2 + sightRange;
float c = sight.getDistance() / sightRange * 2 + sightRange;
if (sight.getElement() instanceof Creature) {
s.setColor(c, 0, 0, 1);
} else if (sight.getElement() instanceof Vegetable) {
s.setColor(0, c, 0, 1);
} else {
s.setColor(1, 1, 1, 1);
}
}
s.circle((float) relX + getX(), (float) relY + getY(), 3);
s.circle((float) relX + getX() + Game.get().getCamera().getX(), (float) relY + getY() + Game.get().getCamera().getY(), 3);
//FOV
float degrees = fov * 180f / (float) Math.PI;
float orient = dir * 180f / (float) Math.PI - degrees / 2;
s.setColor(0.3f, 0.3f, 0.3f, 1);
s.arc((float) relX + getX(), (float) relY + getY(), sightRange, orient, degrees);
s.arc((float) relX + getX() + Game.get().getCamera().getX(), (float) relY + getY() + Game.get().getCamera().getY(), sightRange, orient, degrees);
}
public Sight look() {
@ -153,9 +167,12 @@ public class Creature extends Element {
}
public void eat() {
for (Element e : Game.get().getWorld().getElements()) {
if (e instanceof Vegetable && overlaps(e)) {
eating = false;
for (Element e : Game.get().getWorld().getPlants()) {
if (overlaps(e)) {
eating = true;
e.setSize(e.getSize() - 0.1f);
if(e.getSize() == 0) e.setSize(0);
hp++;
fitness++;
if (hp > 100) {
@ -177,9 +194,9 @@ public class Creature extends Element {
return fitness;
}
public void reset(){
public void reset() {
fitness = 0;
hp = 100;
}
}

View File

@ -23,7 +23,7 @@ public class Vegetable extends Element {
@Override
public void update() {
if(getSize() < 0){
if (getSize() <= 0) {
Game.get().getWorld().getDeadPlants().add(this);
}
}
@ -31,6 +31,6 @@ public class Vegetable extends Element {
@Override
public void render(ShapeRenderer s) {
s.setColor(1, 1, 1, 1);
s.circle(getX(), getY(), getSize());
s.circle(getX() + Game.get().getCamera().getX(), getY() + Game.get().getCamera().getY(), getSize());
}
}

View File

@ -41,16 +41,16 @@ public class World {
elements.removeAll(graveyard);
elements.removeAll(deadPlants);
plants.removeAll(deadPlants);
creatures.removeAll(graveyard);
deadPlants.clear();
creatures.removeAll(graveyard);
if (creatures.isEmpty()) {
// All dead, next gen
newGen(false);
}
while (plants.size() < 50) {
while (plants.size() < (width*height)/5000) {
spawnVegetable();
}
for (Creature e : creatures) {
for (Element e : elements) {
e.update();
}
}
@ -63,7 +63,7 @@ public class World {
@Override
public int compare(Creature t, Creature t1) {
// put the highest fitness first (sort in reverse)
return (int) (t1.getFitness() - t.getFitness() );
return (int) (t1.getFitness() - t.getFitness());
/*if (t.getFitness() < t1.getFitness()) {
return -1;
} else if (t.getFitness() > t1.getFitness()) {
@ -132,7 +132,7 @@ public class World {
}
} while (overlaps);
if (isCreature) {
Log.log(Log.INFO, "New Creat: " + x + " " + y);
Log.log(Log.DEBUG, "New Creat: " + x + " " + y);
Creature c = new Creature(x, y);
if (brainMap != null) {
c.getBrain().remap(brainMap);
@ -140,7 +140,7 @@ public class World {
elements.add(c);
creatures.add(c);
} else {
Log.log(Log.INFO, "New Veg: " + x + " " + y);
Log.log(Log.DEBUG, "New Veg: " + x + " " + y);
Vegetable v = new Vegetable(x, y);
elements.add(v);
plants.add(v);
@ -179,4 +179,12 @@ public class World {
return deadPlants;
}
public ArrayList<Creature> getCreatures() {
return creatures;
}
public ArrayList<Vegetable> getPlants() {
return plants;
}
}

View File

@ -57,10 +57,10 @@ public class Brain {
for (int i = 0; i < brainMap.length; i++) { // for each layer
for (int j = 0; j < brainMap[i].length; j++) { // for each neuron
// skip input layer
if (neurons[i+1][j] == null) {
neurons[i+1][j] = new Neuron(j, bias, this, brainMap[i][j]);
if (neurons[i + 1][j] == null) {
neurons[i + 1][j] = new Neuron(j, bias, this, brainMap[i][j]);
} else {
neurons[i+1][j].setWeights(brainMap[i][j]);
neurons[i + 1][j].setWeights(brainMap[i][j]);
}
}
}
@ -86,8 +86,13 @@ public class Brain {
*
* @param values the array of input. Its length must match the number 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 void input(float[] values) {
public void input(float[] values) throws Exception {
if (values.length != neurons[0].length) {
throw new Exception("Not enough or too many inputs");
}
for (int i = 0; i < values.length; i++) {
neurons[0][i].setOutput(values[i]);
}
@ -114,8 +119,10 @@ public class Brain {
*
* @param values
* @return the results of the neural network
* @throws Exception if the number of inputs given differs from the number
* of input neurons of this brain
*/
public float[] compute(float[] values) {
public float[] compute(float[] values) throws Exception {
input(values);
return compute();
}
@ -127,13 +134,13 @@ public class Brain {
* mind
*/
public float[][][] getMap() {
float[][][] res = new float[neurons.length-1][][];
float[][][] res = new float[neurons.length - 1][][];
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
{
res[i-1] = new float[neurons[i].length][];
res[i - 1] = new float[neurons[i].length][];
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
{
res[i-1][j] = neurons[i][j].getWeights();
res[i - 1][j] = neurons[i][j].getWeights();
}
}
return res;
@ -146,27 +153,27 @@ public class Brain {
* @return a mutated brain map of this brain's mind
*/
public float[][][] getMutatedMap(float mutationFactor) {
float[][][] res = new float[neurons.length-1][][];
float[][][] res = new float[neurons.length - 1][][];
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
{
res[i-1] = new float[neurons[i].length][];
res[i - 1] = new float[neurons[i].length][];
for (int j = 0; j < neurons[i].length; j++) // neurons per layer
{
res[i-1][j] = neurons[i][j].mutate(mutationFactor);
res[i - 1][j] = neurons[i][j].mutate(mutationFactor);
}
}
return res;
}
public float[][][] breed(float[][][] map) throws Exception {
float[][][] res = new float[neurons.length-1][][];
if (map.length != neurons.length-1) {
float[][][] res = new float[neurons.length - 1][][];
if (map.length != neurons.length - 1) {
throw new Exception("incompatible brains");
}
for (int i = 1; i < neurons.length; i++) // layers (skip input layer)
{
res[i-1] = new float[neurons[i].length][];
if (map[i-1].length != neurons[i].length) {
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
@ -174,17 +181,17 @@ public class Brain {
// j = 8 not valid for neurons[i][j]. investigate why.
//System.out.println(i+" "+j+" | "+neurons[i].length+" "+res[i-1].length+" "+neurons[i][j].getWeights().length);
//System.out.println(neurons[i].length +" has to be > "+j);
res[i-1][j] = new float[neurons[i][j].getWeights().length];
if (map[i-1][j].length != neurons[i][j].getWeights().length) {
res[i - 1][j] = new float[neurons[i][j].getWeights().length];
if (map[i - 1][j].length != neurons[i][j].getWeights().length) {
throw new Exception("incompatible brains");
}
for (int z = 0; z < neurons[i][j].getWeights().length; z++) // each weight
{
// Combine the two weights
if (Math.random() < 0.5) {
res[i-1][j][z] = map[i-1][j][z];
res[i - 1][j][z] = map[i - 1][j][z];
} else {
res[i-1][j][z] = neurons[i][j].getWeights()[z];
res[i - 1][j][z] = neurons[i][j].getWeights()[z];
}
}
}