import java.awt.Rectangle;
import java.util.ArrayList;
public class Cell extends sObject {
Network brain;
float absX, absY;
int energy = 0;
int life = 0;
boolean reproduce = true;
boolean eat = false;
boolean dormant = false; // NOT IN USE
Genome Gnm;
public Cell(Network n, Genome g, int x, int y, int en) {
brain = n;
Gnm = g;
energy = en;
r = new Rectangle(x, y, 10, 10);
absX = x;
absY = y;
}
public Plant bestPlant(ArrayList<Plant> plants, Plant p) {
Plant bestPlant = plants.get(0);
// bestPlant = (bestPlant != p) ? bestPlant : plants.get(1);
int bDist = distance(bestPlant);
for (int i = 1; i < plants.size(); i++) {
Plant nPlant = plants.get(i);
int nDist = distance(nPlant);
if (nDist < bDist && nPlant != p) {
bestPlant = nPlant;
bDist = nDist;
}
}
return bestPlant;
}
public Cell bestCell(ArrayList<Cell> cell, Cell c) {
// There is always another cell (this)
Cell bestCell = (Cell) cell.get(0);
int bestCellDist = distance(bestCell);
for (int i = 1; i < cell.size(); i++) {
Cell nCell = cell.get(i);
int nCellDist = distance(nCell);
if ((nCellDist < bestCellDist && nCell != c && nCellDist != 0)
|| bestCellDist == 0) {
bestCell = nCell;
bestCellDist = nCellDist;
}
}
return bestCell;
}
public void moveCell() {
float run = brain.Output[1].value;
float rise = brain.Output[0].value;
float speed = Math.abs(run + rise); // Output 2
absX += run;
absY += rise;
energy -= speed * PIXLE_MOVE_COST; // COST TO MOVE
if (absX > width) {
absX = 0;
energy -= R_EN;
} else if (absX < 0) {
absX = width;
energy -= R_EN;
}
if (absY > height) {
absY = 0;
energy -= R_EN;
} else if (absY < 0) {
absY = height;
energy -= R_EN;
}
r.setLocation((int) absX, (int) absY); // MOVE THE CELL
}
public Cell reproduce(Cell other, int energy) {
return new Cell(brain.crossMutate(other.brain)[round(random(1))], Gnm.crossMutate(other.Gnm),
(int) (r.getX() + random(-15, 15)), (int) (r.getY() + random(-15, 15)), energy);
}
public boolean die(ArrayList<Cell> cell, ArrayList<Plant> plant) {
if (energy <= 0 && (life > Gnm.maxAge || !AGE_LIMIT)) { // LIFE
cell.remove(this);
if (energy > 0)
plant.add(new Plant((int) getX(), (int) getY(), energy, energy / SIZE_CAL + 2));
return true;
}
return false;
}
public void update(ArrayList<Cell> cells, ArrayList<Plant> plants) {
life++;
// GET CLOSEST PLANT
Plant bestPlant = bestPlant(plants, null);
// Plant nextBestPlant = bestPlant(plant, bestPlant);
Cell bestCell = bestCell(cells, this);
energy = constrain(energy, -10, MAXENERGY);
r.setSize(energy / SIZE_CAL + 2, energy / SIZE_CAL + 2);
brain.Input[0].value = (bestPlant.getX() - getX()) / width;
brain.Input[1].value = (bestPlant.getY() - getY()) / height;
brain.Input[2].value = (bestCell.getX() - getX()) / width;
brain.Input[3].value = (bestCell.getY() - getY()) / height;
brain.Input[4].value = (bestCell.energy) / MAXENERGY;
/*
* brain.Input[2].value = getX()/width; brain.Input[3].value =
* getY()/height;
*/
brain.Input[5].value = energy / MAXENERGY;
brain.Input[6].value = life / MAX_AGE_FROMBINARY;
//brain.Input[8].value = (bestCell.Gnm.gHue/256);
brain.runNet();
// This function evals and does moving
moveCell();
energy -= BASE_METABOLISM; // METABOLISM
// Update Bool
reproduce = (brain.Output[2].value >= 0);
eat = (brain.Output[3].value >= 0);
if (eat)
energy -= EAT_ENERGY; // If trying to eat using energy
// //// EAT
Rectangle or = bestPlant.getRect();
if (r.intersects(or) || r.contains(or) || or.contains(r)) {
energy += bestPlant.energy;
plants.remove(bestPlant);
// bestPlant.getRect().setLocation((int)random(width),
// (int)random(height));
return;
}
// Rep, [-1.0, 0.0] do nothing, [0.0, 0.5] find mate and rep, [0.5, 1.0]
// asexual
// //// REPRODUCE
boolean reproduced = false;
if (bestCell != this) { // Valid Choice
Rectangle cr = bestCell.getRect();
boolean intersects = r.intersects(cr) || r.contains(cr) || cr.contains(r);
if (this.life > this.Gnm.reporductionAge // If can reproduce
&& this.energy > ENERGYTOREPRODUCE && this.reproduce) {
if (intersects) { // sextual reproduction
if (bestCell.energy > ENERGYTOREPRODUCE
&& bestCell.life > bestCell.Gnm.reporductionAge) {
int energyToGive = (int) (energy * ENERGY_TOGIVE + bestCell.energy
* ENERGY_TOGIVE);
cells.add(reproduce(bestCell, energyToGive));
energy = (int) (energy * ENERGY_TOGIVE - ENERGYTOREPRODUCE);
bestCell.energy = (int) (bestCell.energy
* ENERGY_TOGIVE - ENERGYTOREPRODUCE);
reproduced = true;
}
} else if (brain.Output[2].value > 0.5) { // asextual
int energyToGive = (int) (energy * ENERGY_TOGIVE);
cells.add(reproduce(this, energyToGive));
energy = energyToGive - ENERGYTOREPRODUCE;
}
}
if (this.eat && !reproduced
&& intersects && (this.energy > bestCell.energy || !bestCell.eat)) { // If eating and larger than other
float rnd = random(0.90f);
energy += bestCell.energy * rnd;
bestCell.energy -= bestCell.energy * (rnd+0.25);
if(bestCell.energy < 0) cells.remove(bestCell);
}
}
die(cells, plants);
}
}
static boolean doDraw = true;
static boolean SELFSUSTAIN = false;
static boolean AGE_LIMIT = true;
static Random rand = new Random(System.currentTimeMillis());
static float maxMutChange = 0.07f;
static float mutationRate = 0.005f;
static float crossOverRate = 0.5f;
static float NUT_NEEDED = 0.9f;
static float NUTRITION = 350;
static float P_INC = 0.00005f;
static float mod = 2.0f;
static int MAXENERGY = (int) (2500 * mod);
static int MAX_PLANT = (int) (1000 * mod);
static int PLNENERGY = (int) (300 * mod);
static int R_EN = (int) (700 * mod);
static int START_ENERGY = (int) (1500 * mod);
static int ENERGYTOREPRODUCE = (int) (600 * mod);
// Unaffected by mod
static int BASE_METABOLISM = 2;
static int PIXLE_MOVE_COST = 3;
static int EAT_ENERGY = 0;
static int MAXLIFE = 1800;
static int MAX_AGE_FROMBINARY = 2050;
static int SIZE_CAL = MAXENERGY / 10;
static float ENERGY_TOGIVE = 0.5f;
static int REP_AGE = 50;
public class Genome {
public int[] a = new int[34];
int maxAge;
int reporductionAge;
int layers; // Not doing anything
int nodesPerLayer; // Not doing anything
int gHue;
public Genome() {
for (int i = 0; i < a.length; i++) {
a[i] = PApplet.round(random(1));
}
eval();
}
public Genome crossMutate(Genome other) {
// Init the new genome
Genome newG = new Genome();
newG.a = this.a.clone();
for (int i = 0; i < a.length; i++) {
if (random(1) < crossOverRate) // Crossover
newG.a[i] = other.a[i];
else if (random(1) < mutationRate) // Mutate
newG.a[i] = (newG.a[i] == 1) ? 0 : 1;
}
newG.eval();
return newG;
}
public void eval() {
String s;
int i = 0, li;
s = "";
for (li = i; i < li + 11; i++)
s = s + a[i];
maxAge = Integer.parseInt(s, 2);
s = "";
for (li = i; i < li + 8; i++)
s = s + a[i];
reporductionAge = Integer.parseInt(s, 2);
s = "";
for (li = i; i < li + 3; i++)
s = s + a[i];
layers = Integer.parseInt(s, 2);
s = "";
for (li = i; i < li + 4; i++)
s = s + a[i];
nodesPerLayer = Integer.parseInt(s, 2);
s = "";
for (li = i; i < li + 8; i++)
s = s + a[i];
gHue = Integer.parseInt(s, 2);
}
}
public class Network implements Comparable<Object> {
public Node[] Input, Output;
public Node[][] Hidden;
public int fitness;
public Network(int in, int hdnLrs, int hi, int ou) {
Input = new Node[in + 1]; // also add bias
Hidden = new Node[hdnLrs][hi + 1];
if (hi == 0)
Hidden = new Node[0][0]; // If the hidden layers are empty, clear
Output = new Node[ou];
fitness = 0;
for (int i = 0; i < Input.length; i++)
Input[i] = new Node(); // for each including bias (no diff from
// other)
for (int i = 0; i < Hidden.length; i++)
for (int iii = 0; iii < Hidden[i].length; iii++) {
if (iii != Hidden[i].length - 1)
Hidden[i][iii] = new Node(
randomValues((i > 0) ? Hidden[i - 1].length
: Input.length)); // for each but bias
else
Hidden[i][iii] = new Node(); // Set Hidden bias
}
for (int i = 0; i < Output.length; i++)
Output[i] = new Node(
randomValues((Hidden.length > 0) ? Hidden[Hidden.length - 1].length
: Input.length));
}
public String toString() {
// PLACE HOLDER
return "";
}
float[] randomValues(int x) {
float[] result = new float[x];
for (int i = 0; i < x; i++) {
result[i] = random(-1, 1);
}
return result;
}
public void runNet() {
for (int i = 0; i < Hidden.length; i++)
for (int j = 0; j < Hidden[i].length; j++)
Hidden[i][j].eval((i > 0) ? Hidden[i - 1] : Input);
for (int i = 0; i < Output.length; i++) {
if (Hidden.length > 0)
Output[i].eval(Hidden[Hidden.length - 1]);
else
Output[i].eval(Input);
}
}
public void evalFitness(int f) {
fitness += f;
}
public void evalFitness() {
}
public int compareTo(Object o) throws ClassCastException {
if (!(o instanceof Network)) {
throw new ClassCastException("A Network object expected.");
}
Network n2 = (Network) o;
return n2.fitness - this.fitness;
}
public Network[] crossMutate(Network other) {
Network childA = this.clone();
Network childB = other.clone();
// Cross this with other (Hidden Layer)
for (int i = 0; i < childA.Hidden.length && i < other.Hidden.length; i++)
// Each layer
for (int j = 0; j < childA.Hidden[i].length
&& j < other.Hidden[i].length; j++)
// Each node
for (int iii = 0; iii < other.Hidden[i][j].weights.length; iii++)
// Each weight
if (random(1) < crossOverRate)
childA.Hidden[i][j].weights[iii] = mutate(other.Hidden[i][j].weights[iii]);
// Cross other with this (Hidden Layer)
for (int i = 0; i < childB.Hidden.length && i < this.Hidden.length; i++)
// Each layer
for (int j = 0; j < childB.Hidden[i].length
&& j < this.Hidden[i].length; j++)
// Each node
for (int iii = 0; iii < this.Hidden[i][j].weights.length; iii++)
// Each weight
if (random(1) < crossOverRate)
childB.Hidden[i][j].weights[iii] = mutate(this.Hidden[i][j].weights[iii]);
// Cross this with other (Output Layer)
for (int i = 0; i < childA.Output.length && i < other.Output.length; i++)
for (int iii = 0; iii < other.Output[i].weights.length; iii++)
if (random(1) < crossOverRate)
childA.Output[i].weights[iii] = mutate(other.Output[i].weights[iii]);
// Cross other with this (Output Layer)
for (int i = 0; i < childB.Output.length && i < this.Output.length; i++)
for (int iii = 0; iii < this.Output[i].weights.length; iii++)
if (random(1) < crossOverRate)
childB.Output[i].weights[iii] = mutate(this.Output[i].weights[iii]);
return new Network[] { childA, childB };
}
private float mutate(float val) {
if (random(1) < mutationRate)
return constrain(val
+ (random(-1, 1) * maxMutChange), -1, 1);
return val;
}
public Network clone() {
int rslt = 0;
for (Node[] nAry : Hidden)
rslt += nAry.length - 1; // average each, remove extra bias
rslt /= Hidden.length;
// Input bias
Network cloneNet = new Network(Input.length - 1, Hidden.length, rslt, Output.length);
for (int i = 0; i < cloneNet.Input.length; i++)
cloneNet.Input[i] = new Node();
for (int i = 0; i < cloneNet.Hidden.length; i++)
for (int j = 0; j < cloneNet.Hidden[i].length; j++)
cloneNet.Hidden[i][j] = new Node((float[]) Hidden[i][j].weights
.clone());
for (int i = 0; i < cloneNet.Output.length; i++)
cloneNet.Output[i] = new Node((float[]) Output[i].weights.clone());
return cloneNet;
}
public class Node {
// default is 1 so it is correct if the node is a bias
public float value = 1;
public float[] weights;
/*
* bias = the bias is needed to be able to solve problems that is not
* linearly seperable
*/
// Initialize a bias or an (on)input
public Node() {
this(new float[0]);
}
// Initialize with weights
public Node(float[] w) {
weights = w;
}
// If an input or a bias (isn't connected to any other nodes) you can
// change the value
public void setValue(float v) {
if (weights.length == 0)
value = v;
}
// Sum the value of the connected Node * it's weight, then run it
// through the activation function (sigmoid)
public void eval(Node[] ns) {
if (weights.length == 0)
return;
float total = 0;
for (int i = 0; i < weights.length; i++)
total += ns[i].value * weights[i];
value = arctan(total);
}
float arctan(float x) {
return (float) ((2 / Math.PI) * Math.atan(Math.PI / 2 * x));
}
float sigmoid(float a) {
return (float) (1 / (1 + Math.pow(Math.E, -a)));
}
}
}
import java.awt.Rectangle;
import java.util.ArrayList;
public class Plant extends sObject {
public int energy = PLNENERGY;
int age = 0;
int maxAge = (int) random(MAX_PLANT * 0.5f, MAX_PLANT * 1.5f);
public Plant(int x, int y) {
r = new Rectangle(x, y, 10, 10);
}
public Plant(int x, int y, int e, int s) {
this(x, y);
energy = constrain(e, -PLNENERGY, PLNENERGY);
r.setSize(s, s);
}
public Plant(int x, int y, boolean well) {
this(x, y);
if (well)
maxAge = Integer.MAX_VALUE;
}
public void update(ArrayList<Plant> plants) {
age++;
if (age >= maxAge || energy <= 0) {
plants.remove(this);
return;
}
if (random(1) < plant_rate) {
double newX = r.getX() + random((float) -r.getWidth() * 10,(float) r.getWidth() * 10);
double newY = r.getY() + random((float) -r.getWidth() * 10, (float) r.getWidth() * 10);
if(newX < 0 || newX > width || newY < 0 || newY > height)
return;
Plant p = new Plant((int) newX, (int) newY, energy, (int) r.getWidth());
plants.add(p);
plant_rate = NUTRITION - (plants.size() * NUT_NEEDED);
}
}
}
import java.util.ArrayList;
float plant_rate = 0.001f;
ArrayList<Cell> cells;
/********** With 50 cells there should be about 35 connections for each ******************/
ArrayList<Plant> plants;
public void setup() {
size(700, 700);
rectMode(CENTER);
colorMode(HSB, 360, 100, 100);
noStroke();
background(100);
reset();
}
void reset() {
plants = new ArrayList<Plant>();
for (int i = 0; i < 300; i++) {
plants.add(new Plant(round(random(width - 40) + 20),
round(random(height - 40) + 20)));
}
cells = new ArrayList<Cell>();
for (int i = 0; i < 100; i++) {
cells.add(new Cell(createNet(), new Genome(),
round(random(width - 40) + 20),
round(random(height - 40) + 20), START_ENERGY));
}
}
Network createNet() {
return new Network(7, 5, 10, 4);
}
public void mousePressed() {
if (mouseButton == LEFT)
cells.add(new Cell(createNet(), new Genome(), mouseX, mouseY, START_ENERGY));
else if (mouseButton == RIGHT)
plants.add(new Plant(mouseX, mouseY));
}
public void keyPressed() {
if (key == 'r' && cells.size() >= 2)
cells.add((Cell) (cells.get(round(random(cells.size() - 1))))
.reproduce(
(Cell) cells.get(round(random(cells.size() - 1))),
START_ENERGY).setXY((int) random(width),
(int) random(height)));
else if (key == 'n')
cells.add(new Cell(createNet(), new Genome(),
round(random(width - 40) + 20),
round(random(height - 40) + 20), START_ENERGY));
else if (key == 'p')
plants.add(new Plant((int) random(width), (int) random(height)));
else if (key == 'e') {
cells.clear();
plants.clear();
}
else if (key == 'd')
doDraw = !doDraw;
else if (key == 's')
SELFSUSTAIN = !SELFSUSTAIN;
}
public void draw() {
background(0, 0, 40);
plant_rate = NUTRITION - (plants.size() * NUT_NEEDED);
if (SELFSUSTAIN) {
if (cells.size() == 0)
reset();
if (random(1) < 0.2 / cells.size() && cells.size() >= 2) {
Cell c1 = (Cell) cells.get(round(random(cells.size() - 1)));
Cell c2 = (Cell) cells.get(round(random(cells.size() - 1)));
cells.add(
(Cell) c1.reproduce(
c2, (int) (c1.energy * ENERGY_TOGIVE + c2.energy * ENERGY_TOGIVE)
).setXY((int) random(width), (int) random(height)));
}
if (random(1) < plant_rate || plants.size() == 0)
plants.add(new Plant((int) random(width), (int) random(height)));
}
for (int i = 0; i < plants.size(); i++) {
Plant p = (Plant) plants.get(i);
p.update(plants);
if (doDraw) {
fill(120, 75, 75);
rect(p.getX(), p.getY(), p.getWidth(), p.getHeight());
}
}
for (int i = 0; i < cells.size(); i++) {
Cell c = (Cell) cells.get(i);
c.update(cells, plants);
if (doDraw) {
fill(c.Gnm.gHue, 75, 100);
rect(c.getX(), c.getY(), c.getWidth(), c.getHeight());
}
}
/*
* if(frameRate < 50 && millis() > 2000) println("Cells: " +
* cells.size() + " Plant: " + plants.size() + " : " + frameRate);
*/
}
import java.awt.Rectangle;
public abstract class sObject {
Rectangle r;
public int distance(sObject other) {
return PApplet.round(PApplet.dist(this.getX(), this.getY(), other
.getX(), other.getY()));
}
public float getX() {
return (float) r.getX();
}
public float getY() {
return (float) r.getY();
}
public float getWidth() {
return (float) r.getWidth();
}
public float getHeight() {
return (float) r.getHeight();
}
public sObject setXY(int x, int y) {
r.setLocation(x, y);
return this;
}
public Rectangle getRect() {
return r;
}
}
Watch cells with a Neural Network brain evolve a system for survival