Last active
July 3, 2023 04:53
-
-
Save realguystuff/1e6d07109369613782811067b9a3e4c8 to your computer and use it in GitHub Desktop.
Code to my stream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Last edited January 17, 2013, UNTIL I brought it back on June 19, 2015! | |
//These are the easy-to-edit variables. | |
final boolean USE_RANDOM_SEED = false; | |
// determines whether random factors will be determined by the preset seed. | |
//If this is false, the program will run differently every time. If it's true, it will run exactly the same. | |
final int SEED = (int) random(-1000, 1000); | |
final float WINDOW_SIZE = 1.0; // window size multiplier. If it's 1, the size is 1280x720. | |
// The seed that determines all random factors in the simulation. Same seed = same simulation results, | |
// different seed = different simulation results. Make sure USE_RANDOM_SEED is true for this. | |
final float SORT_ANIMATION_SPEED = 5.0; // Determines speed of sorting animation. Higher number is faster. | |
final float MINIMUM_NODE_SIZE = 0.4; // Note: all units are 20 cm. Meaning, a value of 1 equates to a 20 cm node. | |
final float MAXIMUM_NODE_SIZE = 0.4; | |
final float MINIMUM_NODE_FRICTION = 0.0; | |
final float MAXIMUM_NODE_FRICTION = 1.0; | |
final float GRAVITY = 0.005; // higher = more friction. | |
final float AIR_FRICTION = 0.95; // The lower the number, the more friction. 1 = no friction. Above 1 = chaos. | |
final float MUTABILITY_FACTOR = 1.0; // How fast the creatures mutate. 1 is normal. | |
boolean haveGround = true; // true if the ground exists, false if no ground. | |
//Add rectangular obstacles by filling up this array of rectangles. The parameters are x1, y1, x2, y2, specifying | |
// two opposite vertices. NOTE: The units are 20 cm, so 1 = 20 cm, and 5 = 1 m. | |
// ALSO NOTE: y-values increase as you go down. So 3 is in the air, and -3 is in the ground. 0 is the surface. | |
final Rectangle[] RECTANGLES = { | |
/*new Rectangle(2,-0.4,7,1), //Example hurdles | |
new Rectangle(4,-0.8,9,1), | |
new Rectangle(6,-1.2,11,1), | |
new Rectangle(8,-1.6,13,1), | |
new Rectangle(10,-2,15,1), | |
new Rectangle(12,-2.4,17,1), | |
new Rectangle(14,-2.8,19,1), | |
new Rectangle(16,-3.2,21,1), | |
new Rectangle(18,-3.6,23,1), | |
new Rectangle(20,-4.0,25,1)*/ | |
}; | |
float histMinValue = -1; //histogram information | |
float histMaxValue = 8; | |
int histBarsPerMeter = 10; | |
// Okay, that's all the easy to edit stuff. | |
PFont font; | |
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); | |
PGraphics graphImage; | |
PGraphics screenImage; | |
PGraphics popUpImage; | |
PGraphics segBarImage; | |
// 0 = 100th percentile | |
// 1 = 90th percentile | |
// ... | |
// 8 = 20th percentile | |
// 9 = 10th percentile | |
// 10 = 9th percentile | |
// 11 = 8th percentile | |
// ... | |
// 19 = 0th percentile | |
final float FRICTION = 4; | |
int minBar = int(histMinValue*histBarsPerMeter); | |
int maxBar = int(histMaxValue*histBarsPerMeter); | |
int barLen = maxBar-minBar; | |
int gensToDo = 0; | |
float cTimer = 60; | |
int windowWidth = 1280; | |
int windowHeight = 720; | |
int timer = 0; | |
float cam = 0; | |
int frames = 60; | |
int menu = 0; | |
int gen = -1; | |
float sliderX = 1170; | |
int genSelected = 0; | |
boolean drag = false; | |
boolean justGotBack = false; | |
int creatures = 0; | |
int creaturesTested = 0; | |
int fontSize = 0; | |
int[] fontSizes = {50,36,25,20,16,14,11,9}; | |
int statusWindow = -4; | |
int overallTimer = 0; | |
boolean miniSimulation = false; | |
int creatureWatching = 0; | |
int simulationTimer = 0; | |
int[] creaturesInPosition = new int[1000]; | |
float camzoom = 0.015; | |
float target; | |
float force; | |
float average; | |
int speed; | |
int id; | |
boolean stepbystep; | |
boolean stepbystepslow; | |
boolean slowDies; | |
int timeShow; | |
int[] p = {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; | |
} | |
float r(){ | |
return pow(random(-1,1),19); | |
} | |
int rInt(){ | |
return int(random(-0.01,1.01)); | |
} | |
class Rectangle{ | |
float x1, y1, x2, y2; | |
Rectangle(float tx1, float ty1, float tx2, float ty2){ | |
x1 = tx1; | |
y1 = ty1; | |
x2 = tx2; | |
y2 = ty2; | |
} | |
} | |
class Node{ | |
float x, y, vx, vy, m, f; | |
Node(float tx, float ty, float tvx, float tvy, float tm, float tf){ | |
x = tx; | |
y = ty; | |
vx = tvx; | |
vy = tvy; | |
m = tm; | |
f = tf; | |
} | |
void applyForces(int i){ | |
Node ni = n.get(i); | |
ni.vx *= AIR_FRICTION; | |
ni.vy *= AIR_FRICTION; | |
ni.y += ni.vy; | |
ni.x += ni.vx; | |
} | |
void applyGravity(int i){ | |
Node ni = n.get(i); | |
ni.vy += GRAVITY; | |
} | |
void hitWalls(int index){ | |
Node ni = n.get(index); | |
float dif = ni.y+ni.m/2; | |
if(dif >= 0 && haveGround){ | |
ni.y = -ni.m/2; | |
ni.vy = 0; | |
ni.x -= ni.vx*ni.f; | |
if(ni.vx > 0){ | |
ni.vx -= ni.f*dif*FRICTION; | |
if(ni.vx < 0){ | |
ni.vx = 0; | |
} | |
}else{ | |
ni.vx += ni.f*dif*FRICTION; | |
if(ni.vx > 0){ | |
ni.vx = 0; | |
} | |
} | |
} | |
for(int i = 0; i < rects.size(); i++){ | |
Rectangle r = rects.get(i); | |
boolean flip = false; | |
float px, py; | |
int section = 0; | |
if(abs(ni.x-(r.x1+r.x2)/2) <= (r.x2-r.x1+ni.m)/2 && abs(ni.y-(r.y1+r.y2)/2) <= (r.y2-r.y1+ni.m)/2){ | |
if(ni.x >= r.x1 && ni.x < r.x2 && ni.y >= r.y1 && ni.y < r.y2){ | |
float d1 = ni.x-r.x1; | |
float d2 = r.x2-ni.x; | |
float d3 = ni.y-r.y1; | |
float d4 = r.y2-ni.y; | |
if(d1 < d2 && d1 < d3 && d1 < d4){ | |
px = r.x1; | |
py = ni.y; | |
section = 3; | |
}else if(d2 < d3 && d2 < d4){ | |
px = r.x2; | |
py = ni.y; | |
section = 5; | |
}else if(d3 < d4){ | |
px = ni.x; | |
py = r.y1; | |
section = 1; | |
}else{ | |
px = ni.x; | |
py = r.y2; | |
section = 7; | |
} | |
flip = true; | |
}else{ | |
if(ni.x < r.x1){ | |
px = r.x1; | |
section = 0; | |
}else if(ni.x < r.x2){ | |
px = ni.x; | |
section = 1; | |
}else{ | |
px = r.x2; | |
section = 2; | |
} | |
if(ni.y < r.y1){ | |
py = r.y1; | |
section += 0; | |
}else if(ni.y < r.y2){ | |
py = ni.y; | |
section += 3; | |
}else{ | |
py = r.y2; | |
section += 6; | |
} | |
} | |
float distance = dist(ni.x,ni.y,px,py); | |
float rad = ni.m/2; | |
float wallAngle = 0; | |
if(distance <= 0.00000001){ // distance is zero, can't use atan2 | |
if(section <= 2){ | |
wallAngle = PI/4.0 + section*PI/4.0; | |
}else if(section >= 6){ | |
wallAngle = 5*PI/4.0 + (8-section)*PI/4.0; | |
}else if(section == 3){ | |
wallAngle = PI; | |
}else if(section == 5 || section == 4){ | |
wallAngle = 0; | |
} | |
flip = false; | |
}else{ | |
wallAngle = atan2(py-ni.y,px-ni.x); | |
} | |
if(flip){ | |
wallAngle += PI; | |
} | |
if(distance < rad || flip){ | |
dif = rad-distance; | |
float multi = rad/distance; | |
if(flip){ | |
multi = -multi; | |
} | |
ni.x = (ni.x-px)*multi+px; | |
ni.y = (ni.y-py)*multi+py; | |
float veloAngle = atan2(ni.vy,ni.vx); | |
float veloMag = dist(0,0,ni.vx,ni.vy); | |
float relAngle = veloAngle-wallAngle; | |
float relY = sin(relAngle)*veloMag*dif*FRICTION; | |
ni.vx = -sin(relAngle)*relY; | |
ni.vy = cos(relAngle)*relY; | |
} | |
} | |
} | |
} | |
Node copyNode(){ | |
return (new Node(x,y,0,0,m,f)); | |
} | |
Node modifyNode(float mutability){ | |
float newX = x+r()*0.5*mutability*MUTABILITY_FACTOR; | |
float newY = y+r()*0.5*mutability*MUTABILITY_FACTOR; | |
float newM = m+r()*0.1*mutability*MUTABILITY_FACTOR; | |
newM = min(max(newM,MINIMUM_NODE_SIZE),MAXIMUM_NODE_SIZE); | |
float newF = f+r()*0.1*mutability*MUTABILITY_FACTOR; | |
newF = min(max(newF,MINIMUM_NODE_FRICTION),MAXIMUM_NODE_FRICTION); | |
return (new Node(newX,newY,0,0,newM,newF));//max(m+r()*0.1,0.2),min(max(f+r()*0.1,0),1) | |
} | |
} | |
class Muscle{ | |
int period, c1, c2; | |
float contractTime,contractLength, extendTime, extendLength; | |
float thruPeriod; | |
boolean contracted; | |
float rigidity; | |
Muscle(int tperiod, int tc1, int tc2, float tcontractTime, | |
float textendTime, float tcontractLength, float textendLength, boolean tcontracted, float trigidity){ | |
period = tperiod; | |
c1 = tc1; | |
c2 = tc2; | |
contractTime = tcontractTime; | |
extendTime = textendTime; | |
contractLength = tcontractLength; | |
extendLength = textendLength; | |
contracted = tcontracted; | |
rigidity = trigidity; | |
} | |
void applyForce(int i, float target){ | |
Node ni1 = n.get(c1); | |
Node ni2 = n.get(c2); | |
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; | |
} | |
Muscle copyMuscle(){ | |
return new Muscle(period,c1,c2,contractTime,extendTime, | |
contractLength,extendLength,contracted,rigidity); | |
} | |
Muscle modifyMuscle(int nodeNum,float mutability){ | |
int newc1 = c1; | |
int newc2 = c2; | |
if(random(0,1)<0.02*mutability*MUTABILITY_FACTOR){ | |
newc1 = int(random(0,nodeNum)); | |
} | |
if(random(0,1)<0.02*mutability*MUTABILITY_FACTOR){ | |
newc2 = int(random(0,nodeNum)); | |
} | |
float newR = min(max(rigidity*(1+r()*0.9*mutability*MUTABILITY_FACTOR),0.01),0.08); | |
float maxMuscleChange = 1+0.025/newR; | |
float newCL = min(max(contractLength+r()*mutability*MUTABILITY_FACTOR,0.4),2); | |
float newEL = min(max(extendLength+r()*mutability*MUTABILITY_FACTOR,0.4),2); | |
float newCL2 = min(newCL,newEL); | |
float newEL2 = min(max(newCL,newEL),newCL2*maxMuscleChange); | |
float newCT = contractTime; | |
float newET = extendTime; | |
if(random(0,1) < 0.5){ //contractTime is changed | |
newCT = ((contractTime-extendTime)*r()*mutability*MUTABILITY_FACTOR+newCT+1)%1; | |
}else{ //extendTime is changed | |
newET = ((extendTime-contractTime)*r()*mutability*MUTABILITY_FACTOR+newET+1)%1; | |
} | |
return new Muscle(max(period+rInt(),0), | |
newc1,newc2,newCT,newET,newCL2,newEL2,isItContracted(newCT,newET),newR); | |
} | |
} | |
class Creature{ | |
ArrayList<Node> n; | |
ArrayList<Muscle> m; | |
float d; | |
int id; | |
boolean alive; | |
float creatureTimer; | |
float mutability; | |
Creature(int tid, ArrayList<Node> tn, ArrayList<Muscle> tm, float td, boolean talive, float tct, float tmut){ | |
id = tid; | |
m = tm; | |
n = tn; | |
d = td; | |
alive = talive; | |
creatureTimer = tct; | |
mutability = tmut; | |
} | |
Creature modified(int id){ | |
Creature modifiedCreature = new Creature(id, | |
new ArrayList<Node>(0),new ArrayList<Muscle>(0),0,true, | |
creatureTimer+r()*16*mutability*MUTABILITY_FACTOR,min(mutability*MUTABILITY_FACTOR*random(0.8,1.25),2)); | |
for(int i = 0; i < n.size(); i++){ | |
modifiedCreature.n.add(n.get(i).modifyNode(mutability)); | |
} | |
for(int i = 0; i < m.size(); i++){ | |
modifiedCreature.m.add(m.get(i).modifyMuscle(n.size(),mutability)); | |
} | |
if(random(0,1) < 0.04*mutability*MUTABILITY_FACTOR || n.size() <= 2){ //Add a node | |
modifiedCreature.addRandomNode(); | |
} | |
if(random(0,1) < 0.04*mutability*MUTABILITY_FACTOR){ //Add a muscle | |
modifiedCreature.addRandomMuscle(-1,-1); | |
} | |
if(random(0,1) < 0.04*mutability*MUTABILITY_FACTOR && modifiedCreature.n.size() >= 4){ //Remove a node | |
modifiedCreature.removeRandomNode(); | |
} | |
if(random(0,1) < 0.04*mutability*MUTABILITY_FACTOR && modifiedCreature.m.size() >= 2){ //Remove a muscle | |
modifiedCreature.removeRandomMuscle(); | |
} | |
modifiedCreature.checkForOverlap(); | |
modifiedCreature.checkForLoneNodes(); | |
return modifiedCreature; | |
} | |
void checkForOverlap(){ | |
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){ | |
bads.add(i); | |
}else if(m.get(i).c1 == m.get(j).c2 && m.get(i).c2 == m.get(j).c1){ | |
bads.add(i); | |
}else if(m.get(i).c1 == m.get(i).c2){ | |
bads.add(i); | |
} | |
} | |
} | |
for(int i = bads.size()-1; i >= 0; i--){ | |
int b = bads.get(i)+0; | |
if(b < m.size()){ | |
m.remove(b); | |
} | |
} | |
} | |
void checkForLoneNodes(){ | |
if(n.size() >= 3){ | |
for(int i = 0; i < n.size(); i++){ | |
int connections = 0; | |
int connectedTo = -1; | |
for(int j = 0; j < m.size(); j++){ | |
if(m.get(j).c1 == i){ | |
connections++; | |
connectedTo = m.get(j).c2; | |
}else if(m.get(j).c2 == i){ | |
connections++; | |
connectedTo = m.get(j).c1; | |
} | |
} | |
if(connections <= 1){ | |
int newConnectionNode = floor(random(0,n.size())); | |
while(newConnectionNode == i || newConnectionNode == connectedTo){ | |
newConnectionNode = floor(random(0,n.size())); | |
} | |
addRandomMuscle(i,newConnectionNode); | |
} | |
} | |
} | |
} | |
void addRandomNode(){ | |
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; | |
n.add(new Node(x,y,0,0,random(MINIMUM_NODE_SIZE,MAXIMUM_NODE_SIZE), | |
random(MINIMUM_NODE_FRICTION,MAXIMUM_NODE_FRICTION))); //random(0.1,1),random(0,1) | |
int nextClosestNode = 0; | |
float record = 100000; | |
for(int i = 0; i < n.size()-1; i++){ | |
if(i != parentNode){ | |
float dx = n.get(i).x-x; | |
float dy = n.get(i).y-y; | |
if(sqrt(dx*dx+dy*dy) < record){ | |
record = sqrt(dx*dx+dy*dy); | |
nextClosestNode = i; | |
} | |
} | |
} | |
addRandomMuscle(parentNode,n.size()-1); | |
addRandomMuscle(nextClosestNode,n.size()-1); | |
} | |
void addRandomMuscle(int tc1, int tc2){ | |
if(tc1 == -1){ | |
tc1 = int(random(0,n.size())); | |
tc2 = tc1; | |
while(tc2 == tc1 && n.size() >= 2){ | |
tc2 = int(random(0,n.size())); | |
} | |
} | |
float rlength1 = random(0.5,1.5); | |
float rlength2 = random(0.5,1.5); | |
float rtime1 = random(0,1); | |
float rtime2 = random(0,1); | |
if(tc1 != -1){ | |
float distance = dist(n.get(tc1).x,n.get(tc1).y,n.get(tc2).x,n.get(tc2).y); | |
float ratio = random(0.01,0.2); | |
rlength1 = distance*(1-ratio); | |
rlength2 = distance*(1+ratio); | |
} | |
m.add(new Muscle(int(random(1,3)),tc1,tc2,rtime1,rtime2, | |
min(rlength1,rlength2),max(rlength1,rlength2),isItContracted(rtime1,rtime2),random(0.02,0.08))); | |
} | |
void removeRandomNode(){ | |
int choice = floor(random(0,n.size())); | |
n.remove(choice); | |
int i = 0; | |
while(i < m.size()){ | |
if(m.get(i).c1 == choice || m.get(i).c2 == choice){ | |
m.remove(i); | |
}else{ | |
i++; | |
} | |
} | |
for(int j = 0; j < m.size(); j++){ | |
if(m.get(j).c1 >= choice){ | |
m.get(j).c1--; | |
} | |
if(m.get(j).c2 >= choice){ | |
m.get(j).c2--; | |
} | |
} | |
} | |
void removeRandomMuscle(){ | |
int choice = floor(random(0,m.size())); | |
m.remove(choice); | |
} | |
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(n.get(i).copyNode()); | |
} | |
for(int i = 0; i < m.size(); i++){ | |
m2.add(m.get(i).copyMuscle()); | |
} | |
if(newID == -1){ | |
newID = id; | |
} | |
return new Creature(newID,n2,m2,d,alive,creatureTimer,mutability); | |
} | |
} | |
void drawGround(int toImage){ | |
if(toImage == 0){ | |
noStroke(); | |
fill(0,130,0); | |
if(haveGround) rect(0,windowHeight*0.8,windowWidth,windowHeight*0.2); | |
for(int i = 0; i < rects.size(); i++){ | |
Rectangle r = rects.get(i); | |
rect(r.x1/camzoom-cam/camzoom+windowWidth/2,r.y1/camzoom+windowHeight*0.8,(r.x2-r.x1)/camzoom,(r.y2-r.y1)/camzoom); | |
} | |
}else if(toImage == 2){ | |
popUpImage.noStroke(); | |
popUpImage.fill(0,130,0); | |
if(haveGround) popUpImage.rect(0,360,450,90); | |
float ww = 450; | |
float wh = 450; | |
for(int i = 0; i < rects.size(); i++){ | |
Rectangle r = rects.get(i); | |
popUpImage.rect(r.x1/camzoom-cam/camzoom+ww/2,r.y1/camzoom+wh*0.8,(r.x2-r.x1)/camzoom,(r.y2-r.y1)/camzoom); | |
} | |
} | |
} | |
void drawNode(Node ni, float x, float y,int toImage){ | |
color c = color(512-int(ni.f*512),0,0); | |
if(ni.f <= 0.5){ | |
c = color(255,255-int(ni.f*512),255-int(ni.f*512)); | |
} | |
if(toImage == 0){ | |
fill(c); | |
noStroke(); | |
ellipse(ni.x/camzoom+x,ni.y/camzoom+y,ni.m/camzoom,ni.m/camzoom); | |
}else if(toImage == 1){ | |
screenImage.fill(c); | |
screenImage.noStroke(); | |
screenImage.ellipse(ni.x/camzoom+x,ni.y/camzoom+y,ni.m/camzoom,ni.m/camzoom); | |
}else if(toImage == 2){ | |
popUpImage.fill(c); | |
popUpImage.noStroke(); | |
popUpImage.ellipse(ni.x/camzoom+x,ni.y/camzoom+y,ni.m/camzoom,ni.m/camzoom); | |
} | |
} | |
void drawMuscle(Muscle mi, Node ni1, Node ni2, float x,float y,int toImage){ | |
boolean c = mi.contracted; | |
float w = 0.1/camzoom; | |
if(c){ | |
w = 0.2/camzoom; | |
} | |
if(toImage == 0){ | |
strokeWeight(w); | |
stroke(70,35,0,mi.rigidity*3000); | |
line(ni1.x/camzoom+x, ni1.y/camzoom+y, ni2.x/camzoom+x, ni2.y/camzoom+y); | |
}else if(toImage == 1){ | |
screenImage.strokeWeight(w); | |
screenImage.stroke(70,35,0,mi.rigidity*3000); | |
screenImage.line(ni1.x/camzoom+x, ni1.y/camzoom+y, ni2.x/camzoom+x, ni2.y/camzoom+y); | |
}else if(toImage == 2){ | |
popUpImage.strokeWeight(w); | |
popUpImage.stroke(70,35,0,mi.rigidity*3000); | |
popUpImage.line(ni1.x/camzoom+x, ni1.y/camzoom+y, ni2.x/camzoom+x, ni2.y/camzoom+y); | |
} | |
} | |
void drawPosts(int toImage){ | |
if(toImage == 0){ | |
noStroke(); | |
for(int i = int((-cam*camzoom-windowWidth/2)/5)-1; | |
i <= int((-cam*camzoom+windowWidth/2)/5)+1; i++){ | |
fill(255); | |
rect(windowWidth/2+(i*5-cam-0.1)/camzoom,windowHeight*0.8-3/camzoom,0.2/camzoom,3/camzoom); | |
rect(windowWidth/2+(i*5-cam-1)/camzoom,windowHeight*0.8-3/camzoom,2/camzoom,1/camzoom); | |
fill(120); | |
text(i+" m",windowWidth/2+(i*5-cam)/camzoom,windowHeight*0.8-2.17/camzoom); | |
} | |
}else if(toImage == 2){ | |
popUpImage.textAlign(CENTER); | |
popUpImage.textFont(font, 0.96/camzoom); | |
popUpImage.noStroke(); | |
float w = 450; | |
float h = 450; | |
for(int i = int((-cam*camzoom-w/2)/5)-1; | |
i <= int((-cam*camzoom+w/2)/5)+1; i++){ | |
popUpImage.fill(255); | |
popUpImage.rect(w/2+(i*5-cam-0.1)/camzoom,h*0.8-3/camzoom,0.2/camzoom,3/camzoom); | |
popUpImage.rect(w/2+(i*5-cam-1)/camzoom,h*0.8-3/camzoom,2/camzoom,1/camzoom); | |
popUpImage.fill(120); | |
popUpImage.text(i+" m",w/2+(i*5-cam)/camzoom,h*0.8-2.17/camzoom); | |
} | |
} | |
} | |
void drawArrow(float x){ | |
noStroke(); | |
fill(120,0,255); | |
rect(windowWidth/2+(x-cam-1.7)/camzoom,windowHeight*0.8-4.8/camzoom,3.4/camzoom,1.1/camzoom); | |
beginShape(); | |
vertex(windowWidth/2+(x-cam)/camzoom,windowHeight*0.8-3.2/camzoom); | |
vertex(windowWidth/2+(x-cam-0.5)/camzoom,windowHeight*0.8-3.7/camzoom); | |
vertex(windowWidth/2+(x-cam+0.5)/camzoom,windowHeight*0.8-3.7/camzoom); | |
endShape(CLOSE); | |
fill(255); | |
text((float(round(x*2))/10)+" m",windowWidth/2+(x-cam)/camzoom,windowHeight*0.8-3.91/camzoom); | |
} | |
void drawGraphImage(){ | |
image(graphImage,50,180,650,380); | |
image(segBarImage,50,580,650,100); | |
if(gen >= 1){ | |
stroke(0,160,0,255); | |
strokeWeight(3); | |
float genWidth = float(610)/gen; | |
float lineX = 90+genSelected*genWidth; | |
line(lineX,180,lineX,500+180); | |
Integer[] s = speciesCounts.get(genSelected); | |
textAlign(LEFT); | |
textFont(font,12); | |
noStroke(); | |
for(int i = 1; i < 101; i++){ | |
int c = s[i]-s[i-1]; | |
if(c >= 25){ | |
float y = ((s[i]+s[i-1])/2)/1000.0*100+573; | |
if(i-1 == topSpeciesCounts.get(genSelected)){ | |
stroke(0); | |
strokeWeight(2); | |
}else{ | |
noStroke(); | |
} | |
fill(255,255,255); | |
rect(lineX+10,y,50,14); | |
colorMode(HSB,1.0); | |
fill(getColor(i-1,true)); | |
text("S"+floor((i-1)/10)+""+((i-1)%10)+": "+c,lineX+11,y+12); | |
colorMode(RGB,255); | |
} | |
} | |
noStroke(); | |
} | |
} | |
color getColor(int i, boolean adjust){ | |
colorMode(HSB,1.0); | |
float col = (i*1.618034)%1; | |
if(i == 46){ | |
col = 0.083333; | |
}else if(i == 44){ | |
col = 0.1666666; | |
}else if(i == 57){ | |
col = 0.5; | |
} | |
float light = 1.0; | |
if(abs(col-0.333) <= 0.18 && adjust){ | |
light = 0.7; | |
} | |
return color(col,1.0,light); | |
} | |
void drawGraph(int graphWidth, int graphHeight){ | |
drawLines(60,int(graphHeight*0.05),graphWidth-60,int(graphHeight*0.9)); | |
if(gen >= 1){ | |
drawSegBars(60,0,graphWidth-60,150); | |
} | |
} | |
void drawLines(int x, int y, int graphWidth, int graphHeight){ | |
graphImage.beginDraw(); | |
graphImage.smooth(); | |
graphImage.background(220); | |
if(gen >= 1){ | |
float gh = float(graphHeight); | |
float genWidth = float(graphWidth)/gen; | |
float best = extreme(1); | |
float worst = extreme(-1); | |
float meterHeight = float(graphHeight)/(best-worst); | |
float zero = (best/(best-worst))*gh; | |
float unit = setUnit(best, worst); | |
graphImage.stroke(150); | |
graphImage.strokeWeight(2); | |
graphImage.fill(150); | |
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)+" m",x-5,lineY+4); | |
} | |
graphImage.stroke(0); | |
for(int i = 0; i < 29; i++){ | |
int k; | |
if(i == 28){ | |
k = 14; | |
}else if(i < 14){ | |
k = i; | |
}else{ | |
k = i+1; | |
} | |
if(k == 14){ | |
graphImage.stroke(255,0,0,255); | |
graphImage.strokeWeight(5); | |
}else{ | |
stroke(0); | |
if(k == 0 || k == 28 || (k >= 10 && k <= 18)){ | |
graphImage.strokeWeight(3); | |
}else{ | |
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); | |
} | |
} | |
} | |
graphImage.endDraw(); | |
} | |
void drawSegBars(int x, int y, int graphWidth, int graphHeight){ | |
segBarImage.beginDraw(); | |
segBarImage.smooth(); | |
segBarImage.noStroke(); | |
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; | |
int cum = 0; | |
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); | |
segBarImage.endShape(); | |
} | |
} | |
segBarImage.endDraw(); | |
colorMode(RGB, 255); | |
} | |
float extreme(float sign){ | |
float record = -sign; | |
for(int i = 0; i < gen; i++){ | |
float toTest = percentile.get(i+1)[int(14-sign*14)]; | |
if(toTest*sign > record*sign){ | |
record = toTest; | |
} | |
} | |
return record; | |
} | |
float setUnit(float best, float worst){ | |
float unit2 = 3*log(best-worst)/log(10)-3.3; | |
if((unit2+100)%3 < 1){ | |
return pow(10,int(unit2/3)); | |
}else if((unit2+100)%3 < 2){ | |
return pow(10,int((unit2-1)/3))*2; | |
}else{ | |
return pow(10,int((unit2-2)/3))*5; | |
} | |
} | |
String showUnit(float i, float unit){ | |
if(unit < 1){ | |
return nf(i,0,2)+""; | |
}else{ | |
return int(i)+""; | |
} | |
} | |
ArrayList<Creature> quickSort(ArrayList<Creature> c){ | |
if(c.size() <= 1){ | |
return c; | |
}else{ | |
ArrayList<Creature> less = new ArrayList<Creature>(); | |
ArrayList<Creature> more = new ArrayList<Creature>(); | |
ArrayList<Creature> equal = new ArrayList<Creature>(); | |
Creature c0 = c.get(0); | |
equal.add(c0); | |
for(int i = 1; i < c.size(); i++){ | |
Creature ci = c.get(i); | |
if(ci.d == c0.d){ | |
equal.add(ci); | |
}else if(ci.d < c0.d){ | |
less.add(ci); | |
}else{ | |
more.add(ci); | |
} | |
} | |
ArrayList<Creature> total = new ArrayList<Creature>(); | |
total.addAll(quickSort(more)); | |
total.addAll(equal); | |
total.addAll(quickSort(less)); | |
return total; | |
} | |
} | |
boolean isItContracted(float rtime1, float rtime2){ | |
boolean contracted; | |
if(rtime1 <= rtime2){ | |
return true; | |
}else{ | |
return false; | |
} | |
} | |
void toStableConfiguration(int nodeNum, int muscleNum){ | |
for(int j = 0; j < 200; j++){ | |
for(int i = 0; i < muscleNum; i++){ | |
Muscle mi = m.get(i); | |
if(mi.contracted){ | |
target = mi.contractLength; | |
}else{ | |
target = mi.extendLength; | |
} | |
mi.applyForce(i,target); | |
} | |
for(int i = 0; i < nodeNum; i++){ | |
Node ni = n.get(i); | |
ni.applyForces(i); | |
} | |
} | |
for(int i = 0; i < nodeNum; i++){ | |
Node ni = n.get(i); | |
ni.vx = 0; | |
ni.vy = 0; | |
} | |
} | |
void adjustToCenter(int nodeNum){ | |
float avx = 0; | |
float lowY = -1000; | |
for(int i = 0; i < nodeNum; i++){ | |
Node ni = n.get(i); | |
avx += ni.x; | |
if(ni.y+ni.m/2 > lowY){ | |
lowY = ni.y+ni.m/2; | |
} | |
} | |
avx /= nodeNum; | |
for(int i = 0; i < nodeNum; i++){ | |
Node ni = n.get(i); | |
ni.x -= avx; | |
ni.y -= lowY; | |
} | |
} | |
void setGlobalVariables(Creature thisCreature){ | |
n.clear(); | |
m.clear(); | |
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()); | |
} | |
id = thisCreature.id; | |
timer = 0; | |
camzoom = 0.01; | |
cam = 0; | |
cTimer = thisCreature.creatureTimer; | |
simulationTimer = 0; | |
} | |
void simulate(){ | |
for(int i = 0; i < m.size(); i++){ | |
Muscle mi = m.get(i); | |
mi.thruPeriod = ((float(timer)/cTimer)/float(mi.period))%float(1); | |
if((mi.thruPeriod <= mi.extendTime && mi.extendTime <= mi.contractTime) || | |
(mi.contractTime <= mi.thruPeriod && mi.thruPeriod <= mi.extendTime) || | |
(mi.extendTime <= mi.contractTime && mi.contractTime <= mi.thruPeriod)){ | |
target = mi.contractLength; | |
mi.contracted = true; | |
}else{ | |
target = mi.extendLength; | |
mi.contracted = false; | |
} | |
mi.applyForce(i,target); | |
} | |
for(int i = 0; i < n.size(); i++){ | |
Node ni = n.get(i); | |
ni.applyForces(i); | |
ni.applyGravity(i); | |
ni.hitWalls(i); | |
} | |
simulationTimer++; | |
} | |
void setAverage(){ | |
average = 0; | |
for(int i = 0; i < n.size(); i++){ | |
Node ni = n.get(i); | |
average += ni.x; | |
} | |
average = average/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) { | |
int delta = event.getCount(); | |
if(menu == 5){ | |
if(delta == -1){ | |
camzoom *= 0.9090909; | |
if(camzoom < 0.006){ | |
camzoom = 0.006; | |
} | |
textFont(font, 0.96/camzoom); | |
}else if(delta == 1){ | |
camzoom *= 1.1; | |
if(camzoom > 0.1){ | |
camzoom = 0.1; | |
} | |
textFont(font, 0.96/camzoom); | |
} | |
} | |
} | |
void mousePressed(){ | |
if(gensToDo >= 1){ | |
gensToDo = 0; | |
} | |
float mX = mouseX/WINDOW_SIZE; | |
float mY = mouseY/WINDOW_SIZE; | |
if(menu == 1 && gen >= 1 && abs(mY-365) <= 25 && abs(mX-sliderX-25) <= 25){ | |
drag = true; | |
} | |
} | |
void openMiniSimulation(){ | |
simulationTimer = 0; | |
if(gensToDo == 0){ | |
miniSimulation = true; | |
int id; | |
Creature cj; | |
if(statusWindow <= -1){ | |
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3); | |
id = cj.id; | |
}else{ | |
id = statusWindow; | |
cj = c2.get(id); | |
} | |
setGlobalVariables(cj); | |
creatureWatching = id; | |
} | |
} | |
void setMenu(int m){ | |
menu = m; | |
if(m == 1){ | |
drawGraph(975,570); | |
} | |
} | |
void startASAP(){ | |
setMenu(4); | |
creaturesTested = 0; | |
stepbystep = false; | |
stepbystepslow = false; | |
} | |
void mouseReleased(){ | |
drag = false; | |
miniSimulation = false; | |
float mX = mouseX/WINDOW_SIZE; | |
float mY = mouseY/WINDOW_SIZE; | |
if(menu == 0 && abs(mX-windowWidth/2) <= 200 && abs(mY-400) <= 100){ | |
setMenu(1); | |
}else if(menu == 1 && gen == -1 && abs(mX-120) <= 100 && abs(mY-300) <= 50){ | |
setMenu(2); | |
}else if(menu == 1 && gen >= 0 && abs(mX-990) <= 230){ | |
if(abs(mY-40) <= 20){ | |
setMenu(4); | |
creaturesTested = 0; | |
stepbystep = true; | |
stepbystepslow = true; | |
} | |
if(abs(mY-90) <= 20){ | |
setMenu(4); | |
creaturesTested = 0; | |
stepbystep = true; | |
stepbystepslow = false; | |
} | |
if(abs(mY-140) <= 20){ | |
if(mX < 990){ | |
gensToDo = 1; | |
}else{ | |
gensToDo = 1000000000; | |
} | |
startASAP(); | |
} | |
}else if(menu == 3 && abs(mX-1030) <= 130 && abs(mY-684) <= 20){ | |
gen = 0; | |
setMenu(1); | |
}else if(menu == 7 && abs(mX-1030) <= 130 && abs(mY-684) <= 20){ | |
setMenu(8); | |
}else if(menu == 9 && abs(mX-1030) <= 130 && abs(mY-690) <= 20){ | |
setMenu(10); | |
}else if(menu == 11 && abs(mX-1130) <= 80 && abs(mY-690) <= 20){ | |
setMenu(12); | |
}else if(menu == 13 && abs(mX-1130) <= 80 && abs(mY-690) <= 20){ | |
setMenu(1); | |
} | |
} | |
void drawScreenImage(int stage){ | |
screenImage.beginDraw(); | |
screenImage.smooth(); | |
screenImage.background(220,253,102); | |
screenImage.noStroke(); | |
camzoom = 0.12; | |
for(int j = 0; j < 1000; j++){ | |
Creature cj = c2.get(j); | |
if(stage == 3) cj = c[cj.id-(gen*1000)-1001]; | |
int j2 = j; | |
if(stage == 0){ | |
j2 = cj.id-(gen*1000)-1; | |
creaturesInPosition[j2] = j; | |
} | |
int x = j2%40; | |
int y = floor(j2/40); | |
if(stage >= 1) y++; | |
drawCreatureWhole(cj,x*30+55,y*25+40,1); | |
} | |
timer = 0; | |
screenImage.textAlign(CENTER); | |
screenImage.textFont(font, 24); | |
screenImage.fill(100,100,200); | |
if(stage == 0){ | |
screenImage.rect(900,664,260,40); | |
screenImage.fill(0); | |
screenImage.text("All 1,000 creatures have been tested. Now let's sort them!",windowWidth/2-200,690); | |
screenImage.text("Sort",windowWidth-250,690); | |
}else if(stage == 1){ | |
screenImage.rect(900,670,260,40); | |
screenImage.fill(0); | |
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); | |
}else if(stage == 2){ | |
screenImage.rect(1050,670,160,40); | |
screenImage.fill(0); | |
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++){ | |
Creature cj = c2.get(j); | |
int x = j%40; | |
int y = floor(j/40)+1; | |
if(cj.alive){ | |
drawCreatureWhole(cj,x*30+55,y*25+40,0); | |
}else{ | |
screenImage.rect(x*30+40,y*25+17,30,25); | |
} | |
} | |
}else if(stage == 3){ | |
screenImage.rect(1050,670,160,40); | |
screenImage.fill(0); | |
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); | |
} | |
screenImage.endDraw(); | |
} | |
void drawpopUpImage(){ | |
camzoom = 0.015; | |
setAverage(); | |
cam += (average-cam)*0.1; | |
popUpImage.beginDraw(); | |
popUpImage.smooth(); | |
if(simulationTimer < 900){ | |
popUpImage.background(120,200,255); | |
}else{ | |
popUpImage.background(60,100,128); | |
} | |
drawPosts(2); | |
drawGround(2); | |
drawCreature(n,m,-cam/camzoom+450/2, 450*0.8,2); | |
popUpImage.noStroke(); | |
popUpImage.endDraw(); | |
} | |
void drawCreatureWhole(Creature cj, float x, float y, int toImage){ | |
for(int i = 0; i < cj.m.size(); i++){ | |
Muscle mi = cj.m.get(i); | |
drawMuscle(mi,cj.n.get(mi.c1),cj.n.get(mi.c2),x,y,toImage); | |
} | |
for(int i = 0; i < cj.n.size(); i++){ | |
drawNode(cj.n.get(i),x,y,toImage); | |
} | |
} | |
void drawCreature(ArrayList<Node> n, ArrayList<Muscle> m, float x, float y, int toImage){ | |
for(int i = 0; i < m.size(); i++){ | |
Muscle mi = m.get(i); | |
drawMuscle(mi,n.get(mi.c1),n.get(mi.c2),x,y,toImage); | |
} | |
for(int i = 0; i < n.size(); i++){ | |
drawNode(n.get(i),x,y,toImage); | |
} | |
} | |
void drawHistogram(int x, int y, int hw, int hh){ | |
int maxH = 1; | |
for(int i = 0; i < barLen; i++){ | |
if(barCounts.get(genSelected)[i] > maxH){ | |
maxH = barCounts.get(genSelected)[i]; | |
} | |
} | |
fill(200); | |
noStroke(); | |
rect(x,y,hw,hh); | |
fill(0,0,0); | |
float barW = (float)hw/barLen; | |
float multiplier = (float)hh/maxH*0.9; | |
textAlign(LEFT); | |
textFont(font,16); | |
stroke(128); | |
strokeWeight(2); | |
int unit = 100; | |
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); | |
if(i == 0) theY -= 5; | |
text(i,x+hw+5,theY+7); | |
} | |
textAlign(CENTER); | |
for(int i = minBar; i <= maxBar; i ++){ | |
if(i%10 == 0){ | |
if(i == 0){ | |
stroke(0,0,255); | |
}else{ | |
stroke(128); | |
} | |
float theX = x+(i-minBar)*barW; | |
text(nf((float)i/histBarsPerMeter,0,1),theX,y+hh+14); | |
line(theX,y,theX,y+hh); | |
} | |
} | |
noStroke(); | |
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)){ | |
fill(255,0,0); | |
}else{ | |
fill(0,0,0); | |
} | |
rect(x+i*barW,y+hh-h,barW,h); | |
} | |
} | |
void drawStatusWindow(){ | |
int x, y, px, py; | |
int rank = (statusWindow+1); | |
Creature cj; | |
stroke(abs(overallTimer%30-15)*17); | |
strokeWeight(3); | |
noFill(); | |
if(statusWindow >= 0){ | |
cj = c2.get(statusWindow); | |
if(menu == 7){ | |
int id = ((cj.id-1)%1000); | |
x = id%40; | |
y = floor(id/40); | |
}else{ | |
x = statusWindow%40; | |
y = floor(statusWindow/40)+1; | |
} | |
px = x*30+55; | |
py = y*25+10; | |
if(px <= 1140){ | |
px += 80; | |
}else{ | |
px -= 80; | |
} | |
rect(x*30+40,y*25+17,30,25); | |
}else{ | |
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3); | |
x = 760+(statusWindow+3)*160; | |
y = 180; | |
px = x; | |
py = y; | |
rect(x,y,140,140); | |
int[] ranks = {1000,500,1}; | |
rank = ranks[statusWindow+3]; | |
} | |
noStroke(); | |
fill(255); | |
rect(px-60,py,120,52); | |
fill(0); | |
textFont(font,12); | |
textAlign(CENTER); | |
text("#"+rank,px,py+12); | |
text("ID: "+cj.id,px,py+24); | |
text("Fitness: "+nf(cj.d,0,3),px,py+36); | |
colorMode(HSB,1); | |
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); | |
colorMode(RGB,255); | |
if(miniSimulation){ | |
int py2 = py-125; | |
if(py >= 360){ | |
py2 -= 180; | |
}else{ | |
py2 += 180; | |
} | |
//py = min(max(py,0),420); | |
int px2 = min(max(px-90,10),970); | |
drawpopUpImage(); | |
image(popUpImage,px2,py2,300,300); | |
fill(255,255,255); | |
rect(px2+240,py2+10,50,30); | |
rect(px2+10,py2+10,100,30); | |
fill(0,0,0); | |
textFont(font,30); | |
textAlign(RIGHT); | |
text(int(simulationTimer/60),px2+285,py2+36); | |
textAlign(LEFT); | |
text(nf(average/5.0,0,3),px2+15,py2+36); | |
simulate(); | |
timer++; | |
int shouldBeWatching = statusWindow; | |
if(statusWindow <= -1){ | |
cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3); | |
shouldBeWatching = cj.id; | |
} | |
if(creatureWatching != shouldBeWatching){ | |
openMiniSimulation(); | |
} | |
} | |
} | |
void setup(){ | |
size(1280, 720, P2D); // Don't change this. It ruins everything. | |
if(USE_RANDOM_SEED){ | |
randomSeed(SEED); | |
} | |
println("seed: "+SEED); | |
smooth(); | |
ellipseMode(CENTER); | |
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++){ | |
beginBar[i] = 0; | |
} | |
for(int i = 0; i < 101; i++){ | |
beginSpecies[i] = 500; | |
} | |
percentile.add(beginPercentile); | |
barCounts.add(beginBar); | |
speciesCounts.add(beginSpecies); | |
topSpeciesCounts.add(0); | |
graphImage = createGraphics(975,570,P2D); | |
screenImage = createGraphics(1280,720,P2D); | |
popUpImage = createGraphics(450,450,P2D); | |
segBarImage = createGraphics(975,150,P2D); | |
font = loadFont("Helvetica-Bold-96.vlw"); | |
textFont(font, 96); | |
textAlign(CENTER); | |
for(int i = 0; i < RECTANGLES.length; i++){ | |
rects.add(RECTANGLES[i]); | |
} | |
} | |
void draw(){ | |
scale(1); | |
if(menu == 0){ | |
background(255); | |
fill(100,200,100); | |
noStroke(); | |
rect(windowWidth/2-200,300,400,200); | |
fill(0); | |
text("EVOLUTION!", windowWidth/2,200); | |
text("START", windowWidth/2,430); | |
}else if(menu == 1){ | |
noStroke(); | |
fill(0); | |
background(255,200,130); | |
textFont(font, 32); | |
textAlign(LEFT); | |
textFont(font,96); | |
text("Generation "+max(genSelected,0),20,100); | |
textFont(font,28); | |
if(gen == -1){ | |
fill(100,200,100); | |
rect(20,250,200,100); | |
fill(0); | |
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("CREATE",56,312); | |
}else{ | |
fill(100,200,100); | |
rect(760,20,460,40); | |
rect(760,70,460,40); | |
rect(760,120,230,40); | |
if(gensToDo >= 2){ | |
fill(128,255,128); | |
}else{ | |
fill(70,140,70); | |
} | |
rect(990,120,230,40); | |
fill(0); | |
text("Do 1 step-by-step generation.",770,50); | |
text("Do 1 quick generation.",770,100); | |
text("Do 1 gen ASAP.",770,150); | |
if(gensToDo >= 2){ | |
textFont(font,15); | |
text("Currently ALAPing.",996,136); | |
text("Click & hold anywhere to stop.",996,153); | |
textFont(font,28); | |
}else{ | |
text("Do gens ALAP.",1000,150); | |
} | |
text("Median Distance",50,160); | |
textAlign(CENTER); | |
textAlign(RIGHT); | |
text(float(round(percentile.get(min(genSelected,percentile.size()-1))[14]*1000))/1000+" m",700,160); | |
drawHistogram(760,410,460,280); | |
drawGraphImage(); | |
} | |
if(gensToDo >= 1){ | |
gensToDo--; | |
if(gensToDo >= 1){ | |
startASAP(); | |
} | |
} | |
}else if(menu == 2){ | |
creatures = 0; | |
camzoom = 0.12; | |
background(220,253,102); | |
for(int y = 0; y < 25; y++){ | |
for(int x = 0; x < 40; x++){ | |
n.clear(); | |
m.clear(); | |
int nodeNum = 1; | |
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, | |
random(MINIMUM_NODE_SIZE,MAXIMUM_NODE_SIZE), | |
random(MINIMUM_NODE_FRICTION,MAXIMUM_NODE_FRICTION))); //replaced all nodes' sizes with 0.4, used to be random(0.1,1), random(0,1) | |
} | |
for(int i = 0; i < muscleNum; i++){ | |
int tc1; | |
int tc2; | |
if(i < nodeNum-1){ | |
tc1 = i; | |
tc2 = i+1; | |
}else{ | |
tc1 = int(random(0,nodeNum)); | |
tc2 = tc1; | |
while(tc2 == tc1){ | |
tc2 = int(random(0,nodeNum)); | |
} | |
} | |
float rlength1 = random(0.5,1.5); | |
float rlength2 = random(0.5,1.5); | |
float rtime1 = random(0,1); | |
float rtime2 = random(0,1); | |
m.add(new Muscle(int(random(1,3)),tc1,tc2,rtime1,rtime2, | |
min(rlength1,rlength2),max(rlength1,rlength2),isItContracted(rtime1,rtime2),random(0.02,0.08))); | |
} | |
toStableConfiguration(nodeNum,muscleNum); | |
adjustToCenter(nodeNum); | |
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); | |
c[y*40+x].checkForOverlap(); | |
c[y*40+x].checkForLoneNodes(); | |
drawCreatureWhole(c[y*40+x],x*30+55,y*25+30,0); | |
} | |
} | |
setMenu(3); | |
noStroke(); | |
fill(100,100,200); | |
rect(900,664,260,40); | |
fill(0); | |
textAlign(CENTER); | |
textFont(font, 24); | |
text("Here are your 1000 randomly generated creatures!!!",windowWidth/2-200,690); | |
text("Back",windowWidth-250,690); | |
}else if(menu == 4){ | |
setGlobalVariables(c[creaturesTested]); | |
camzoom = 0.01; | |
setMenu(5); | |
if(stepbystepslow){ | |
if(creaturesTested <= 4){ | |
speed = max(creaturesTested,1); | |
}else{ | |
speed = min(creaturesTested*3-9,1000); | |
} | |
}else{ | |
for(int i = 0; i < 1000; i++){ | |
setGlobalVariables(c[i]); | |
for(int s = 0; s < 900; s++){ | |
simulate(); | |
timer++; | |
} | |
setAverage(); | |
c[i].d = average*0.2; | |
} | |
setMenu(6); | |
} | |
} | |
if(menu == 5){ //simulate running | |
if(timer <= 900){ | |
textAlign(CENTER); | |
textFont(font, 0.96/camzoom); | |
background(120,200,255); | |
for(int s = 0; s < speed; s++){ | |
if(timer < 900){ | |
simulate(); | |
timer++; | |
} | |
} | |
setAverage(); | |
if(speed < 30){ | |
for(int s = 0; s < speed; s++){ | |
cam += (average-cam)*0.03; | |
} | |
}else{ | |
cam = average; | |
} | |
drawPosts(0); | |
drawGround(0); | |
drawCreature(n,m,-cam/camzoom+windowWidth/2, windowHeight*0.8,0); | |
drawArrow(average); | |
textAlign(RIGHT); | |
textFont(font,32); | |
fill(0); | |
text("Creature ID: "+id,windowWidth-10,32); | |
if(speed > 60){ | |
timeShow = int((timer+creaturesTested*37)/60)%15; | |
}else{ | |
timeShow = (timer/60); | |
} | |
timeShow = round(timeShow); | |
text("Time: "+timeShow+" / 15 sec.",windowWidth-10,64); | |
text("Playback Speed: x"+speed,windowWidth-10,96); | |
} | |
if(timer == 900){ | |
if(speed < 30){ | |
noStroke(); | |
fill(0,0,0,130); | |
rect(0,0,windowWidth,windowHeight); | |
fill(0,0,0,255); | |
rect(windowWidth/2-500,200,1000,240); | |
fill(255,0,0); | |
textAlign(CENTER); | |
textFont(font, 96); | |
text("Creature's Distance:",windowWidth/2,300); | |
text(float(round(average*200))/1000 + " m",windowWidth/2,400); | |
}else{ | |
timer = 1020; | |
} | |
c[creaturesTested].d = average*0.2; | |
} | |
if(timer >= 1020){ | |
setMenu(4); | |
creaturesTested++; | |
if(creaturesTested == 1000){ | |
setMenu(6); | |
} | |
cam = 0; | |
} | |
if(timer >= 900){ | |
timer += speed; | |
} | |
} | |
if(menu == 6){ | |
//sort | |
c2 = new ArrayList<Creature>(0); | |
for(int i = 0; i < 1000; i++){ | |
c2.add(c[i]); | |
} | |
c2 = quickSort(c2); | |
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++){ | |
beginBar[i] = 0; | |
} | |
barCounts.add(beginBar); | |
Integer[] beginSpecies = new Integer[101]; | |
for(int i = 0; i < 101; i++){ | |
beginSpecies[i] = 0; | |
} | |
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; | |
beginSpecies[species]++; | |
} | |
speciesCounts.add(new Integer[101]); | |
speciesCounts.get(gen+1)[0] = 0; | |
int cum = 0; | |
int record = 0; | |
int holder = 0; | |
for(int i = 0; i < 100; i++){ | |
cum += beginSpecies[i]; | |
speciesCounts.get(gen+1)[i+1] = cum; | |
if(beginSpecies[i] > record){ | |
record = beginSpecies[i]; | |
holder = i; | |
} | |
} | |
topSpeciesCounts.add(holder); | |
if(stepbystep){ | |
drawScreenImage(0); | |
setMenu(7); | |
}else{ | |
setMenu(10); | |
} | |
} | |
if(menu == 8){ | |
//cool sorting animation | |
camzoom = 0.12; | |
background(220,253,102); | |
float transition = 0.5-0.5*cos(min(float(timer)/60,PI)); | |
for(int j = 0; j < 1000; j++){ | |
Creature cj = c2.get(j); | |
int j2 = cj.id-(gen*1000)-1; | |
int x1 = j2%40; | |
int y1 = floor(j2/40); | |
int x2 = j%40; | |
int y2 = floor(j/40)+1; | |
float x3 = inter(x1,x2,transition); | |
float y3 = inter(y1,y2,transition); | |
drawCreatureWhole(cj,x3*30+55,y3*25+40,0); | |
} | |
if(stepbystepslow){ | |
timer += 1*SORT_ANIMATION_SPEED; | |
}else{ | |
timer += 3*SORT_ANIMATION_SPEED; | |
} | |
if(timer > 60*PI){ | |
drawScreenImage(1); | |
setMenu(9); | |
} | |
} | |
float mX = mouseX/WINDOW_SIZE; | |
float mY = mouseY/WINDOW_SIZE; | |
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{ | |
statusWindow = -4; | |
} | |
}else{ | |
statusWindow = -4; | |
} | |
}else if(menu == 1 && genSelected >= 1 && gensToDo == 0 && !drag){ | |
statusWindow = -4; | |
if(abs(mY-250) <= 70){ | |
if(abs(mX-990) <= 230){ | |
float modX = (mX-760)%160; | |
if(modX < 140){ | |
statusWindow = floor((mX-760)/160)-3; | |
} | |
} | |
} | |
}else{ | |
statusWindow = -4; | |
} | |
if(menu == 10){ | |
//Kill! | |
for(int j = 0; j < 500; j++){ | |
float f = float(j)/1000; | |
float rand = (pow(random(-1,1),3)+1)/2; //cube function | |
slowDies = (f <= rand); | |
int j2; | |
int j3; | |
if(slowDies){ | |
j2 = j; | |
j3 = 999-j; | |
}else{ | |
j2 = 999-j; | |
j3 = j; | |
} | |
Creature cj = c2.get(j2); | |
cj.alive = true; | |
Creature ck = c2.get(j3); | |
ck.alive = false; | |
} | |
if(stepbystep){ | |
drawScreenImage(2); | |
setMenu(11); | |
}else{ | |
setMenu(12); | |
} | |
} | |
if(menu == 12){ //Reproduce and mutate | |
justGotBack = true; | |
for(int j = 0; j < 500; j++){ | |
int j2 = j; | |
if(!c2.get(j).alive) j2 = 999-j; | |
Creature cj = c2.get(j2); | |
Creature cj2 = c2.get(999-j2); | |
c2.set(999-j2,cj.modified(cj2.id+1000)); //mutated offspring 1 | |
n = c2.get(999-j2).n; | |
m = c2.get(999-j2).m; | |
toStableConfiguration(n.size(),m.size()); | |
adjustToCenter(n.size()); | |
c2.set(j2,cj.copyCreature(cj.id+1000)); //duplicate | |
} | |
for(int j = 0; j < 1000; j++){ | |
Creature cj = c2.get(j); | |
c[cj.id-(gen*1000)-1001] = cj.copyCreature(-1); | |
} | |
drawScreenImage(3); | |
gen++; | |
if(stepbystep){ | |
setMenu(13); | |
}else{ | |
setMenu(1); | |
} | |
} | |
if(menu%2 == 1 && abs(menu-10) <= 3){ | |
image(screenImage,0,0,windowWidth,windowHeight); | |
} | |
if(menu == 1 || gensToDo >= 1){ | |
mX = mouseX/WINDOW_SIZE; | |
mY = mouseY/WINDOW_SIZE; | |
noStroke(); | |
if(gen >= 1){ | |
textAlign(CENTER); | |
if(gen >= 5){ | |
genSelected = round((sliderX-760)*(gen-1)/410)+1; | |
}else{ | |
genSelected = round((sliderX-760)*gen/410); | |
} | |
if(drag) sliderX = min(max(sliderX+(mX-25-sliderX)*0.2,760),1170); | |
fill(100); | |
rect(760,340,460,50); | |
fill(220); | |
rect(sliderX,340,50,50); | |
int fs = 0; | |
if(genSelected >= 1){ | |
fs = floor(log(genSelected)/log(10)); | |
} | |
fontSize = fontSizes[fs]; | |
textFont(font,fontSize); | |
fill(0); | |
text(genSelected,sliderX+25,366+fontSize*0.3333); | |
} | |
if(genSelected >= 1){ | |
textAlign(CENTER); | |
camzoom = 0.028; | |
for(int k = 0; k < 3; k++){ | |
fill(220); | |
rect(760+k*160,180,140,140); | |
drawCreatureWhole(creatureDatabase.get((genSelected-1)*3+k),830+160*k,290,0); | |
} | |
fill(0); | |
textFont(font,16); | |
text("Worst Creature",830,310); | |
text("Median Creature",990,310); | |
text("Best Creature",1150,310); | |
} | |
if(justGotBack) justGotBack = false; | |
} | |
if(statusWindow >= -3){ | |
drawStatusWindow(); | |
if(statusWindow >= -3 && !miniSimulation){ | |
openMiniSimulation(); | |
} | |
} | |
overallTimer++; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment