final float windowSizeMultiplier = 0.8;
ArrayList<Float[]> percentile = new ArrayList<Float[]>(0);
ArrayList<Integer[]> barCounts = new ArrayList<Integer[]>(0);
ArrayList<Integer[]> speciesCounts = new ArrayList<Integer[]>(0);
ArrayList<Integer> topSpeciesCounts = new ArrayList<Integer>(0);
ArrayList<Creature> creatureDatabase = new ArrayList<Creature>(0);
ArrayList<Rectangle> rects = new ArrayList<Rectangle>(0);
boolean haveGround = true;
int histBarsPerMeter = 5;
String[] operationNames = {"#","time","px","py","+","-","*","รท","%","sin","sig","pres"};
int[] operationAxons = {0, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 0};
String fitnessUnit = "m";
String fitnessName = "Distance";
float baselineEnergy = 0.0;
final float FRICTION = 4;
float bigMutationChance = 0.06;
boolean saveFramesPerGeneration = true;
float pressureUnit = 500.0/2.37;
int barLen = maxBar-minBar;
float postFontSize = 0.96;
float scaleToFixBug = 1000;
float averageNodeNausea = 0;
float totalNodeNausea = 0;
color axonColor = color(255,255,0);
boolean justGotBack = false;
50, 36, 25, 20, 16, 14, 11, 9
int prevStatusWindow = -4;
boolean miniSimulation = false;
int creatureWatching = 0;
int[] creaturesInPosition = new int[1000];
float airFriction = 0.95;
0, 10, 20, 30, 40, 50, 60, 70, 80, 90,
100, 200, 300, 400, 500, 600, 700, 800, 900, 910, 920, 930, 940, 950, 960, 970, 980, 990, 999
float inter(int a, int b, float offset) {
return float(a)+(float(b)-float(a))*offset;
return pow(random(-1, 1), 19);
return int(random(-0.01, 1.01));
Rectangle(float tx1, float ty1, float tx2, float ty2) {
float x, y, vx, vy, prevX, prevY, pvx, pvy, m, f, value, valueToBe;
int operation, axon1, axon2;
Node(float tx, float ty, float tvx, float tvy, float tm, float tf, float val, int op, int a1, int a2) {
float acc = dist(vx,vy,pvx,pvy);
totalNodeNausea += acc*acc*nauseaUnit;
void pressAgainstGround(float groundY){
float dif = y-(groundY-m/2);
pressure += dif*pressureUnit;
if (dif >= 0 && haveGround) {
if(y > prevY && hazelStairs >= 0){
float bottomPointNow = y+m/2;
float bottomPointPrev = prevY+m/2;
int levelNow = (int)(ceil(bottomPointNow/hazelStairs));
int levelPrev = (int)(ceil(bottomPointPrev/hazelStairs));
if(levelNow > levelPrev){
float groundLevel = levelPrev*hazelStairs;
pressAgainstGround(groundLevel);
for (int i = 0; i < rects.size(); i++) {
Rectangle r = rects.get(i);
if (abs(x-(r.x1+r.x2)/2) <= (r.x2-r.x1+m)/2 && abs(y-(r.y1+r.y2)/2) <= (r.y2-r.y1+m)/2) {
if (x >= r.x1 && x < r.x2 && y >= r.y1 && y < r.y2) {
if (d1 < d2 && d1 < d3 && d1 < d4) {
}else if (d2 < d3 && d2 < d4) {
float distance = dist(x, y, px, py);
float wallAngle = atan2(py-y, px-x);
if (distance < rad || flip) {
pressure += dif*pressureUnit;
float multi = rad/distance;
float veloAngle = atan2(vy, vx);
float veloMag = dist(0, 0, vx, vy);
float relAngle = veloAngle-wallAngle;
float relY = sin(relAngle)*veloMag*dif*FRICTION;
vx = -sin(relAngle)*relY;
void doMath(int i, ArrayList<Node> n){
float axonValue1 = n.get(axon1).value;
float axonValue2 = n.get(axon2).value;
}else if(operation == 1){
valueToBe = simulationTimer/60.0;
}else if(operation == 2){
}else if(operation == 3){
}else if(operation == 4){
valueToBe = axonValue1+axonValue2;
}else if(operation == 5){
valueToBe = axonValue1-axonValue2;
}else if(operation == 6){
valueToBe = axonValue1*axonValue2;
}else if(operation == 7){
valueToBe = axonValue1/axonValue2;
}else if(operation == 8){
valueToBe = axonValue1%axonValue2;
}else if(operation == 9){
valueToBe = sin(axonValue1);
}else if(operation == 10){
valueToBe = 1/(1+pow(2.71828182846,-axonValue1));
}else if(operation == 11){
void realizeMathValues(int i){
return (new Node(x, y, 0, 0, m, f, value, operation, axon1, axon2));
Node modifyNode(float mutability, int nodeNum) {
float newX = x+r()*0.5*mutability;
float newY = y+r()*0.5*mutability;
float newM = m+r()*0.1*mutability;
newM = min(max(newM, 0.3), 0.5);
float newV = value*(1+r()*0.2*mutability);
int newOperation = operation;
if(random(0,1)<bigMutationChance*mutability){
newOperation = int(random(0,operationCount));
if(random(0,1)<bigMutationChance*mutability){
newAxon1 = int(random(0,nodeNum));
if(random(0,1)<bigMutationChance*mutability){
newAxon2 = int(random(0,nodeNum));
}else if(newOperation == 2){
}else if(newOperation == 3){
Node newNode = new Node(newX, newY, 0, 0, newM, min(max(f+r()*0.1*mutability, 0), 1), newV, newOperation, newAxon1, newAxon2);
Muscle(int taxon, int tc1, int tc2, float tlen, float trigidity) {
previousTarget = len = tlen;
void applyForce(int i, ArrayList<Node> n) {
float target = previousTarget;
if(energyDirection == 1 || energy >= 0.0001){
if(axon >= 0 && axon < n.size()){
target = len*toMuscleUsable(n.get(axon).value);
float distance = dist(ni1.x, ni1.y, ni2.x, ni2.y);
float angle = atan2(ni1.y-ni2.y, ni1.x-ni2.x);
force = min(max(1-(distance/target), -0.4), 0.4);
ni1.vx += cos(angle)*force*rigidity/ni1.m;
ni1.vy += sin(angle)*force*rigidity/ni1.m;
ni2.vx -= cos(angle)*force*rigidity/ni2.m;
ni2.vy -= sin(angle)*force*rigidity/ni2.m;
energy = max(energy+energyDirection*abs(previousTarget-target)*rigidity*energyUnit,0);
return new Muscle(axon, c1, c2, len, rigidity);
Muscle modifyMuscle(int nodeNum, float mutability) {
if(random(0,1)<bigMutationChance*mutability){
newc1 = int(random(0,nodeNum));
if(random(0,1)<bigMutationChance*mutability){
newc2 = int(random(0,nodeNum));
if(random(0,1)<bigMutationChance*mutability){
newAxon = getNewMuscleAxon(nodeNum);
float newR = min(max(rigidity*(1+r()*0.9*mutability),0.01),0.08);
float newLen = min(max(len+r()*mutability,0.4),1.25);
return new Muscle(newAxon, newc1, newc2, newLen, newR);
int getNewMuscleAxon(int nodeNum){
return int(random(0,nodeNum));
Creature(int tid, ArrayList<Node> tn, ArrayList<Muscle> tm, float td, boolean talive, float tct, float tmut) {
Creature modified(int id) {
Creature modifiedCreature = new Creature(id,
new ArrayList<Node>(0), new ArrayList<Muscle>(0), 0, true, creatureTimer+r()*16*mutability, min(mutability*random(0.8, 1.25), 2));
for (int i = 0; i < n.size(); i++) {
modifiedCreature.n.add(n.get(i).modifyNode(mutability,n.size()));
for (int i = 0; i < m.size(); i++) {
modifiedCreature.m.add(m.get(i).modifyMuscle(n.size(), mutability));
if (random(0, 1) < bigMutationChance*mutability || n.size() <= 2) {
modifiedCreature.addRandomNode();
if (random(0, 1) < bigMutationChance*mutability) {
modifiedCreature.addRandomMuscle(-1, -1);
if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.n.size() >= 4) {
modifiedCreature.removeRandomNode();
if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.m.size() >= 2) {
modifiedCreature.removeRandomMuscle();
modifiedCreature.checkForOverlap();
modifiedCreature.checkForLoneNodes();
modifiedCreature.checkForBadAxons();
ArrayList<Integer> bads = new ArrayList<Integer>();
for (int i = 0; i < m.size(); i++) {
for (int j = i+1; j < m.size(); j++) {
if (m.get(i).c1 == m.get(j).c1 && m.get(i).c2 == m.get(j).c2) {
else if (m.get(i).c1 == m.get(j).c2 && m.get(i).c2 == m.get(j).c1) {
else if (m.get(i).c1 == m.get(i).c2) {
for (int i = bads.size()-1; i >= 0; i--) {
void checkForLoneNodes() {
for (int i = 0; i < n.size(); i++) {
for (int j = 0; j < m.size(); j++) {
if (m.get(j).c1 == i || m.get(j).c2 == i) {
int newConnectionNode = floor(random(0, n.size()));
while (newConnectionNode == i || newConnectionNode == connectedTo) {
newConnectionNode = floor(random(0, n.size()));
addRandomMuscle(i, newConnectionNode);
for (int i = 0; i < n.size(); i++) {
if(ni.axon1 >= n.size()){
ni.axon1 = int(random(0,n.size()));
if(ni.axon2 >= n.size()){
ni.axon2 = int(random(0,n.size()));
for (int i = 0; i < m.size(); i++) {
mi.axon = getNewMuscleAxon(n.size());
for (int i = 0; i < n.size(); i++) {
ni.safeInput = (operationAxons[ni.operation] == 0);
boolean didSomething = false;
while(iterations < 1000){
for (int i = 0; i < n.size(); i++) {
if((operationAxons[ni.operation] == 1 && n.get(ni.axon1).safeInput) ||
(operationAxons[ni.operation] == 2 && n.get(ni.axon1).safeInput && n.get(ni.axon2).safeInput)){
for (int i = 0; i < n.size(); i++) {
int parentNode = floor(random(0, n.size()));
float ang1 = random(0, 2*PI);
float distance = sqrt(random(0, 1));
float x = n.get(parentNode).x+cos(ang1)*0.5*distance;
float y = n.get(parentNode).y+sin(ang1)*0.5*distance;
int newNodeCount = n.size()+1;
n.add(new Node(x, y, 0, 0, 0.4, random(0, 1), random(0,1), floor(random(0,operationCount)),
floor(random(0,newNodeCount)),floor(random(0,newNodeCount))));
for (int i = 0; i < n.size()-1; i++) {
if (sqrt(dx*dx+dy*dy) < record) {
record = sqrt(dx*dx+dy*dy);
addRandomMuscle(parentNode, n.size()-1);
addRandomMuscle(nextClosestNode, n.size()-1);
void addRandomMuscle(int tc1, int tc2) {
int axon = getNewMuscleAxon(n.size());
tc1 = int(random(0, n.size()));
while (tc2 == tc1 && n.size () >= 2) {
tc2 = int(random(0, n.size()));
float len = random(0.5, 1.5);
len = dist(n.get(tc1).x, n.get(tc1).y, n.get(tc2).x, n.get(tc2).y);
m.add(new Muscle(axon, tc1, tc2, len, random(0.02, 0.08)));
void removeRandomNode() {
int choice = floor(random(0, n.size()));
if (m.get(i).c1 == choice || m.get(i).c2 == choice) {
for (int j = 0; j < m.size(); j++) {
if (m.get(j).c1 >= choice) {
if (m.get(j).c2 >= choice) {
void removeRandomMuscle() {
int choice = floor(random(0, m.size()));
Creature copyCreature(int newID) {
ArrayList<Node> n2 = new ArrayList<Node>(0);
ArrayList<Muscle> m2 = new ArrayList<Muscle>(0);
for (int i = 0; i < n.size(); i++) {
n2.add(this.n.get(i).copyNode());
for (int i = 0; i < m.size(); i++) {
m2.add(this.m.get(i).copyMuscle());
return new Creature(newID, n2, m2, d, alive, creatureTimer, mutability);
void drawGround(int toImage) {
int stairDrawStart = max(1,(int)(-averageY/hazelStairs)-10);
if (haveGround) rect((camX-camZoom*800.0)*scaleToFixBug, 0*scaleToFixBug, (camZoom*1600.0)*scaleToFixBug, (camZoom*900.0)*scaleToFixBug);
for (int i = 0; i < rects.size(); i++) {
Rectangle r = rects.get(i);
rect(r.x1*scaleToFixBug, r.y1*scaleToFixBug, (r.x2-r.x1)*scaleToFixBug, (r.y2-r.y1)*scaleToFixBug);
for(int i = stairDrawStart; i < stairDrawStart+20; i++){
rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.3*scaleToFixBug);
rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.15*scaleToFixBug);
}else if (toImage == 2) {
popUpImage.fill(0, 130, 0);
if (haveGround) popUpImage.rect((camX-camZoom*300.0)*scaleToFixBug, 0*scaleToFixBug, (camZoom*600.0)*scaleToFixBug, (camZoom*600.0)*scaleToFixBug);
for (int i = 0; i < rects.size(); i++) {
Rectangle r = rects.get(i);
popUpImage.rect(r.x1*scaleToFixBug, r.y1*scaleToFixBug, (r.x2-r.x1)*scaleToFixBug, (r.y2-r.y1)*scaleToFixBug);
for(int i = stairDrawStart; i < stairDrawStart+20; i++){
popUpImage.fill(255,255,255,128);
popUpImage.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.3*scaleToFixBug);
popUpImage.fill(255,255,255,255);
popUpImage.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.15*scaleToFixBug);
void drawNode(Node ni, float x, float y, int toImage) {
color c = color(512-int(ni.f*512), 0, 0);
c = color(255, 255-int(ni.f*512), 255-int(ni.f*512));
ellipse((ni.x+x)*scaleToFixBug, (ni.y+y)*scaleToFixBug, ni.m*scaleToFixBug, ni.m*scaleToFixBug);
textFont(font, 0.4*ni.m*scaleToFixBug);
text(nf(ni.value,0,2),(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY2+y)*scaleToFixBug);
text(operationNames[ni.operation],(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY1+y)*scaleToFixBug);
} else if (toImage == 1) {
screenImage.ellipse((ni.x+x)*scaleToFixBug, (ni.y+y)*scaleToFixBug, ni.m*scaleToFixBug, ni.m*scaleToFixBug);
screenImage.textAlign(CENTER);
screenImage.textFont(font, 0.4*ni.m*scaleToFixBug);
screenImage.text(nf(ni.value,0,2),(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY2+y)*scaleToFixBug);
screenImage.text(operationNames[ni.operation],(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY1+y)*scaleToFixBug);
} else if (toImage == 2) {
popUpImage.ellipse((ni.x+x)*scaleToFixBug, (ni.y+y)*scaleToFixBug, ni.m*scaleToFixBug, ni.m*scaleToFixBug);
popUpImage.textAlign(CENTER);
popUpImage.textFont(font, 0.4*ni.m*scaleToFixBug);
popUpImage.text(nf(ni.value,0,2),(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY2+y)*scaleToFixBug);
popUpImage.text(operationNames[ni.operation],(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY1+y)*scaleToFixBug);
void drawNodeAxons(ArrayList<Node> n, int i, float x, float y, int toImage) {
if(operationAxons[ni.operation] >= 1){
Node axonSource = n.get(n.get(i).axon1);
float point1x = ni.x-ni.m*0.3+x;
float point1y = ni.y-ni.m*0.3+y;
float point2x = axonSource.x+x;
float point2y = axonSource.y+axonSource.m*0.5+y;
drawSingleAxon(point1x,point1y,point2x,point2y,toImage);
if(operationAxons[ni.operation] == 2){
Node axonSource = n.get(n.get(i).axon2);
float point1x = ni.x+ni.m*0.3+x;
float point1y = ni.y-ni.m*0.3+y;
float point2x = axonSource.x+x;
float point2y = axonSource.y+axonSource.m*0.5+y;
drawSingleAxon(point1x,point1y,point2x,point2y,toImage);
void drawSingleAxon(float x1, float y1, float x2, float y2, int toImage){
float arrowHeadSize = 0.1;
float angle = atan2(y2-y1,x2-x1);
strokeWeight(0.03*scaleToFixBug);
line(x1*scaleToFixBug,y1*scaleToFixBug,x2*scaleToFixBug,y2*scaleToFixBug);
line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug);
line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug);
screenImage.stroke(axonColor);
screenImage.strokeWeight(0.03*scaleToFixBug);
screenImage.line(x1*scaleToFixBug,y1*scaleToFixBug,x2*scaleToFixBug,y2*scaleToFixBug);
screenImage.line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug);
screenImage.line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug);
popUpImage.stroke(axonColor);
popUpImage.strokeWeight(0.03*scaleToFixBug);
popUpImage.line(x1*scaleToFixBug,y1*scaleToFixBug,x2*scaleToFixBug,y2*scaleToFixBug);
popUpImage.line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*0.25)*arrowHeadSize)*scaleToFixBug);
popUpImage.line(x1*scaleToFixBug,y1*scaleToFixBug,(x1+cos(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug,(y1+sin(angle+PI*1.75)*arrowHeadSize)*scaleToFixBug);
void drawMuscle(Muscle mi, ArrayList<Node> n, float x, float y, int toImage) {
if(mi.axon >= 0 && mi.axon < n.size()){
w = toMuscleUsable(n.get(mi.axon).value)*0.15;
strokeWeight(w*scaleToFixBug);
stroke(70, 35, 0, mi.rigidity*3000);
line((ni1.x+x)*scaleToFixBug, (ni1.y+y)*scaleToFixBug, (ni2.x+x)*scaleToFixBug, (ni2.y+y)*scaleToFixBug);
} else if (toImage == 1) {
screenImage.strokeWeight(w*scaleToFixBug);
screenImage.stroke(70, 35, 0, mi.rigidity*3000);
screenImage.line((ni1.x+x)*scaleToFixBug, (ni1.y+y)*scaleToFixBug, (ni2.x+x)*scaleToFixBug, (ni2.y+y)*scaleToFixBug);
} else if (toImage == 2) {
popUpImage.strokeWeight(w*scaleToFixBug);
popUpImage.stroke(70, 35, 0, mi.rigidity*3000);
popUpImage.line((ni1.x+x)*scaleToFixBug, (ni1.y+y)*scaleToFixBug, (ni2.x+x)*scaleToFixBug, (ni2.y+y)*scaleToFixBug);
void drawMuscleAxons(Muscle mi, ArrayList<Node> n, float x, float y, int toImage) {
if(mi.axon >= 0 && mi.axon < n.size()){
Node axonSource = n.get(mi.axon);
float muscleMidX = (ni1.x+ni2.x)*0.5+x;
float muscleMidY = (ni1.y+ni2.y)*0.5+y;
drawSingleAxon(muscleMidX, muscleMidY, axonSource.x+x,axonSource.y+axonSource.m*0.5+y,toImage);
float averageMass = (ni1.m+ni2.m)*0.5;
textFont(font, 0.4*averageMass*scaleToFixBug);
text(nf(toMuscleUsable(n.get(mi.axon).value),0,2),muscleMidX*scaleToFixBug,muscleMidY*scaleToFixBug);
screenImage.fill(axonColor);
screenImage.textAlign(CENTER);
screenImage.textFont(font, 0.4*averageMass*scaleToFixBug);
screenImage.text(nf(toMuscleUsable(n.get(mi.axon).value),0,2),muscleMidX*scaleToFixBug,muscleMidY*scaleToFixBug);
popUpImage.fill(axonColor);
popUpImage.textAlign(CENTER);
popUpImage.textFont(font, 0.4*averageMass*scaleToFixBug);
popUpImage.text(nf(toMuscleUsable(n.get(mi.axon).value),0,2),muscleMidX*scaleToFixBug,muscleMidY*scaleToFixBug);
float toMuscleUsable(float f){
return min(max(f,0.5),1.5);
void drawPosts(int toImage) {
int startPostY = min(-8,(int)(averageY/4)*4-4);
textFont(font, postFontSize*scaleToFixBug);
for(int postY = startPostY; postY <= startPostY+8; postY += 4){
for (int i = (int)(averageX/5-5); i <= (int)(averageX/5+5); i++) {
rect((i*5.0-0.1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 0.2*scaleToFixBug, 3.0*scaleToFixBug);
rect((i*5.0-1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 2.0*scaleToFixBug, 1.0*scaleToFixBug);
text(i+" m", i*5.0*scaleToFixBug, (-2.17+postY)*scaleToFixBug);
} else if (toImage == 2) {
popUpImage.textAlign(CENTER);
popUpImage.textFont(font, postFontSize*scaleToFixBug);
for(int postY = startPostY; postY <= startPostY+8; postY += 4){
for (int i = (int)(averageX/5-5); i <= (int)(averageX/5+5); i++) {
popUpImage.rect((i*5-0.1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 0.2*scaleToFixBug, 3*scaleToFixBug);
popUpImage.rect((i*5-1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 2*scaleToFixBug, 1*scaleToFixBug);
popUpImage.text(i+" m", i*5*scaleToFixBug, (-2.17+postY)*scaleToFixBug);
void drawArrow(float x) {
textFont(font, postFontSize*scaleToFixBug);
rect((x-1.7)*scaleToFixBug, -4.8*scaleToFixBug, 3.4*scaleToFixBug, 1.1*scaleToFixBug);
vertex(x*scaleToFixBug, -3.2*scaleToFixBug);
vertex((x-0.5)*scaleToFixBug, -3.7*scaleToFixBug);
vertex((x+0.5)*scaleToFixBug, -3.7*scaleToFixBug);
text((float(round(x*2))/10)+" m", x*scaleToFixBug, -3.91*scaleToFixBug);
image(graphImage, 50, 180, 650, 380);
image(segBarImage, 50, 580, 650, 100);
float genWidth = 590.0/gen;
float lineX = 110+genSelected*genWidth;
line(lineX, 180, lineX, 500+180);
Integer[] s = speciesCounts.get(genSelected);
for (int i = 1; i < 101; i++) {
float y = ((s[i]+s[i-1])/2)/1000.0*100+573;
if (i-1 == topSpeciesCounts.get(genSelected)) {
rect(lineX+3, y, 56, 14);
fill(getColor(i-1, true));
text("S"+floor((i-1)/10)+""+((i-1)%10)+": "+c, lineX+5, y+11);
color getColor(int i, boolean adjust) {
float col = (i*1.618034)%1;
if (abs(col-0.333) <= 0.18 && adjust) {
return color(col, 1.0, light);
void drawGraph(int graphWidth, int graphHeight) {
graphImage.background(220);
drawLines(90, int(graphHeight*0.05), graphWidth-90, int(graphHeight*0.9));
drawSegBars(90, 0, graphWidth-90, 150);
void drawLines(int x, int y, int graphWidth, int graphHeight) {
float gh = float(graphHeight);
float genWidth = float(graphWidth)/gen;
float worst = extreme(-1);
float meterHeight = float(graphHeight)/(best-worst);
float zero = (best/(best-worst))*gh;
float unit = setUnit(best, worst);
graphImage.strokeWeight(2);
graphImage.textFont(font, 18);
graphImage.textAlign(RIGHT);
for (float i = ceil((worst-(best-worst)/18.0)/unit)*unit; i < best+(best-worst)/18.0;i+=unit) {
float lineY = y-i*meterHeight+zero;
graphImage.line(x, lineY, graphWidth+x, lineY);
graphImage.text(showUnit(i, unit)+" "+fitnessUnit, x-5, lineY+4);
for (int i = 0; i < 29; i++) {
graphImage.stroke(255, 0, 0, 255);
graphImage.strokeWeight(5);
if (k == 0 || k == 28 || (k >= 10 && k <= 18)) {
graphImage.strokeWeight(3);
graphImage.strokeWeight(1);
for (int j = 0; j < gen; j++) {
graphImage.line(x+j*genWidth, (-percentile.get(j)[k])*meterHeight+zero+y,
x+(j+1)*genWidth, (-percentile.get(j+1)[k])*meterHeight+zero+y);
void drawSegBars(int x, int y, int graphWidth, int graphHeight) {
segBarImage.colorMode(HSB, 1);
segBarImage.background(0, 0, 0.5);
float genWidth = float(graphWidth)/gen;
int gensPerBar = floor(gen/500)+1;
for (int i = 0; i < gen; i+=gensPerBar) {
int i2 = min(i+gensPerBar, gen);
float barX1 = x+i*genWidth;
float barX2 = x+i2*genWidth;
for (int j = 0; j < 100; j++) {
segBarImage.fill(getColor(j, false));
segBarImage.beginShape();
segBarImage.vertex(barX1, y+speciesCounts.get(i)[j]/1000.0*graphHeight);
segBarImage.vertex(barX1, y+speciesCounts.get(i)[j+1]/1000.0*graphHeight);
segBarImage.vertex(barX2, y+speciesCounts.get(i2)[j+1]/1000.0*graphHeight);
segBarImage.vertex(barX2, y+speciesCounts.get(i2)[j]/1000.0*graphHeight);
float extreme(float sign) {
for (int i = 0; i < gen; i++) {
float toTest = percentile.get(i+1)[int(14-sign*14)];
if (toTest*sign > record*sign) {
float setUnit(float best, float worst) {
float unit2 = 3*log(best-worst)/log(10)-2;
return pow(10, floor(unit2/3));
} else if ((unit2+90)%3 < 2) {
return pow(10, floor((unit2-1)/3))*2;
return pow(10, floor((unit2-2)/3))*5;
String showUnit(float i, float unit) {
ArrayList<Creature> quickSort(ArrayList<Creature> c) {
ArrayList<Creature> less = new ArrayList<Creature>();
ArrayList<Creature> more = new ArrayList<Creature>();
ArrayList<Creature> equal = new ArrayList<Creature>();
for (int i = 1; i < c.size(); i++) {
ArrayList<Creature> total = new ArrayList<Creature>();
total.addAll(quickSort(more));
total.addAll(quickSort(less));
void toStableConfiguration(int nodeNum, int muscleNum) {
for (int j = 0; j < 200; j++) {
for (int i = 0; i < muscleNum; i++) {
m.get(i).applyForce(i, n);
for (int i = 0; i < nodeNum; i++) {
for (int i = 0; i < nodeNum; i++) {
void adjustToCenter(int nodeNum) {
for (int i = 0; i < nodeNum; i++) {
if (ni.y+ni.m/2 > lowY) {
for (int i = 0; i < nodeNum; i++) {
for (int i = 0; i < m.size(); i++) {
m.get(i).applyForce(i, n);
for (int i = 0; i < n.size(); i++) {
for (int i = 0; i < n.size(); i++) {
n.get(i).realizeMathValues(i);
averageNodeNausea = totalNodeNausea/n.size();
for (int i = 0; i < n.size(); i++) {
averageX = averageX/n.size();
averageY = averageY/n.size();
ArrayList<Node> n = new ArrayList<Node>();
ArrayList<Muscle> m = new ArrayList<Muscle>();
Creature[] c = new Creature[1000];
ArrayList<Creature> c2 = new ArrayList<Creature>();
void mouseWheel(MouseEvent event) {
float delta = event.getCount();
textFont(font, postFontSize);
textFont(font, postFontSize);
float mX = mouseX/windowSizeMultiplier;
float mY = mouseY/windowSizeMultiplier;
if (menu == 1 && gen >= 1 && abs(mY-365) <= 25 && abs(mX-sliderX-25) <= 25) {
void openMiniSimulation() {
if (statusWindow <= -1) {
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3);
String zeros(int n, int zeros){
for(int i = s.length(); i < zeros; i++){
float mX = mouseX/windowSizeMultiplier;
float mY = mouseY/windowSizeMultiplier;
if (menu == 0 && abs(mX-windowWidth/2) <= 200 && abs(mY-400) <= 100) {
}else if (menu == 1 && gen == -1 && abs(mX-120) <= 100 && abs(mY-300) <= 50) {
}else if (menu == 1 && gen >= 0 && abs(mX-990) <= 230) {
}else if (menu == 3 && abs(mX-1030) <= 130 && abs(mY-684) <= 20) {
} else if (menu == 7 && abs(mX-1030) <= 130 && abs(mY-684) <= 20) {
} else if((menu == 5 || menu == 4) && mY >= windowHeight-40){
for (int s = timer; s < 900; s++) {
}else if(mX >= 120 && mX < 360){
if(speed == 1024) speed = 900;
if(speed >= 1800) speed = 1;
}else if(mX >= windowWidth-120){
for (int s = timer; s < 900; s++) {
for (int i = creaturesTested; i < 1000; i++) {
setGlobalVariables(c[i]);
for (int s = 0; s < 900; s++) {
} else if(menu == 8 && mX < 90 && mY >= windowHeight-40){
} else if (menu == 9 && abs(mX-1030) <= 130 && abs(mY-690) <= 20) {
}else if (menu == 11 && abs(mX-1130) <= 80 && abs(mY-690) <= 20) {
}else if (menu == 13 && abs(mX-1130) <= 80 && abs(mY-690) <= 20) {
void drawScreenImage(int stage) {
screenImage.pushMatrix();
screenImage.scale(15.0/scaleToFixBug);
screenImage.background(220, 253, 102);
for (int j = 0; j < 1000; j++) {
if (stage == 3) cj = c[cj.id-(gen*1000)-1001];
creaturesInPosition[j2] = j;
drawCreature(cj,x*3+5.5, y*2.5+4, 1);
screenImage.pushMatrix();
screenImage.textAlign(CENTER);
screenImage.textFont(font, 24);
screenImage.fill(100, 100, 200);
screenImage.rect(900, 664, 260, 40);
screenImage.text("All 1,000 creatures have been tested. Now let's sort them!", windowWidth/2-200, 690);
screenImage.text("Sort", windowWidth-250, 690);
screenImage.rect(900, 670, 260, 40);
screenImage.text("Fastest creatures at the top!", windowWidth/2, 30);
screenImage.text("Slowest creatures at the bottom. (Going backward = slow)", windowWidth/2-200, 700);
screenImage.text("Kill 500", windowWidth-250, 700);
screenImage.rect(1050, 670, 160, 40);
screenImage.text("Faster creatures are more likely to survive because they can outrun their predators. Slow creatures get eaten.", windowWidth/2, 30);
screenImage.text("Because of random chance, a few fast ones get eaten, while a few slow ones survive.", windowWidth/2-130, 700);
screenImage.text("Reproduce", windowWidth-150, 700);
for (int j = 0; j < 1000; j++) {
drawCreature(cj, x*30+55, y*25+40, 0);
screenImage.rect(x*30+40, y*25+17, 30, 25);
screenImage.rect(1050, 670, 160, 40);
screenImage.text("These are the 1000 creatures of generation #"+(gen+2)+".", windowWidth/2, 30);
screenImage.text("What perils will they face? Find out next time!", windowWidth/2-130, 700);
screenImage.text("Back", windowWidth-150, 700);
camX += (averageX-camX)*0.1;
camY += (averageY-camY)*0.1;
popUpImage.translate(225,225);
popUpImage.scale(1.0/camZoom/scaleToFixBug);
popUpImage.translate(-camX*scaleToFixBug,-camY*scaleToFixBug);
if (simulationTimer < 900) {
popUpImage.background(120, 200, 255);
popUpImage.background(60, 100, 128);
drawCreaturePieces(n, m, 0, 0, 2);
void drawCreature(Creature cj, float x, float y, int toImage) {
for (int i = 0; i < cj.m.size(); i++) {
drawMuscle(cj.m.get(i), cj.n, x, y, toImage);
for (int i = 0; i < cj.n.size(); i++) {
drawNode(cj.n.get(i), x, y, toImage);
for (int i = 0; i < cj.m.size(); i++) {
drawMuscleAxons(cj.m.get(i), cj.n, x, y, toImage);
for (int i = 0; i < cj.n.size(); i++) {
drawNodeAxons(cj.n, i, x, y, toImage);
void drawCreaturePieces(ArrayList<Node> n, ArrayList<Muscle> m, float x, float y, int toImage) {
for (int i = 0; i < m.size(); i++) {
drawMuscle(m.get(i), n, x, y, toImage);
for (int i = 0; i < n.size(); i++) {
drawNode(n.get(i), x, y, toImage);
for (int i = 0; i < m.size(); i++) {
drawMuscleAxons(m.get(i), n, x, y, toImage);
for (int i = 0; i < n.size(); i++) {
drawNodeAxons(n, i, x, y, toImage);
void drawHistogram(int x, int y, int hw, int hh) {
for (int i = 0; i < barLen; i++) {
if (barCounts.get(genSelected)[i] > maxH) {
maxH = barCounts.get(genSelected)[i];
float barW = (float)hw/barLen;
float multiplier = (float)hh/maxH*0.9;
if (maxH < 300) unit = 50;
if (maxH < 100) unit = 20;
if (maxH < 50) unit = 10;
for (int i = 0; i < hh/multiplier; i += unit) {
float theY = y+hh-i*multiplier;
line(x, theY, x+hw, theY);
for (int i = minBar; i <= maxBar; i += 10) {
float theX = x+(i-minBar)*barW;
text(nf((float)i/histBarsPerMeter, 0, 1), theX, y+hh+14);
line(theX, y, theX, y+hh);
for (int i = 0; i < barLen; i++) {
float h = min(barCounts.get(genSelected)[i]*multiplier, hh);
if (i+minBar == floor(percentile.get(min(genSelected, percentile.size()-1))[14]*histBarsPerMeter)) {
rect(x+i*barW, y+hh-h, barW, h);
void drawStatusWindow(boolean isFirstFrame) {
int rank = (statusWindow+1);
stroke(abs(overallTimer%30-15)*17);
cj = c2.get(statusWindow);
int id = ((cj.id-1)%1000);
y = floor(statusWindow/40)+1;
rect(x*30+40, y*25+17, 30, 25);
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3);
x = 760+(statusWindow+3)*160;
rank = ranks[statusWindow+3];
rect(px-60, py, 120, 52);
text("#"+rank, px, py+12);
text("ID: "+cj.id, px, py+24);
text("Fitness: "+nf(cj.d, 0, 3), px, py+36);
int sp = (cj.n.size()%10)*10+(cj.m.size()%10);
fill(getColor(sp, true));
text("Species: S"+(cj.n.size()%10)+""+(cj.m.size()%10), px, py+48);
int px2 = min(max(px-90, 10), 970);
image(popUpImage, px2, py2, 300, 300);
drawStats(px2+295, py2, 0.45);
int shouldBeWatching = statusWindow;
if (statusWindow <= -1) {
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3);
shouldBeWatching = cj.id;
if (creatureWatching != shouldBeWatching || isFirstFrame) {
size((int)(windowWidth*windowSizeMultiplier), (int)(windowHeight*windowSizeMultiplier));
Float[] beginPercentile = new Float[29];
Integer[] beginBar = new Integer[barLen];
Integer[] beginSpecies = new Integer[101];
for (int i = 0; i < 29; i++) {
beginPercentile[i] = 0.0;
for (int i = 0; i < barLen; i++) {
for (int i = 0; i < 101; i++) {
percentile.add(beginPercentile);
speciesCounts.add(beginSpecies);
graphImage = createGraphics(975, 570);
screenImage = createGraphics(1920, 1080);
popUpImage = createGraphics(450, 450);
segBarImage = createGraphics(975, 150);
segBarImage.background(220);
popUpImage.background(220);
font = loadFont("Helvetica-Bold-96.vlw");
scale(windowSizeMultiplier);
rect(windowWidth/2-200, 300, 400, 200);
text("EVOLUTION!", windowWidth/2, 200);
text("START", windowWidth/2, 430);
background(255, 200, 130);
text("Generation "+max(genSelected, 0), 20, 100);
text("Since there are no creatures yet, create 1000 creatures!", 20, 160);
text("They will be randomly created, and also very simple.", 20, 200);
text("Do 1 step-by-step generation.", 770, 50);
text("Do 1 quick generation.", 770, 100);
text("Do 1 gen ASAP.", 770, 150);
text("Do gens ALAP.", 1000, 150);
text("Median "+fitnessName, 50, 160);
text(float(round(percentile.get(min(genSelected, percentile.size()-1))[14]*1000))/1000+" "+fitnessUnit, 700, 160);
drawHistogram(760, 410, 460, 280);
if(saveFramesPerGeneration && gen > lastImageSaved){
saveFrame("imgs//"+zeros(gen,5)+".png");
background(220, 253, 102);
scale(10.0/scaleToFixBug);
for (int y = 0; y < 25; y++) {
for (int x = 0; x < 40; x++) {
int nodeNum = int(random(3, 6));
int muscleNum = int(random(nodeNum-1, nodeNum*3-6));
for (int i = 0; i < nodeNum; i++) {
n.add(new Node(random(-1, 1), random(-1, 1), 0, 0, 0.4, random(0, 1), random(0,1),
floor(random(0,operationCount)),floor(random(0,nodeNum)),floor(random(0,nodeNum))));
for (int i = 0; i < muscleNum; i++) {
int taxon = getNewMuscleAxon(nodeNum);
tc1 = int(random(0, nodeNum));
tc2 = int(random(0, nodeNum));
float len = random(0.5,1.5);
m.add(new Muscle(taxon, tc1, tc2, len, random(0.02, 0.08)));
toStableConfiguration(nodeNum, muscleNum);
float heartbeat = random(40, 80);
c[y*40+x] = new Creature(y*40+x+1, new ArrayList<Node>(n), new ArrayList<Muscle>(m), 0, true, heartbeat, 1.0);
drawCreature(c[y*40+x], x*3+5.5, y*2.5+3, 0);
c[y*40+x].checkForOverlap();
c[y*40+x].checkForLoneNodes();
c[y*40+x].checkForBadAxons();
text("Here are your 1000 randomly generated creatures!!!", windowWidth/2-200, 690);
text("Back", windowWidth-250, 690);
setGlobalVariables(c[creaturesTested]);
for (int i = 0; i < 1000; i++) {
setGlobalVariables(c[i]);
for (int s = 0; s < 900; s++) {
background(120, 200, 255);
for (int s = 0; s < speed; s++) {
for (int s = 0; s < speed; s++) {
camX += (averageX-camX)*0.06;
camY += (averageY-camY)*0.06;
translate(width/2.0, height/2.0);
scale(1.0/camZoom/scaleToFixBug);
translate(-camX*scaleToFixBug,-camY*scaleToFixBug);
drawCreaturePieces(n, m, 0, 0, 0);
drawStats(windowWidth-10, 0,0.7);
rect(0, 0, windowWidth, windowHeight);
rect(windowWidth/2-500, 200, 1000, 240);
text("Creature's "+fitnessName+":", windowWidth/2, 300);
text(nf(averageX*0.2,0,2) + " "+fitnessUnit, windowWidth/2, 400);
setFitness(creaturesTested);
if (creaturesTested == 1000) {
c2 = new ArrayList<Creature>(0);
for(int i = 0; i < 1000; i++){
percentile.add(new Float[29]);
for (int i = 0; i < 29; i++) {
percentile.get(gen+1)[i] = c2.get(p[i]).d;
creatureDatabase.add(c2.get(999).copyCreature(-1));
creatureDatabase.add(c2.get(499).copyCreature(-1));
creatureDatabase.add(c2.get(0).copyCreature(-1));
Integer[] beginBar = new Integer[barLen];
for (int i = 0; i < barLen; i++) {
Integer[] beginSpecies = new Integer[101];
for (int i = 0; i < 101; i++) {
for (int i = 0; i < 1000; i++) {
int bar = floor(c2.get(i).d*histBarsPerMeter-minBar);
if (bar >= 0 && bar < barLen) {
barCounts.get(gen+1)[bar]++;
int species = (c2.get(i).n.size()%10)*10+c2.get(i).m.size()%10;
speciesCounts.add(new Integer[101]);
speciesCounts.get(gen+1)[0] = 0;
for (int i = 0; i < 100; i++) {
speciesCounts.get(gen+1)[i+1] = cum;
if (beginSpecies[i] > record) {
record = beginSpecies[i];
topSpeciesCounts.add(holder);
background(220, 253, 102);
scale(10.0/scaleToFixBug);
float transition = 0.5-0.5*cos(min(float(timer)/60, PI));
for (int j = 0; j < 1000; j++) {
int j2 = cj.id-(gen*1000)-1;
float x3 = inter(x1, x2, transition);
float y3 = inter(y1, y2, transition);
drawCreature(cj, x3*3+5.5, y3*2.5+4, 0);
float mX = mouseX/windowSizeMultiplier;;
float mY = mouseY/windowSizeMultiplier;;
prevStatusWindow = statusWindow;
if (abs(menu-9) <= 2 && gensToDo == 0 && !drag) {
if (abs(mX-639.5) <= 599.5) {
if (menu == 7 && abs(mY-329) <= 312) {
statusWindow = creaturesInPosition[floor((mX-40)/30)+floor((mY-17)/25)*40];
else if (menu >= 9 && abs(mY-354) <= 312) {
statusWindow = floor((mX-40)/30)+floor((mY-42)/25)*40;
} else if (menu == 1 && genSelected >= 1 && gensToDo == 0 && !drag) {
if (abs(mX-990) <= 230) {
float modX = (mX-760)%160;
statusWindow = floor((mX-760)/160)-3;
for (int j = 0; j < 500; j++) {
float rand = (pow(random(-1, 1), 3)+1)/2;
Creature cj = c2.get(j2);
Creature ck = c2.get(j3);
for (int j = 0; j < 500; j++) {
if (!c2.get(j).alive) j2 = 999-j;
Creature cj = c2.get(j2);
Creature cj2 = c2.get(999-j2);
c2.set(j2, cj.copyCreature(cj.id+1000));
c2.set(999-j2, cj.modified(cj2.id+1000));
toStableConfiguration(n.size(), m.size());
adjustToCenter(n.size());
for (int j = 0; j < 1000; j++) {
c[cj.id-(gen*1000)-1001] = cj.copyCreature(-1);
if (menu%2 == 1 && abs(menu-10) <= 3) {
image(screenImage, 0, 0, 1280, 720);
if (menu == 1 || gensToDo >= 1) {
mX = mouseX/windowSizeMultiplier;;
mY = mouseY/windowSizeMultiplier;;
genSelected = round((sliderX-760)*(gen-1)/410)+1;
genSelected = round((sliderX-760)*gen/410);
if (drag) sliderX = min(max(sliderX+(mX-25-sliderX)*0.2, 760), 1170);
rect(sliderX, 340, 50, 50);
fs = floor(log(genSelected)/log(10));
fontSize = fontSizes[fs];
textFont(font, fontSize);
text(genSelected, sliderX+25, 366+fontSize*0.3333);
for (int k = 0; k < 3; k++) {
rect(760+k*160, 180, 140, 140);
translate(830+160*k, 290);
scale(60.0/scaleToFixBug);
drawCreature(creatureDatabase.get((genSelected-1)*3+k),0,0,0);
text("Worst Creature", 830, 310);
text("Median Creature", 990, 310);
text("Best Creature", 1150, 310);
if (justGotBack) justGotBack = false;
if (statusWindow >= -3) {
drawStatusWindow(prevStatusWindow == -4);
if (statusWindow >= -3 && !miniSimulation) {
void drawStats(float x, float y, float size){
text("Creature ID: "+id, 0, 32);
timeShow = int((timer+creaturesTested*37)/60)%15;
text("Time: "+nf(timeShow,0,2)+" / 15 sec.", 0, 64);
text("Playback Speed: x"+max(1,speed), 0, 96);
String extraWord = "used";
if(energyDirection == -1){
text("X: "+nf(averageX/5.0,0,2)+"", 0, 128);
text("Y: "+nf(-averageY/5.0,0,2)+"", 0, 160);
text("Energy "+extraWord+": "+nf(energy,0,2)+" yums", 0, 192);
text("A.N.Nausea: "+nf(averageNodeNausea,0,2)+" blehs", 0, 224);
rect(0,windowHeight-40,90,40);
text("SKIP",45,windowHeight-8);
rect(120,windowHeight-40,240,40);
text("PB speed: x"+speed,240,windowHeight-8);
rect(windowWidth-120,windowHeight-40,120,40);
text("FINISH",windowWidth-60,windowHeight-8);
void setGlobalVariables(Creature thisCreature) {
for (int i = 0; i < thisCreature.n.size(); i++) {
n.add(thisCreature.n.get(i).copyNode());
for (int i = 0; i < thisCreature.m.size(); i++) {
m.add(thisCreature.m.get(i).copyMuscle());
cTimer = thisCreature.creatureTimer;