diff --git a/Axon.pde b/Axon.pde index 7ca19b4..84bd051 100644 --- a/Axon.pde +++ b/Axon.pde @@ -1,24 +1,36 @@ -class Axon{ - final double MUTABILITY_MUTABILITY = 0.7; - final int mutatePower = 9; - final double MUTATE_MULTI; - - double weight; - double mutability; - public Axon(double w, double m){ - weight = w; - mutability = m; - MUTATE_MULTI = Math.pow(0.5,mutatePower); - } - - public Axon mutateAxon(){ - double mutabilityMutate = Math.pow(0.5,pmRan()*MUTABILITY_MUTABILITY); - return new Axon(weight+r()*mutability/MUTATE_MULTI,mutability*mutabilityMutate); - } - public double r(){ - return Math.pow(pmRan(),mutatePower); - } - public double pmRan(){ - return (Math.random()*2-1); - } -} +class Axon implements ISavable{ + final double MUTABILITY_MUTABILITY = 0.7; + final int mutatePower = 9; + final double MUTATE_MULTI; + + double weight; + double mutability; + public Axon(double w, double m){ + weight = w; + mutability = m; + MUTATE_MULTI = Math.pow(0.5,mutatePower); + } + + public Axon mutateAxon(){ + double mutabilityMutate = Math.pow(0.5,pmRan()*MUTABILITY_MUTABILITY); + return new Axon(weight+r()*mutability/MUTATE_MULTI,mutability*mutabilityMutate); + } + public double r(){ + return Math.pow(pmRan(),mutatePower); + } + public double pmRan(){ + return (Math.random()*2-1); + } + + public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + object.setDouble("weight", weight); + object.setDouble("mutability", mutability); + return object; + } + + public void loadFromJson(JSONObject parent){ + weight = parent.getDouble("weight", weight); + mutability = parent.getDouble("mutability", mutability); + } +} \ No newline at end of file diff --git a/Brain.pde b/Brain.pde index 0598d20..45eecb1 100644 --- a/Brain.pde +++ b/Brain.pde @@ -1,180 +1,239 @@ -class Brain { - float[][] neurons; - Axon[][][] axons; - int BRAIN_WIDTH = 0; - int BRAIN_HEIGHT = 0; - Brain(int bw, int bh, Axon[][][] templateAxons, Boolean haveNeurons, Boolean mutate){ //This is to copy a brain EXACTLY. - setUpBasics(bw,bh,haveNeurons); - axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; - if(mutate){ - for(int x = 0; x < BRAIN_WIDTH-1; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - for(int z = 0; z < BRAIN_HEIGHT-1; z++){ - axons[x][y][z] = templateAxons[x][y][z].mutateAxon(); - } - } - } - }else{ - for(int x = 0; x < BRAIN_WIDTH-1; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - for(int z = 0; z < BRAIN_HEIGHT-1; z++){ - axons[x][y][z] = new Axon(templateAxons[x][y][z].weight,templateAxons[x][y][z].mutability); - } - } - } - } - } - Brain(int bw, int bh){ - setUpBasics(bw,bh,false); - axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; - for(int x = 0; x < BRAIN_WIDTH-1; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - for(int z = 0; z < BRAIN_HEIGHT-1; z++){ - double startingWeight = 0; - if(y == BRAIN_HEIGHT-1){ - startingWeight = (Math.random()*2-1)*STARTING_AXON_VARIABILITY; - } - axons[x][y][z] = new Axon(startingWeight,AXON_START_MUTABILITY); - } - } - } - } - void changeBrainStructure(int bw, int bh, int rowInsertionIndex, int rowRemovalIndex){ - setUpBasics(bw,bh,false); - Axon[][][] oldAxons = axons; - axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; - for(int x = 0; x < BRAIN_WIDTH-1; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - for(int z = 0; z < BRAIN_HEIGHT-1; z++){ - if(y == rowInsertionIndex || z == rowInsertionIndex){ - double startingWeight = 0; - if(y == BRAIN_HEIGHT-1 || true){ - startingWeight = (Math.random()*2-1)*STARTING_AXON_VARIABILITY; - } - axons[x][y][z] = new Axon(startingWeight,AXON_START_MUTABILITY); - }else{ - int oldY = y; - int oldZ = z; - if(rowInsertionIndex >= 0 && y >= rowInsertionIndex) oldY--; - if(rowInsertionIndex >= 0 && z >= rowInsertionIndex) oldZ--; - if(rowRemovalIndex >= 0 && y >= rowRemovalIndex) oldY++; - if(rowRemovalIndex >= 0 && z >= rowRemovalIndex) oldZ++; - axons[x][y][z] = oldAxons[x][oldY][oldZ]; - } - } - } - } - } - void setUpBasics(int bw, int bh, Boolean haveNeurons){ - BRAIN_WIDTH = bw; - BRAIN_HEIGHT = bh; - if(haveNeurons){ - neurons = new float[BRAIN_WIDTH][BRAIN_HEIGHT]; - for(int x = 0; x < BRAIN_WIDTH; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - if(y == BRAIN_HEIGHT-1){ - neurons[x][y] = 1; - }else{ - neurons[x][y] = 0; - } - } - } - }else{ - neurons = null; - } - } - public void useBrain(Creature owner){ - ArrayList n = owner.n; - ArrayList m = owner.m; - for(int i = 0; i < n.size(); i++){ - Node ni = n.get(i); - neurons[0][i] = dist(ni.x, ni.y, ni.z, foodX, foodY, foodZ); - } - for(int i = 0; i < m.size(); i++){ - Muscle am = m.get(i); - Node ni1 = n.get(am.c1); - Node ni2 = n.get(am.c2); - neurons[0][n.size()+i] = dist(ni1.x, ni1.y, ni1.z, ni2.x, ni2.y, ni2.z)/am.len; - } - for(int x = 1; x < BRAIN_WIDTH; x++){ - for(int y = 0; y < BRAIN_HEIGHT-1; y++){ - float total = 0; - for(int input = 0; input < BRAIN_HEIGHT; input++){ - total += neurons[x-1][input]*axons[x-1][input][y].weight; - } - if(x == BRAIN_WIDTH-1){ - neurons[x][y] = total; - }else{ - neurons[x][y] = sigmoid(total); - } - } - } - for(int i = 0; i < m.size(); i++){ - m.get(i).brainOutput = neurons[BRAIN_WIDTH-1][n.size()+i]; - } - } - public float sigmoid(float input){ - return 1.0/(1.0+pow(2.71828182846,-input)); - } - Brain getUsableCopyOfBrain(){ - return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,true,false); - } - Brain copyBrain(){ - return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,false,false); - } - Brain copyMutatedBrain(){ - return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,false,true); - } - public void drawBrain(float scaleUp, Creature owner){ - ArrayList n = owner.n; - ArrayList m = owner.m; - final float neuronSize = 0.4; - int abw = BRAIN_WIDTH*2-1; - noStroke(); - fill(100); - rect(-neuronSize*2*scaleUp,-neuronSize*2*scaleUp,(abw+neuronSize*2)*scaleUp,(BRAIN_HEIGHT+neuronSize*2)*scaleUp); - fill(255); - rect(-neuronSize*3*scaleUp,-neuronSize*scaleUp,neuronSize*scaleUp,n.size()*scaleUp); - fill(0); - rect(-neuronSize*3*scaleUp,(n.size()-neuronSize)*scaleUp,neuronSize*scaleUp,m.size()*scaleUp); - ellipseMode(RADIUS); - strokeWeight(0.5); - textAlign(CENTER); - textFont(font,0.58*scaleUp); - for(int x = 0; x < BRAIN_WIDTH; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - noStroke(); - double val = neurons[x][y]; - fill(neuronFillColor(val)); - ellipse(x*2*scaleUp,y*scaleUp,neuronSize*scaleUp,neuronSize*scaleUp); - fill(neuronTextColor(val)); - text(nf((float)val,0,1),x*2*scaleUp,(y+(neuronSize*0.6))*scaleUp); - } - } - for(int x = 0; x < BRAIN_WIDTH-1; x++){ - for(int y = 0; y < BRAIN_HEIGHT; y++){ - for(int z = 0; z < BRAIN_HEIGHT-1; z++){ - drawAxon(x,y,x+1,z,scaleUp); - } - } - } - } - public void drawAxon(int x1, int y1, int x2, int y2, float scaleUp){ - stroke(neuronFillColor(axons[x1][y1][y2].weight*neurons[x1][y1])); - line(x1*2*scaleUp,y1*scaleUp,x2*2*scaleUp,y2*scaleUp); - } - public color neuronFillColor(double d){ - if(d >= 0){ - return color(255,255,255,(float)(d*255)); - }else{ - return color(1,1,1,abs((float)(d*255))); - } - } - public color neuronTextColor(double d){ - if(d >= 0){ - return color(0,0,0); - }else{ - return color(255,255,255); - } - } -} +class Brain implements ISavable{ + float[][] neurons; + Axon[][][] axons; + int BRAIN_WIDTH = 0; + int BRAIN_HEIGHT = 0; + Brain(int bw, int bh, Axon[][][] templateAxons, Boolean haveNeurons, Boolean mutate){ //This is to copy a brain EXACTLY. + setUpBasics(bw,bh,haveNeurons); + axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; + if(mutate){ + for(int x = 0; x < BRAIN_WIDTH-1; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + for(int z = 0; z < BRAIN_HEIGHT-1; z++){ + axons[x][y][z] = templateAxons[x][y][z].mutateAxon(); + } + } + } + }else{ + for(int x = 0; x < BRAIN_WIDTH-1; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + for(int z = 0; z < BRAIN_HEIGHT-1; z++){ + axons[x][y][z] = new Axon(templateAxons[x][y][z].weight,templateAxons[x][y][z].mutability); + } + } + } + } + } + Brain(int bw, int bh){ + setUpBasics(bw,bh,false); + axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; + for(int x = 0; x < BRAIN_WIDTH-1; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + for(int z = 0; z < BRAIN_HEIGHT-1; z++){ + double startingWeight = 0; + if(y == BRAIN_HEIGHT-1){ + startingWeight = (Math.random()*2-1)*STARTING_AXON_VARIABILITY; + } + axons[x][y][z] = new Axon(startingWeight,AXON_START_MUTABILITY); + } + } + } + } + void changeBrainStructure(int bw, int bh, int rowInsertionIndex, int rowRemovalIndex){ + setUpBasics(bw,bh,false); + Axon[][][] oldAxons = axons; + axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; + for(int x = 0; x < BRAIN_WIDTH-1; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + for(int z = 0; z < BRAIN_HEIGHT-1; z++){ + if(y == rowInsertionIndex || z == rowInsertionIndex){ + double startingWeight = 0; + if(y == BRAIN_HEIGHT-1 || true){ + startingWeight = (Math.random()*2-1)*STARTING_AXON_VARIABILITY; + } + axons[x][y][z] = new Axon(startingWeight,AXON_START_MUTABILITY); + }else{ + int oldY = y; + int oldZ = z; + if(rowInsertionIndex >= 0 && y >= rowInsertionIndex) oldY--; + if(rowInsertionIndex >= 0 && z >= rowInsertionIndex) oldZ--; + if(rowRemovalIndex >= 0 && y >= rowRemovalIndex) oldY++; + if(rowRemovalIndex >= 0 && z >= rowRemovalIndex) oldZ++; + axons[x][y][z] = oldAxons[x][oldY][oldZ]; + } + } + } + } + } + void setUpBasics(int bw, int bh, Boolean haveNeurons){ + BRAIN_WIDTH = bw; + BRAIN_HEIGHT = bh; + if(haveNeurons){ + neurons = new float[BRAIN_WIDTH][BRAIN_HEIGHT]; + for(int x = 0; x < BRAIN_WIDTH; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + if(y == BRAIN_HEIGHT-1){ + neurons[x][y] = 1; + }else{ + neurons[x][y] = 0; + } + } + } + }else{ + neurons = null; + } + } + public void useBrain(Creature owner){ + ArrayList n = owner.n; + ArrayList m = owner.m; + for(int i = 0; i < n.size(); i++){ + Node ni = n.get(i); + neurons[0][i] = dist(ni.x, ni.y, ni.z, foodX, foodY, foodZ); + } + for(int i = 0; i < m.size(); i++){ + Muscle am = m.get(i); + Node ni1 = n.get(am.c1); + Node ni2 = n.get(am.c2); + neurons[0][n.size()+i] = dist(ni1.x, ni1.y, ni1.z, ni2.x, ni2.y, ni2.z)/am.len; + } + for(int x = 1; x < BRAIN_WIDTH; x++){ + for(int y = 0; y < BRAIN_HEIGHT-1; y++){ + float total = 0; + for(int input = 0; input < BRAIN_HEIGHT; input++){ + total += neurons[x-1][input]*axons[x-1][input][y].weight; + } + if(x == BRAIN_WIDTH-1){ + neurons[x][y] = total; + }else{ + neurons[x][y] = sigmoid(total); + } + } + } + for(int i = 0; i < m.size(); i++){ + m.get(i).brainOutput = neurons[BRAIN_WIDTH-1][n.size()+i]; + } + } + public float sigmoid(float input){ + return 1.0/(1.0+pow(2.71828182846,-input)); + } + Brain getUsableCopyOfBrain(){ + return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,true,false); + } + Brain copyBrain(){ + return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,false,false); + } + Brain copyMutatedBrain(){ + return new Brain(BRAIN_WIDTH,BRAIN_HEIGHT,axons,false,true); + } + public void drawBrain(float scaleUp, Creature owner){ + ArrayList n = owner.n; + ArrayList m = owner.m; + final float neuronSize = 0.4; + int abw = BRAIN_WIDTH*2-1; + noStroke(); + fill(100); + rect(-neuronSize*2*scaleUp,-neuronSize*2*scaleUp,(abw+neuronSize*2)*scaleUp,(BRAIN_HEIGHT+neuronSize*2)*scaleUp); + fill(255); + rect(-neuronSize*3*scaleUp,-neuronSize*scaleUp,neuronSize*scaleUp,n.size()*scaleUp); + fill(0); + rect(-neuronSize*3*scaleUp,(n.size()-neuronSize)*scaleUp,neuronSize*scaleUp,m.size()*scaleUp); + ellipseMode(RADIUS); + strokeWeight(0.5); + textAlign(CENTER); + textFont(font,0.58*scaleUp); + for(int x = 0; x < BRAIN_WIDTH; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + noStroke(); + double val = neurons[x][y]; + fill(neuronFillColor(val)); + ellipse(x*2*scaleUp,y*scaleUp,neuronSize*scaleUp,neuronSize*scaleUp); + fill(neuronTextColor(val)); + text(nf((float)val,0,1),x*2*scaleUp,(y+(neuronSize*0.6))*scaleUp); + } + } + for(int x = 0; x < BRAIN_WIDTH-1; x++){ + for(int y = 0; y < BRAIN_HEIGHT; y++){ + for(int z = 0; z < BRAIN_HEIGHT-1; z++){ + drawAxon(x,y,x+1,z,scaleUp); + } + } + } + } + public void drawAxon(int x1, int y1, int x2, int y2, float scaleUp){ + stroke(neuronFillColor(axons[x1][y1][y2].weight*neurons[x1][y1])); + line(x1*2*scaleUp,y1*scaleUp,x2*2*scaleUp,y2*scaleUp); + } + public color neuronFillColor(double d){ + if(d >= 0){ + return color(255,255,255,(float)(d*255)); + }else{ + return color(1,1,1,abs((float)(d*255))); + } + } + public color neuronTextColor(double d){ + if(d >= 0){ + return color(0,0,0); + }else{ + return color(255,255,255); + } + } + + public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + object.setInt("width", BRAIN_WIDTH); + object.setInt("height", BRAIN_HEIGHT); + JSONArray axonArray = new JSONArray(); + if(axons != null){ + for (int i = 0; i < axons.length; i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < axons[i].length; j++){ + JSONArray jArray = new JSONArray(); + for(int k = 0; k < axons[i][j].length; k++){ + jArray.setJSONObject(k, axons[i][j][k].saveToJson()); + } + iArray.setJSONArray(j, jArray); + } + axonArray.setJSONArray(i, iArray); + } + } + object.setJSONArray("axons", axonArray); + JSONArray neuronArray = new JSONArray(); + if(neurons != null){ + for(int i = 0; i < neurons.length; i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < neurons[i].length; j++){ + iArray.setFloat(j, neurons[i][j]); + } + neuronArray.setJSONArray(i, iArray); + } + } + object.setJSONArray("neurons", neuronArray); + return object; + } + + public void loadFromJson(JSONObject parent){ + BRAIN_WIDTH = parent.getInt("width"); + BRAIN_HEIGHT = parent.getInt("height"); + axons = new Axon[BRAIN_WIDTH-1][BRAIN_HEIGHT][BRAIN_HEIGHT-1]; + JSONArray axonArray = parent.getJSONArray("axons"); + for(int i = 0; i < axonArray.size(); i++){ + JSONArray iArray = axonArray.getJSONArray(i); + for(int j = 0; j < iArray.size(); j++){ + JSONArray jArray = iArray.getJSONArray(j); + for(int k = 0; k < jArray.size(); k++){ + axons[i][j][k] = new Axon(0,0); + axons[i][j][k].loadFromJson(jArray.getJSONObject(k)); + } + } + } + neurons = new float[BRAIN_WIDTH][BRAIN_HEIGHT]; + JSONArray neuronArray = parent.getJSONArray("neurons"); + for(int i = 0; i < neuronArray.size(); i++){ + JSONArray iArray = neuronArray.getJSONArray(i); + for(int j = 0; j < iArray.size(); j++){ + neurons[i][j] = iArray.getFloat(j); + } + } + } + +} \ No newline at end of file diff --git a/Creature.pde b/Creature.pde index 8857a87..30a7437 100644 --- a/Creature.pde +++ b/Creature.pde @@ -1,291 +1,366 @@ -class Creature { - ArrayList n; - ArrayList m; - float d; - int id; - boolean alive; - float creatureTimer; - float mutability; - Brain brain; - int[] name; - float[][] foodPositions = new float[100][3]; - Creature(int[] tname, int tid, ArrayList tn, ArrayList tm, float td, boolean talive, float tct, float tmut, Brain newBrain, float[][] tfoodpos) { - id = tid; - m = tm; - n = tn; - d = td; - alive = talive; - creatureTimer = tct; - mutability = tmut; - if(newBrain != null){ - brain = newBrain; - }else{ - brain = new Brain(BRAIN_WIDTH, getBrainHeight()); - } - if(tname == null){ - name = getNewCreatureName(); - }else{ - name = new int[2]; - name[0] = tname[0]; - name[1] = tname[1]; - } - if(tfoodpos == null){ - for(int i = 0; i < 100; i++){ - foodPositions[i][0] = random(-foodAngleChange,foodAngleChange); - foodPositions[i][1] = random(-1.2,-0.55); - foodPositions[i][2] = random(0,1); - } - }else{ - for(int i = 0; i < 100; i++){ - foodPositions[i][0] = tfoodpos[i][0]; - foodPositions[i][1] = tfoodpos[i][1]; - foodPositions[i][2] = tfoodpos[i][2]; - } - } - } - int getBrainHeight(){ - return n.size()+m.size()+1; - } - void changeBrainStructure(int rowInsertionIndex, int rowRemovalIndex){ - brain.changeBrainStructure(BRAIN_WIDTH, getBrainHeight(), rowInsertionIndex,rowRemovalIndex); - } - public float sigmoid(float input){ - return 1.0/(1.0+pow(2.71828182846,-input)); - } - Creature modified(int id) { - ArrayList newN = new ArrayList(0); - ArrayList newM = new ArrayList(0); - for (int i = 0; i < n.size(); i++) { - newN.add(n.get(i).modifyNode(mutability,n.size())); - } - for (int i = 0; i < m.size(); i++) { - newM.add(m.get(i).modifyMuscle(n.size(), mutability)); - } - int[] newName = new int[2]; - newName[0] = name[0]; - newName[1] = CREATURES_PER_PATRON[name[0]]; - CREATURES_PER_PATRON[name[0]]++; - Creature modifiedCreature = new Creature(newName, id, - newN, newM, 0, true, creatureTimer+r()*16*mutability, min(mutability*random(0.8, 1.25), 2), brain.copyMutatedBrain(),null); - if (random(0, 1) < bigMutationChance*mutability || n.size() <= 2) { //Add a node - modifiedCreature.addRandomNode(); - } - if (random(0, 1) < bigMutationChance*mutability) { //Add a muscle - modifiedCreature.addRandomMuscle(-1, -1); - } - if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.n.size() >= 5) { //Remove a node - modifiedCreature.removeRandomNode(); - } - if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.m.size() >= 2) { //Remove a muscle - modifiedCreature.removeRandomMuscle(); - } - modifiedCreature.checkForOverlap(); - modifiedCreature.checkForLoneNodes(); - modifiedCreature.toStableConfiguration(); - modifiedCreature.moveToCenter(); - return modifiedCreature; - } - void moveToCenter(){ - float avX = 0; - float avZ = 0; - for(int i = 0; i < n.size(); i++) { - avX += n.get(i).x; - avZ += n.get(i).z; - } - avX /= n.size(); - avZ /= n.size(); - for(int i = 0; i < n.size(); i++) { - n.get(i).x -= avX; - n.get(i).z -= avZ; - } - } - void checkForOverlap() { - ArrayList bads = new ArrayList(); - 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); - changeBrainStructure(-1,n.size()+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 || m.get(j).c2 == i) { - connections++; - connectedTo = j; - } - } - 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 vertical = random(-1,1); - float x = n.get(parentNode).x+cos(ang1)*0.5*distance; - float y = n.get(parentNode).y+vertical*0.5*distance; - float z = n.get(parentNode).y+sin(ang1)*0.5*distance; - int newNodeCount = n.size()+1; - - n.add(new Node(x, y, z, 0, 0, 0, 0.4, random(0, 1))); - changeBrainStructure(n.size()-1,-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 len = random(0.5, 1.5); - if (tc1 != -1) { - len = dist(n.get(tc1).x, n.get(tc1).y, n.get(tc2).x, n.get(tc2).y); - } - m.add(new Muscle(tc1, tc2, len, random(0.02, 0.08))); - changeBrainStructure(getBrainHeight()-2,-1); - } - void removeRandomNode() { - int choice = floor(random(0, n.size())); - n.remove(choice); - changeBrainStructure(-1,choice); - int i = 0; - while (i < m.size ()) { - if (m.get(i).c1 == choice || m.get(i).c2 == choice) { - m.remove(i); - changeBrainStructure(-1,n.size()+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); - changeBrainStructure(-1,n.size()+choice); - } - Creature copyCreature(int newID, Boolean changeFood, Boolean withUsableBrain) { - ArrayList n2 = new ArrayList(0); - ArrayList m2 = new ArrayList(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()); - } - if (newID == -1) { - newID = id; - } - float[][] newFoodPositions = null; - if(!changeFood){ - newFoodPositions = foodPositions; - } - Brain newBrain = brain.copyBrain(); - if(withUsableBrain){ - newBrain = brain.getUsableCopyOfBrain(); - } - return new Creature(name, newID, n2, m2, d, alive, creatureTimer, mutability,newBrain,newFoodPositions); - } - void drawCreature(PGraphics img, Boolean putInFrontOfBack) { - if(putInFrontOfBack && false){ - float minZ = 9999; - for (int i = 0; i < n.size(); i++) { - float value = n.get(i).z-n.get(i).m*0.5; - if(value < minZ){ - minZ = value; - } - } - img.pushMatrix(); - img.translate(0,0,-minZ*scaleToFixBug); - } - for (int i = 0; i < m.size(); i++) { - m.get(i).drawMuscle(n, img); - } - for (int i = 0; i < n.size(); i++) { - n.get(i).drawNode(img); - } - if(putInFrontOfBack && false){ - img.popMatrix(); - } - } - void toStableConfiguration() { - for (int j = 0; j < 200; j++) { - for (int i = 0; i < m.size(); i++) { - m.get(i).applyForce(i, n); - } - for (int i = 0; i < n.size(); i++) { - n.get(i).applyForces(); - } - } - for (int i = 0; i < n.size(); i++) { - n.get(i).vx = 0; - n.get(i).vy = 0; - } - } - void simulate() { - brain.useBrain(this); - for (int i = 0; i < m.size(); i++) { - m.get(i).applyForce(i, n); - } - for (int i = 0; i < n.size(); i++) { - Node ni = n.get(i); - ni.applyGravity(); - ni.applyForces(); - ni.hitWalls((i >= 2)); - float distFromFood = dist(ni.x,ni.y,ni.z,foodX,foodY,foodZ); - if(distFromFood <= 0.4){ - chomps++; - setFoodLocation(); - } - } - } -} +class Creature implements ISavable{ + ArrayList n; + ArrayList m; + float d; + int id; + boolean alive; + float creatureTimer; + float mutability; + Brain brain; + int[] name; + float[][] foodPositions = new float[100][3]; + Creature(int[] tname, int tid, ArrayList tn, ArrayList tm, float td, boolean talive, float tct, float tmut, Brain newBrain, float[][] tfoodpos) { + id = tid; + m = tm; + n = tn; + d = td; + alive = talive; + creatureTimer = tct; + mutability = tmut; + if(newBrain != null){ + brain = newBrain; + }else{ + brain = new Brain(BRAIN_WIDTH, getBrainHeight()); + } + if(tname == null){ + name = getNewCreatureName(); + }else{ + name = new int[2]; + name[0] = tname[0]; + name[1] = tname[1]; + } + if(tfoodpos == null){ + for(int i = 0; i < 100; i++){ + foodPositions[i][0] = random(-foodAngleChange,foodAngleChange); + foodPositions[i][1] = random(-1.2,-0.55); + foodPositions[i][2] = random(0,1); + } + }else{ + for(int i = 0; i < 100; i++){ + foodPositions[i][0] = tfoodpos[i][0]; + foodPositions[i][1] = tfoodpos[i][1]; + foodPositions[i][2] = tfoodpos[i][2]; + } + } + } + int getBrainHeight(){ + return n.size()+m.size()+1; + } + void changeBrainStructure(int rowInsertionIndex, int rowRemovalIndex){ + brain.changeBrainStructure(BRAIN_WIDTH, getBrainHeight(), rowInsertionIndex,rowRemovalIndex); + } + public float sigmoid(float input){ + return 1.0/(1.0+pow(2.71828182846,-input)); + } + Creature modified(int id) { + ArrayList newN = new ArrayList(0); + ArrayList newM = new ArrayList(0); + for (int i = 0; i < n.size(); i++) { + newN.add(n.get(i).modifyNode(mutability,n.size())); + } + for (int i = 0; i < m.size(); i++) { + newM.add(m.get(i).modifyMuscle(n.size(), mutability)); + } + int[] newName = new int[2]; + newName[0] = name[0]; + newName[1] = CREATURES_PER_PATRON[name[0]]; + CREATURES_PER_PATRON[name[0]]++; + Creature modifiedCreature = new Creature(newName, id, + newN, newM, 0, true, creatureTimer+r()*16*mutability, min(mutability*random(0.8, 1.25), 2), brain.copyMutatedBrain(),null); + if (random(0, 1) < bigMutationChance*mutability || n.size() <= 2) { //Add a node + modifiedCreature.addRandomNode(); + } + if (random(0, 1) < bigMutationChance*mutability) { //Add a muscle + modifiedCreature.addRandomMuscle(-1, -1); + } + if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.n.size() >= 5) { //Remove a node + modifiedCreature.removeRandomNode(); + } + if (random(0, 1) < bigMutationChance*mutability && modifiedCreature.m.size() >= 2) { //Remove a muscle + modifiedCreature.removeRandomMuscle(); + } + modifiedCreature.checkForOverlap(); + modifiedCreature.checkForLoneNodes(); + modifiedCreature.toStableConfiguration(); + modifiedCreature.moveToCenter(); + return modifiedCreature; + } + void moveToCenter(){ + float avX = 0; + float avZ = 0; + for(int i = 0; i < n.size(); i++) { + avX += n.get(i).x; + avZ += n.get(i).z; + } + avX /= n.size(); + avZ /= n.size(); + for(int i = 0; i < n.size(); i++) { + n.get(i).x -= avX; + n.get(i).z -= avZ; + } + } + void checkForOverlap() { + ArrayList bads = new ArrayList(); + 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); + changeBrainStructure(-1,n.size()+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 || m.get(j).c2 == i) { + connections++; + connectedTo = j; + } + } + 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 vertical = random(-1,1); + float x = n.get(parentNode).x+cos(ang1)*0.5*distance; + float y = n.get(parentNode).y+vertical*0.5*distance; + float z = n.get(parentNode).y+sin(ang1)*0.5*distance; + int newNodeCount = n.size()+1; + + n.add(new Node(x, y, z, 0, 0, 0, 0.4, random(0, 1))); + changeBrainStructure(n.size()-1,-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 len = random(0.5, 1.5); + if (tc1 != -1) { + len = dist(n.get(tc1).x, n.get(tc1).y, n.get(tc2).x, n.get(tc2).y); + } + m.add(new Muscle(tc1, tc2, len, random(0.02, 0.08))); + changeBrainStructure(getBrainHeight()-2,-1); + } + void removeRandomNode() { + int choice = floor(random(0, n.size())); + n.remove(choice); + changeBrainStructure(-1,choice); + int i = 0; + while (i < m.size ()) { + if (m.get(i).c1 == choice || m.get(i).c2 == choice) { + m.remove(i); + changeBrainStructure(-1,n.size()+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); + changeBrainStructure(-1,n.size()+choice); + } + Creature copyCreature(int newID, Boolean changeFood, Boolean withUsableBrain) { + ArrayList n2 = new ArrayList(0); + ArrayList m2 = new ArrayList(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()); + } + if (newID == -1) { + newID = id; + } + float[][] newFoodPositions = null; + if(!changeFood){ + newFoodPositions = foodPositions; + } + Brain newBrain = brain.copyBrain(); + if(withUsableBrain){ + newBrain = brain.getUsableCopyOfBrain(); + } + return new Creature(name, newID, n2, m2, d, alive, creatureTimer, mutability,newBrain,newFoodPositions); + } + void drawCreature(PGraphics img, Boolean putInFrontOfBack) { + if(putInFrontOfBack && false){ + float minZ = 9999; + for (int i = 0; i < n.size(); i++) { + float value = n.get(i).z-n.get(i).m*0.5; + if(value < minZ){ + minZ = value; + } + } + img.pushMatrix(); + img.translate(0,0,-minZ*scaleToFixBug); + } + for (int i = 0; i < m.size(); i++) { + m.get(i).drawMuscle(n, img); + } + for (int i = 0; i < n.size(); i++) { + n.get(i).drawNode(img); + } + if(putInFrontOfBack && false){ + img.popMatrix(); + } + } + void toStableConfiguration() { + for (int j = 0; j < 200; j++) { + for (int i = 0; i < m.size(); i++) { + m.get(i).applyForce(i, n); + } + for (int i = 0; i < n.size(); i++) { + n.get(i).applyForces(); + } + } + for (int i = 0; i < n.size(); i++) { + n.get(i).vx = 0; + n.get(i).vy = 0; + } + } + void simulate() { + brain.useBrain(this); + for (int i = 0; i < m.size(); i++) { + m.get(i).applyForce(i, n); + } + for (int i = 0; i < n.size(); i++) { + Node ni = n.get(i); + ni.applyGravity(); + ni.applyForces(); + ni.hitWalls((i >= 2)); + float distFromFood = dist(ni.x,ni.y,ni.z,foodX,foodY,foodZ); + if(distFromFood <= 0.4){ + chomps++; + setFoodLocation(); + } + } + } + public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + JSONArray nodeArray = new JSONArray(); + if(n != null){ + for(int i = 0 ; i < n.size(); i++){ + nodeArray.setJSONObject(i, n.get(i).saveToJson()); + } + } + object.setJSONArray("nodes", nodeArray); + JSONArray muscleArray = new JSONArray(); + for(int i = 0; i < m.size(); i++){ + muscleArray.setJSONObject(i, m.get(i).saveToJson()); + } + object.setJSONArray("muscles", muscleArray); + object.setFloat("d", d); + object.setInt("id", id); + object.setBoolean("alive", alive); + object.setFloat("creatureTimer", creatureTimer); + object.setFloat("mutability", mutability); + object.setJSONObject("brain", brain.saveToJson()); + JSONArray nameArray = new JSONArray(); + for(int i = 0; i < name.length; i++){ + nameArray.setInt(i, name[i]); + } + object.setJSONArray("name", nameArray); + JSONArray foodArray = new JSONArray(); + for(int i = 0; i < foodPositions.length; i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < foodPositions[i].length; j++){ + iArray.setFloat(j, foodPositions[i][j]); + } + foodArray.setJSONArray(i, iArray); + } + object.setJSONArray("food", foodArray); + return object; + } + + public void loadFromJson(JSONObject parent){ + JSONArray nodeArray = parent.getJSONArray("nodes"); + n = new ArrayList(); + for(int i = 0; i < nodeArray.size(); i++){ + Node node = new Node(0, 0, 0, 0, 0, 0, 0, 0); + node.loadFromJson(nodeArray.getJSONObject(i)); + n.add(node); + } + JSONArray muscleArray = parent.getJSONArray("muscles"); + m = new ArrayList(); + for(int i = 0; i < muscleArray.size(); i++){ + Muscle muscle = new Muscle(0, 0, 0, 0); + muscle.loadFromJson(muscleArray.getJSONObject(i)); + m.add(muscle); + } + d = parent.getFloat("d"); + id = parent.getInt("id"); + alive = parent.getBoolean("alive"); + creatureTimer = parent.getFloat("creatureTimer"); + mutability = parent.getFloat("mutability"); + brain = new Brain(1, 1); + brain.loadFromJson(parent.getJSONObject("brain")); + JSONArray nameArray = parent.getJSONArray("name"); + name = new int[nameArray.size()]; + for(int i = 0; i < nameArray.size(); i++){ + name[i] = nameArray.getInt(i); + } + JSONArray foodArray = parent.getJSONArray("food"); + foodPositions = new float[foodArray.size()][foodArray.getJSONArray(0).size()]; + for(int i = 0; i < foodArray.size(); i++){ + JSONArray iArray = foodArray.getJSONArray(i); + for(int j = 0; j < iArray.size(); j++){ + foodPositions[i][j] = iArray.getFloat(j); + } + } + + } + +} \ No newline at end of file diff --git a/ISavable.pde b/ISavable.pde new file mode 100644 index 0000000..63fd620 --- /dev/null +++ b/ISavable.pde @@ -0,0 +1,5 @@ +interface ISavable{ + public JSONObject saveToJson(); + + public void loadFromJson(JSONObject object); +} \ No newline at end of file diff --git a/Muscle.pde b/Muscle.pde index 476e0e2..eeb3444 100644 --- a/Muscle.pde +++ b/Muscle.pde @@ -1,68 +1,88 @@ -class Muscle { - int c1, c2; - float len; - float rigidity; - float previousTarget; - float brainOutput; - Muscle(int tc1, int tc2, float tlen, float trigidity) { - previousTarget = len = tlen; - c1 = tc1; - c2 = tc2; - rigidity = trigidity; - brainOutput = 1; - } - void applyForce(int i, ArrayList n) { - float target = previousTarget; - if(energyDirection == 1 || energy >= 0.0001){ - target = len*toMuscleUsable(brainOutput); - }else{ - target = len; - } - Node ni1 = n.get(c1); - Node ni2 = n.get(c2); - float distance = dist(ni1.x, ni1.y, ni1.z, ni2.x, ni2.y, ni2.z); - if(distance >= 0.0001){ - float normX = (ni1.x-ni2.x)/distance; - float normY = (ni1.y-ni2.y)/distance; - float normZ = (ni1.z-ni2.z)/distance; - force = min(max(1-(distance/target), -1.7), 1.7); - ni1.vx += normX*force*rigidity/ni1.m; - ni1.vy += normY*force*rigidity/ni1.m; - ni1.vz += normZ*force*rigidity/ni1.m; - ni2.vx -= normX*force*rigidity/ni2.m; - ni2.vy -= normY*force*rigidity/ni2.m; - ni2.vz -= normZ*force*rigidity/ni2.m; - energy = max(energy+energyDirection*abs(previousTarget-target)*rigidity*energyUnit,0); - previousTarget = target; - } - } - Muscle copyMuscle() { - return new Muscle(c1, c2, len, rigidity); - } - Muscle modifyMuscle(int nodeNum, float mutability) { - int newc1 = c1; - int newc2 = c2; - if(random(0,1) n, PGraphics img) { - Node ni1 = n.get(c1); - Node ni2 = n.get(c2); - float w = toMuscleUsable(brainOutput)*0.15; - img.strokeWeight(w*scaleToFixBug); - float brownness = rigidity*13; - img.stroke(255-180*brownness, 255-210*brownness, 255-255*brownness, 255); - img.line(ni1.x*scaleToFixBug, ni1.y*scaleToFixBug, - ni1.z*scaleToFixBug, - ni2.x*scaleToFixBug, ni2.y*scaleToFixBug, - ni2.z*scaleToFixBug); - } -} +class Muscle implements ISavable{ + int c1, c2; + float len; + float rigidity; + float previousTarget; + float brainOutput; + Muscle(int tc1, int tc2, float tlen, float trigidity) { + previousTarget = len = tlen; + c1 = tc1; + c2 = tc2; + rigidity = trigidity; + brainOutput = 1; + } + void applyForce(int i, ArrayList n) { + float target = previousTarget; + if(energyDirection == 1 || energy >= 0.0001){ + target = len*toMuscleUsable(brainOutput); + }else{ + target = len; + } + Node ni1 = n.get(c1); + Node ni2 = n.get(c2); + float distance = dist(ni1.x, ni1.y, ni1.z, ni2.x, ni2.y, ni2.z); + if(distance >= 0.0001){ + float normX = (ni1.x-ni2.x)/distance; + float normY = (ni1.y-ni2.y)/distance; + float normZ = (ni1.z-ni2.z)/distance; + force = min(max(1-(distance/target), -1.7), 1.7); + ni1.vx += normX*force*rigidity/ni1.m; + ni1.vy += normY*force*rigidity/ni1.m; + ni1.vz += normZ*force*rigidity/ni1.m; + ni2.vx -= normX*force*rigidity/ni2.m; + ni2.vy -= normY*force*rigidity/ni2.m; + ni2.vz -= normZ*force*rigidity/ni2.m; + energy = max(energy+energyDirection*abs(previousTarget-target)*rigidity*energyUnit,0); + previousTarget = target; + } + } + Muscle copyMuscle() { + return new Muscle(c1, c2, len, rigidity); + } + Muscle modifyMuscle(int nodeNum, float mutability) { + int newc1 = c1; + int newc2 = c2; + if(random(0,1) n, PGraphics img) { + Node ni1 = n.get(c1); + Node ni2 = n.get(c2); + float w = toMuscleUsable(brainOutput)*0.15; + img.strokeWeight(w*scaleToFixBug); + float brownness = rigidity*13; + img.stroke(255-180*brownness, 255-210*brownness, 255-255*brownness, 255); + img.line(ni1.x*scaleToFixBug, ni1.y*scaleToFixBug, + ni1.z*scaleToFixBug, + ni2.x*scaleToFixBug, ni2.y*scaleToFixBug, + ni2.z*scaleToFixBug); + } + + public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + object.setInt("c1", c1); + object.setInt("c2", c2); + object.setFloat("len", len); + object.setFloat("rigidity", rigidity); + object.setFloat("previousTarget", previousTarget); + object.setFloat("brainOutput", brainOutput); + return object; + } + + public void loadFromJson(JSONObject parent){ + c1 = parent.getInt("c1"); + c2 = parent.getInt("c2"); + len = parent.getFloat("len"); + rigidity = parent.getFloat("rigidity"); + previousTarget = parent.getFloat("previousTarget"); + brainOutput = parent.getFloat("brainOutput"); + } +} \ No newline at end of file diff --git a/Node.pde b/Node.pde index b617c45..26ef914 100644 --- a/Node.pde +++ b/Node.pde @@ -1,189 +1,229 @@ -class Node { - float x, y, z, vx, vy, vz, prevX, prevY, prevZ, pvx, pvy, pvz, m, f; - boolean safeInput; - float pressure; - Node(float tx, float ty, float tz, - float tvx, float tvy, float tvz, - float tm, float tf) { - prevX = x = tx; - prevY = y = ty; - prevZ = z = tz; - pvx = vx = tvx; - pvy = vy = tvy; - pvz = vz = tvz; - m = tm; - f = tf; - pressure = 0; - } - void applyForces() { - vx *= airFriction; - vy *= airFriction; - vz *= airFriction; - y += vy; - x += vx; - z += vz; - float acc = dist(vx,vy,vz,pvx,pvy,pvz); - totalNodeNausea += acc*acc*nauseaUnit; - pvx = vx; - pvy = vy; - pvz = vz; - } - void applyGravity() { - vy += gravity; - } - void pressAgainstGround(float groundY){ - float dif = y-(groundY-m/2); - pressure += dif*pressureUnit; - y = (groundY-m/2); - vy = 0; - x -= vx*f; - z -= vz*f; - if (vx > 0) { - vx -= f*dif*FRICTION; - if (vx < 0) { - vx = 0; - } - } else { - vx += f*dif*FRICTION; - if (vx > 0) { - vx = 0; - } - } - if (vz > 0) { - vz -= f*dif*FRICTION; - if (vz < 0) { - vz = 0; - } - } else { - vz += f*dif*FRICTION; - if (vz > 0) { - vz = 0; - } - } - } - void hitWalls(Boolean addToAngular) { - pressure = 0; - float dif = y+m/2; - if (dif >= 0 && haveGround) { - pressAgainstGround(0); - } - 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); - boolean flip = false; - float px, py; - 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) { - float d1 = x-r.x1; - float d2 = r.x2-x; - float d3 = y-r.y1; - float d4 = r.y2-y; - if (d1 < d2 && d1 < d3 && d1 < d4) { - px = r.x1; - py = y; - }else if (d2 < d3 && d2 < d4) { - px = r.x2; - py = y; - }else if (d3 < d4) { - px = x; - py = r.y1; - } else { - px = x; - py = r.y2; - } - flip = true; - } else { - if (x < r.x1) { - px = r.x1; - }else if (x < r.x2) { - px = x; - }else { - px = r.x2; - } - if (y < r.y1) { - py = r.y1; - }else if (y < r.y2) { - py = y; - }else { - py = r.y2; - } - } - float distance = dist(x, y, px, py); - float rad = m/2; - float wallAngle = atan2(py-y, px-x); - if (flip) { - wallAngle += PI; - } - if (distance < rad || flip) { - dif = rad-distance; - pressure += dif*pressureUnit; - float multi = rad/distance; - if (flip) { - multi = -multi; - } - x = (x-px)*multi+px; - y = (y-py)*multi+py; - 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; - vy = cos(relAngle)*relY; - } - } - }*/ - prevY = y; - prevX = x; - } - Node copyNode() { - return (new Node(x, y, z, 0, 0, 0, m, f)); - } - Node modifyNode(float mutability, int nodeNum) { - float newX = x+r()*0.5*mutability; - float newY = y+r()*0.5*mutability; - float newZ = z+r()*0.5*mutability; - //float newM = m+r()*0.1*mutability; - //newM = min(max(newM, 0.3), 0.5); - float newM = 0.4; - float newF = min(max(f+r()*0.1*mutability, 0), 1); - Node newNode = new Node(newX, newY, newZ, 0, 0, 0, newM, newF); - return newNode;//max(m+r()*0.1,0.2),min(max(f+r()*0.1,0),1) - } - void drawNode(PGraphics img) { - color c = color(0,0,0); - if (f <= 0.5) { - c = colorLerp(color(255,255,255),color(180,0,255),f*2); - }else{ - c = colorLerp(color(180,0,255),color(0,0,0),f*2-1); - } - img.fill(c); - img.noStroke(); - img.lights(); - img.pushMatrix(); - img.translate(x*scaleToFixBug, y*scaleToFixBug,z*scaleToFixBug); - img.sphere(m*scaleToFixBug*0.5); - img.popMatrix(); - //img.ellipse((ni.x+x)*scaleToFixBug, (ni.y+y)*scaleToFixBug, ni.m*scaleToFixBug, ni.m*scaleToFixBug); - /*if(ni.f >= 0.5){ - img.fill(255); - }else{ - img.fill(0); - } - img.textAlign(CENTER); - img.textFont(font, 0.4*ni.m*scaleToFixBug); - img.text(nf(ni.value,0,2),(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY2+y)*scaleToFixBug); - img.text(operationNames[ni.operation],(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY1+y)*scaleToFixBug);*/ - } - color colorLerp(color a, color b, float x){ - return color(red(a)+(red(b)-red(a))*x, green(a)+(green(b)-green(a))*x, blue(a)+(blue(b)-blue(a))*x); - } -} +class Node implements ISavable{ + float x, y, z, vx, vy, vz, prevX, prevY, prevZ, pvx, pvy, pvz, m, f; + boolean safeInput; + float pressure; + Node(float tx, float ty, float tz, + float tvx, float tvy, float tvz, + float tm, float tf) { + prevX = x = tx; + prevY = y = ty; + prevZ = z = tz; + pvx = vx = tvx; + pvy = vy = tvy; + pvz = vz = tvz; + m = tm; + f = tf; + pressure = 0; + } + void applyForces() { + vx *= airFriction; + vy *= airFriction; + vz *= airFriction; + y += vy; + x += vx; + z += vz; + float acc = dist(vx,vy,vz,pvx,pvy,pvz); + totalNodeNausea += acc*acc*nauseaUnit; + pvx = vx; + pvy = vy; + pvz = vz; + } + void applyGravity() { + vy += gravity; + } + void pressAgainstGround(float groundY){ + float dif = y-(groundY-m/2); + pressure += dif*pressureUnit; + y = (groundY-m/2); + vy = 0; + x -= vx*f; + z -= vz*f; + if (vx > 0) { + vx -= f*dif*FRICTION; + if (vx < 0) { + vx = 0; + } + } else { + vx += f*dif*FRICTION; + if (vx > 0) { + vx = 0; + } + } + if (vz > 0) { + vz -= f*dif*FRICTION; + if (vz < 0) { + vz = 0; + } + } else { + vz += f*dif*FRICTION; + if (vz > 0) { + vz = 0; + } + } + } + void hitWalls(Boolean addToAngular) { + pressure = 0; + float dif = y+m/2; + if (dif >= 0 && haveGround) { + pressAgainstGround(0); + } + 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); + boolean flip = false; + float px, py; + 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) { + float d1 = x-r.x1; + float d2 = r.x2-x; + float d3 = y-r.y1; + float d4 = r.y2-y; + if (d1 < d2 && d1 < d3 && d1 < d4) { + px = r.x1; + py = y; + }else if (d2 < d3 && d2 < d4) { + px = r.x2; + py = y; + }else if (d3 < d4) { + px = x; + py = r.y1; + } else { + px = x; + py = r.y2; + } + flip = true; + } else { + if (x < r.x1) { + px = r.x1; + }else if (x < r.x2) { + px = x; + }else { + px = r.x2; + } + if (y < r.y1) { + py = r.y1; + }else if (y < r.y2) { + py = y; + }else { + py = r.y2; + } + } + float distance = dist(x, y, px, py); + float rad = m/2; + float wallAngle = atan2(py-y, px-x); + if (flip) { + wallAngle += PI; + } + if (distance < rad || flip) { + dif = rad-distance; + pressure += dif*pressureUnit; + float multi = rad/distance; + if (flip) { + multi = -multi; + } + x = (x-px)*multi+px; + y = (y-py)*multi+py; + 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; + vy = cos(relAngle)*relY; + } + } + }*/ + prevY = y; + prevX = x; + } + Node copyNode() { + return (new Node(x, y, z, 0, 0, 0, m, f)); + } + Node modifyNode(float mutability, int nodeNum) { + float newX = x+r()*0.5*mutability; + float newY = y+r()*0.5*mutability; + float newZ = z+r()*0.5*mutability; + //float newM = m+r()*0.1*mutability; + //newM = min(max(newM, 0.3), 0.5); + float newM = 0.4; + float newF = min(max(f+r()*0.1*mutability, 0), 1); + Node newNode = new Node(newX, newY, newZ, 0, 0, 0, newM, newF); + return newNode;//max(m+r()*0.1,0.2),min(max(f+r()*0.1,0),1) + } + void drawNode(PGraphics img) { + color c = color(0,0,0); + if (f <= 0.5) { + c = colorLerp(color(255,255,255),color(180,0,255),f*2); + }else{ + c = colorLerp(color(180,0,255),color(0,0,0),f*2-1); + } + img.fill(c); + img.noStroke(); + img.lights(); + img.pushMatrix(); + img.translate(x*scaleToFixBug, y*scaleToFixBug,z*scaleToFixBug); + img.sphere(m*scaleToFixBug*0.5); + img.popMatrix(); + //img.ellipse((ni.x+x)*scaleToFixBug, (ni.y+y)*scaleToFixBug, ni.m*scaleToFixBug, ni.m*scaleToFixBug); + /*if(ni.f >= 0.5){ + img.fill(255); + }else{ + img.fill(0); + } + img.textAlign(CENTER); + img.textFont(font, 0.4*ni.m*scaleToFixBug); + img.text(nf(ni.value,0,2),(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY2+y)*scaleToFixBug); + img.text(operationNames[ni.operation],(ni.x+x)*scaleToFixBug,(ni.y+ni.m*lineY1+y)*scaleToFixBug);*/ + } + color colorLerp(color a, color b, float x){ + return color(red(a)+(red(b)-red(a))*x, green(a)+(green(b)-green(a))*x, blue(a)+(blue(b)-blue(a))*x); + } + + public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + object.setFloat("x", x); + object.setFloat("y", y); + object.setFloat("z", z); + object.setFloat("vx", vx); + object.setFloat("vy", vy); + object.setFloat("vz", vz); + object.setFloat("prevX", prevX); + object.setFloat("prevY", prevY); + object.setFloat("prevZ", prevZ); + object.setFloat("pvx", pvx); + object.setFloat("pvy", pvy); + object.setFloat("pvz", pvz); + object.setFloat("m", m); + object.setFloat("f", f); + object.setBoolean("safeInput", safeInput); + object.setFloat("pressure", pressure); + return object; + } + + public void loadFromJson(JSONObject parent){ + x = parent.getFloat("x"); + y = parent.getFloat("y"); + z = parent.getFloat("z"); + vx = parent.getFloat("vx"); + vy = parent.getFloat("vy"); + vz = parent.getFloat("vz"); + prevX = parent.getFloat("prevX"); + prevY = parent.getFloat("prevY"); + prevZ = parent.getFloat("prevZ"); + pvx = parent.getFloat("pvx"); + pvy = parent.getFloat("pvy"); + pvz = parent.getFloat("pvz"); + m = parent.getFloat("m"); + f = parent.getFloat("f"); + safeInput = parent.getBoolean("safeInput"); + pressure = parent.getFloat("pressure"); + } +} \ No newline at end of file diff --git a/Rectangle.pde b/Rectangle.pde index 24a4ec0..c2b0ddc 100644 --- a/Rectangle.pde +++ b/Rectangle.pde @@ -1,9 +1,11 @@ -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 Rectangle { + float x1, y1, x2, y2; + Rectangle(float tx1, float ty1, float tx2, float ty2) { + x1 = tx1; + y1 = ty1; + x2 = tx2; + y2 = ty2; + } + + +} \ No newline at end of file diff --git a/evolutionSteer.pde b/evolutionSteer.pde index ea1723a..9818569 100644 --- a/evolutionSteer.pde +++ b/evolutionSteer.pde @@ -1,1612 +1,1775 @@ -final float windowSizeMultiplier = 1.4; -final int SEED = 31; //7; ;( - -PFont font; -ArrayList percentile = new ArrayList(0); -ArrayList barCounts = new ArrayList(0); -ArrayList speciesCounts = new ArrayList(0); -ArrayList topSpeciesCounts = new ArrayList(0); -ArrayList creatureDatabase = new ArrayList(0); -ArrayList rects = new ArrayList(0); -Creature currentCreature; -PGraphics simulationImage; -PGraphics graphImage; -PGraphics screenImage; -PGraphics popUpImage; -PGraphics segBarImage; -boolean haveGround = true; -int histBarsPerMeter = 5; -String fitnessUnit = "chomps"; -String fitnessName = "Chomps"; -float baselineEnergy = 0.0; -int energyDirection = 1; // if 1, it'll count up how much energy is used. if -1, it'll count down from the baseline energy, and when energy hits 0, the creature dies. -final float FRICTION = 4; -float bigMutationChance = 0.03; -float hazelStairs = -1; -float cumulativeAngularVelocity = 0; -boolean saveFramesPerGeneration = true; -color gridBGColor = color(220, 253, 102, 255); -float foodAngleChange = 0.0; -float foodX = 0; -float foodY = 0; -float foodZ = 0; -float foodAngle = 0; -int chomps = 0; - -int lastImageSaved = -1; -float pressureUnit = 500.0/2.37; -float energyUnit = 20; -float nauseaUnit = 5; -int minBar = -10; -int maxBar = 100; -int barLen = maxBar-minBar; -int gensToDo = 0; -float postFontSize = 0.96; -float scaleToFixBug = 1000; -float energy = 0; -float averageNodeNausea = 0; -float totalNodeNausea = 0; -float CAMERA_MOVE_SPEED = 0.03; - -float lineY1 = -0.08; // These are for the lines of text on each node. -float lineY2 = 0.35; - -int windowWidth = 1280; -int windowHeight = 720; -int timer = 0; -float camX = 0; -float camY = 0; -float camZ = 0; -float camHA = 0; -float camVA = -0.5; -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 prevStatusWindow = -4; -int overallTimer = 0; -boolean miniSimulation = false; -int creatureWatching = 0; -int simulationTimer = 0; -int[] creaturesInPosition = new int[1000]; - -float camZoom = 0.015; -float gravity = 0.006;//0.007; -float airFriction = 0.95; -float MIN_FOOD_DISTANCE = 1; -float MAX_FOOD_DISTANCE = 2.5; - -float target; -float force; -float averageX; -float averageY; -float averageZ; -int speed; -boolean stepbystep; -boolean stepbystepslow; -boolean slowDies; -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 -}; -final int BRAIN_WIDTH = 3; -float STARTING_AXON_VARIABILITY = 1.0; -float AXON_START_MUTABILITY = 0.0005; -String[] patronData; -int PATRON_COUNT = 75; -float TOTAL_PLEDGED = 183.39; -int[] CREATURES_PER_PATRON = new int[PATRON_COUNT]; -float startingFoodDistance = 0; - -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)); -} -void drawGround(PGraphics img) { - int stairDrawStart = max(1,(int)(-averageY/hazelStairs)-10); - img.noStroke(); - if (haveGround){ - float groundTileSize = 5.0; - int cx = round(averageX/5); - int cz = round(averageZ/5); - for(int x = cx-5; x < cx+5; x++){ - for(int z = cz-5; z < cz+5; z++){ - float lowX = (groundTileSize*x)*scaleToFixBug; - float highX = (groundTileSize*(x+1))*scaleToFixBug; - float lowZ = (groundTileSize*z)*scaleToFixBug; - float highZ = (groundTileSize*(z+1))*scaleToFixBug; - img.fill(0, 100+((x+z+100)%2)*30, 0); - img.beginShape(); - img.vertex(lowX, 0, lowZ); - img.vertex(highX, 0, lowZ); - img.vertex(highX, 0, highZ); - img.vertex(lowX, 0, highZ); - img.endShape(CLOSE); - } - } - } - /*for (int i = 0; i < rects.size(); i++) { - Rectangle r = rects.get(i); - img.rect(r.x1*scaleToFixBug, r.y1*scaleToFixBug, (r.x2-r.x1)*scaleToFixBug, (r.y2-r.y1)*scaleToFixBug); - } - if(hazelStairs > 0){ - for(int i = stairDrawStart; i < stairDrawStart+20; i++){ - img.fill(255,255,255,128); - img.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.3*scaleToFixBug); - img.fill(255,255,255,255); - img.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.15*scaleToFixBug); - } - }*/ -} -float toMuscleUsable(float f){ - return min(max(f,0.8),1.2); -} -void drawPosts(PGraphics img) { - int startPostY = min(-8,(int)(averageY/4)*4-4); - img.noStroke(); - img.textAlign(CENTER); - img.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++) { - img.fill(255); - img.rect((i*5.0-0.1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 0.2*scaleToFixBug, 3.0*scaleToFixBug); - img.rect((i*5.0-1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 2.0*scaleToFixBug, 1.0*scaleToFixBug); - img.fill(120); - img.textAlign(CENTER); - img.text(i+" m", i*5.0*scaleToFixBug, (-2.17+postY)*scaleToFixBug); - } - }*/ - img.fill(0,0,255); - img.beginShape(); - float s = 0.4*scaleToFixBug; - float y = -0.001*scaleToFixBug; - img.vertex(0,y,s); - img.vertex(-s,y,0); - img.vertex(0,y,-s); - img.vertex(s,y,0); - img.endShape(CLOSE); - - colorMode(HSB,1.0); - color c = color((timer%40)/40.0,1.0,1.0); - img.fill(c); - img.noStroke(); - img.lights(); - - img.pushMatrix(); - img.translate(foodX*scaleToFixBug,foodY*scaleToFixBug,foodZ*scaleToFixBug); - img.sphere(0.4*scaleToFixBug*0.5); - img.popMatrix(); - - img.noLights(); - - colorMode(RGB,255); - img.fill(0,60,0); - img.pushMatrix(); - img.translate(foodX*scaleToFixBug,0,foodZ*scaleToFixBug); - img.scale(1,0.02,1); - img.sphere(0.4*scaleToFixBug*0.5); - img.popMatrix(); -} -void drawArrow(float x, float y, float z, PGraphics img) { - img.noLights(); - img.pushMatrix(); - img.translate(x*scaleToFixBug,0,z*scaleToFixBug); - img.rotateY(camHA); - img.rotateX(-camVA); - img.textAlign(CENTER); - img.textFont(font, postFontSize*scaleToFixBug); - img.noStroke(); - img.fill(255, 0, 0); - img.beginShape(); - float dist = 2.7*scaleToFixBug; - img.vertex(dist, -3.8*scaleToFixBug, 0); - img.vertex(dist, -2.7*scaleToFixBug, 0); - img.vertex(-dist, -2.7*scaleToFixBug, 0); - img.vertex(-dist, -3.8*scaleToFixBug, 0); - img.endShape(); - img.beginShape(); - img.vertex(0, -2.2*scaleToFixBug); - img.vertex(-0.5*scaleToFixBug, -2.7*scaleToFixBug); - img.vertex(0.5*scaleToFixBug, -2.7*scaleToFixBug); - img.endShape(CLOSE); - String fitnessString = nf(getFitness(),0,2)+" "+fitnessUnit; - img.fill(255); - img.text(fitnessString, 0, -2.91*scaleToFixBug,0.1*scaleToFixBug); - img.popMatrix(); -} -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 = 563.333333333/gen; - float lineX = 136.66666+genSelected*genWidth; - line(lineX, 180, lineX, 500+180); - Integer[] s = speciesCounts.get(genSelected); - textAlign(RIGHT); - 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+3, y, 56, 14); - - fill(0); - text(toRealSpeciesName(i-1)+": "+c, lineX+58, y+11); - - //colorMode(HSB, 1.0); - //fill(getColor(i-1, true)); - //text("S"+floor((i-1)/10)+""+((i-1)%10)+": "+c, lineX+5, y+11); - //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; - } - float light = 1.0; - if (abs(col-0.333) <= 0.18 && adjust) { - light = 0.7; - } - color c = color(col, 1.0, light); - colorMode(RGB, 255); - return c; -} -void drawGraph(int graphWidth, int graphHeight) { - graphImage.beginDraw(); - graphImage.smooth(); - graphImage.background(220); - if (gen >= 1) { - drawLines(130, int(graphHeight*0.05), graphWidth-130, int(graphHeight*0.9)); - drawSegBars(130, 0, graphWidth-130, 150); - } - graphImage.endDraw(); -} -void drawLines(int x, int y, int graphWidth, int graphHeight) { - 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)+" "+fitnessUnit, 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); - } - } -} -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)-2; - if ((unit2+90)%3 < 1) { - return pow(10, floor(unit2/3)); - } else if ((unit2+90)%3 < 2) { - return pow(10, floor((unit2-1)/3))*2; - } else { - return pow(10, floor((unit2-2)/3))*5; - } -} -String showUnit(float i, float unit) { - if (unit < 1) { - return nf(i, 0, 2)+""; - } - else { - return int(i)+""; - } -} -ArrayList quickSort(ArrayList c) { - if (c.size() <= 1) { - return c; - } - else { - ArrayList less = new ArrayList(); - ArrayList more = new ArrayList(); - ArrayList equal = new ArrayList(); - 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 total = new ArrayList(); - total.addAll(quickSort(more)); - total.addAll(equal); - total.addAll(quickSort(less)); - return total; - } -} -void adjustToCenter(int nodeNum) { - float avx = 0; - float lowY = -1000; - for (int i = 0; i < nodeNum; i++) { - Node ni = currentCreature.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 = currentCreature.n.get(i); - ni.x -= avx; - ni.y -= lowY; - } -} -void setAverages() { - averageX = 0; - averageY = 0; - averageZ = 0; - for (int i = 0; i < currentCreature.n.size(); i++) { - Node ni = currentCreature.n.get(i); - averageX += ni.x; - averageY += ni.y; - averageZ += ni.z; - } - averageX = averageX/currentCreature.n.size(); - averageY = averageY/currentCreature.n.size(); - averageZ = averageZ/currentCreature.n.size(); -} -Creature[] c = new Creature[1000]; -ArrayList c2 = new ArrayList(); - -void mouseWheel(MouseEvent event) { - float delta = event.getCount(); - if (menu == 5 || statusWindow >= -3) { - if (delta == -1) { - camZoom *= 0.9090909; - if (camZoom < 0.002) { - camZoom = 0.002; - } - textFont(font, postFontSize); - } else if (delta == 1) { - camZoom *= 1.1; - if (camZoom > 0.1) { - camZoom = 0.1; - } - textFont(font, postFontSize); - } - } -} - -void mousePressed() { - if (gensToDo >= 1) { - gensToDo = 0; - } - float mX = mouseX/windowSizeMultiplier; - float mY = mouseY/windowSizeMultiplier; - 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); - } -} -String zeros(int n, int zeros){ - String s = n+""; - for(int i = s.length(); i < zeros; i++){ - s = "0"+s; - } - return s; -} - -void startASAP() { - setMenu(4); - creaturesTested = 0; - stepbystep = false; - stepbystepslow = false; -} -void mouseReleased() { - drag = false; - miniSimulation = false; - float mX = mouseX/windowSizeMultiplier; - float mY = mouseY/windowSizeMultiplier; - 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); - speed = 1; - 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 == 5 || menu == 4) && mY >= windowHeight-40){ - if(mX < 90){ - for (int s = timer; s < 900; s++) { - simulateCurrentCreature(); - } - timer = 1021; - }else if(mX >= 120 && mX < 360){ - speed *= 2; - if(speed == 1024) speed = 900; - if(speed >= 1800) speed = 1; - }else if(mX >= windowWidth-120){ - for (int s = timer; s < 900; s++) { - simulateCurrentCreature(); - } - timer = 0; - creaturesTested++; - for (int i = creaturesTested; i < 1000; i++) { - setGlobalVariables(c[i]); - for (int s = 0; s < 900; s++) { - simulateCurrentCreature(); - } - setAverages(); - setFitness(i); - } - setMenu(6); - } - } else if(menu == 8 && mX < 90 && mY >= windowHeight-40){ - timer = 100000; - } 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 simulateCurrentCreature(){ - currentCreature.simulate(); - averageNodeNausea = totalNodeNausea/currentCreature.n.size(); - simulationTimer++; - timer++; -} -void drawScreenImage(int stage) { - screenImage.beginDraw(); - screenImage.pushMatrix(); - screenImage.scale(10.0*windowSizeMultiplier/scaleToFixBug); - screenImage.smooth(); - screenImage.background(gridBGColor); - screenImage.noStroke(); - 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++; - screenImage.pushMatrix(); - screenImage.translate((x*3+5.5)*scaleToFixBug, (y*2.5+3)*scaleToFixBug, 0); - cj.drawCreature(screenImage,true); - screenImage.popMatrix(); - } - timer = 0; - screenImage.popMatrix(); - screenImage.noLights(); - - screenImage.pushMatrix(); - screenImage.scale(windowSizeMultiplier); // Arbitrary, do not change. - - screenImage.textAlign(CENTER); - screenImage.textFont(font, 24); - screenImage.fill(100, 100, 200); - screenImage.noStroke(); - 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) { - /*screenImage.pushMatrix(); - screenImage.scale(10.0*windowSizeMultiplier/scaleToFixBug); - - screenImage.translate(x*18+500, y*16+200, 570); - screenImage.scale(0.13); - cj.drawCreature(screenImage,false); - screenImage.popMatrix();*/ - } else { - screenImage.fill(0); - screenImage.beginShape(); - screenImage.vertex(x*30+40, y*25+17,0.01); - screenImage.vertex(x*30+70, y*25+17,0.01); - screenImage.vertex(x*30+70, y*25+42,0.01); - screenImage.vertex(x*30+40, y*25+42,0.01); - screenImage.endShape(); - } - } - } 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.popMatrix(); - screenImage.endDraw(); -} -void drawpopUpImage() { - setAverages(); - moveCamera(); - popUpImage.beginDraw(); - popUpImage.smooth(); - - float camDist = (450/2.0) / tan(PI*30.0 / 180.0); - popUpImage.pushMatrix(); - - popUpImage.camera(camX/camZoom+camDist*sin(camHA)*cos(camVA), - camY/camZoom+camDist*sin(camVA), camZ/camZoom+camDist*cos(camHA)*cos(camVA), - camX/camZoom, camY/camZoom, camZ/camZoom, 0, 1, 0); - - popUpImage.scale(1.0/camZoom/scaleToFixBug); - - if (simulationTimer < 900) { - popUpImage.background(120, 200, 255); - } else { - popUpImage.background(60, 100, 128); - } - drawPosts(popUpImage); - drawGround(popUpImage); - currentCreature.drawCreature(popUpImage,false); - drawArrow(averageX,averageY,averageZ,popUpImage); - popUpImage.noStroke(); - popUpImage.endDraw(); - popUpImage.popMatrix(); -} -void moveCamera(){ - camX += (averageX-camX)*0.2; - camY += (averageY-camY)*0.2; - camZ += (averageZ-camZ)*0.2; -} -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, 0, x+hw, theY, 0); - if (i == 0) theY -= 5; - text(i, x+hw+5, theY+7, 0); - } - textAlign(CENTER); - for (int i = minBar; i <= maxBar; i += 10) { - 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, 0); - line(theX, y, 0, theX, y+hh, 0); - } - 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(boolean isFirstFrame) { - 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) { - keysToMoveCamera(); - int py2 = py-175; - if (py >= 360) { - py2 -= 190; - }else { - py2 += 238; - } - py = min(max(py,0),420); - int px2 = min(max(px-90, 10), 900); - drawpopUpImage(); - pushMatrix(); - translate(0,0,1); - image(popUpImage, px2, py2, 360, 360); - popMatrix(); - drawBrain(px2-130, py2, 1,5, cj); - drawStats(px2+355, py2+239, 1, 0.45); - - simulateCurrentCreature(); - int shouldBeWatching = statusWindow; - if (statusWindow <= -1) { - cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3); - shouldBeWatching = cj.id; - } - if (creatureWatching != shouldBeWatching || isFirstFrame) { - openMiniSimulation(); - } - } -} -void setup() { - String[] prePatronData = loadStrings("PatronReport_2017-06-12.csv"); - patronData = new String[PATRON_COUNT]; - int lineAt = 0; - for(int i = 0; i < prePatronData.length; i++){ - if(i != 0 && prePatronData[i].indexOf("Reward") == -1){ - patronData[lineAt] = prePatronData[i]; - lineAt++; - } - } - for(int i = 0; i < PATRON_COUNT; i++){ - CREATURES_PER_PATRON[i] = 0; - } - frameRate(60); - randomSeed(SEED); - noSmooth(); - size((int)(windowWidth*windowSizeMultiplier), (int)(windowHeight*windowSizeMultiplier),P3D); - 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); - - simulationImage = createGraphics(width, height, P3D); - graphImage = createGraphics(975, 570); - screenImage = createGraphics(width, height, P3D); - popUpImage = createGraphics(450, 450, P3D); - segBarImage = createGraphics(975, 150); - segBarImage.beginDraw(); - segBarImage.smooth(); - segBarImage.background(220); - segBarImage.endDraw(); - popUpImage.beginDraw(); - popUpImage.smooth(); - popUpImage.background(220); - popUpImage.endDraw(); - - font = loadFont("Helvetica-Bold-96.vlw"); - textFont(font, 96); - textAlign(CENTER); - - /*rects.add(new Rectangle(4,-7,9,-3)); - rects.add(new Rectangle(6,-1,10,10)); - rects.add(new Rectangle(9.5,-1.5,13,10)); - rects.add(new Rectangle(12,-2,16,10)); - rects.add(new Rectangle(15,-2.5,19,10)); - rects.add(new Rectangle(18,-3,22,10)); - rects.add(new Rectangle(21,-3.5,25,10)); - rects.add(new Rectangle(24,-4,28,10)); - rects.add(new Rectangle(27,-4.5,31,10)); - rects.add(new Rectangle(30,-5,34,10)); - rects.add(new Rectangle(33,-5.5,37,10)); - rects.add(new Rectangle(36,-6,40,10)); - rects.add(new Rectangle(39,-6.5,100,10));*/ - - //rects.add(new Rectangle(-100,-100,100,-2.8)); - //rects.add(new Rectangle(-100,0,100,100)); - //Snaking thing below: - /*rects.add(new Rectangle(-400,-10,1.5,-1.5)); - rects.add(new Rectangle(-400,-10,3,-3)); - rects.add(new Rectangle(-400,-10,4.5,-4.5)); - rects.add(new Rectangle(-400,-10,6,-6)); - rects.add(new Rectangle(0.75,-0.75,400,4)); - rects.add(new Rectangle(2.25,-2.25,400,4)); - rects.add(new Rectangle(3.75,-3.75,400,4)); - rects.add(new Rectangle(5.25,-5.25,400,4)); - rects.add(new Rectangle(-400,-5.25,0,4));*/ -} -void draw() { - scale(windowSizeMultiplier); - 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("GEN "+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("Survivor Bias: "+percentify(getSB(genSelected)), 437, 50); - text("Curve: ±"+nf(foodAngleChange/(2*PI)*360,0,2)+" degrees", 420, 50); - 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); - textAlign(CENTER); - textAlign(RIGHT); - text(float(round(percentile.get(min(genSelected, percentile.size()-1))[14]*1000))/1000+" "+fitnessUnit, 700, 160); - drawHistogram(760, 410, 460, 280); - drawGraphImage(); - //if(saveFramesPerGeneration && gen > lastImageSaved){ - // saveFrame("imgs//"+zeros(gen,5)+".png"); - // lastImageSaved = gen; - //} - } - if (gensToDo >= 1) { - gensToDo--; - if (gensToDo >= 1) { - startASAP(); - } - } - }else if (menu == 2) { - creatures = 0; - for (int y = 0; y < 25; y++) { - for (int x = 0; x < 40; x++) { - int nodeNum = int(random(4, 8)); - int muscleNum = int(random(nodeNum, nodeNum*3)); - ArrayList n = new ArrayList(nodeNum); - ArrayList m = new ArrayList(muscleNum); - for (int i = 0; i < nodeNum; i++) { - n.add(new Node(random(-1, 1), random(-1, 1), random(-1, 1), - 0, 0, 0, 0.4, random(0, 1))); //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 = 0; - int tc2 = 0; - 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 s = 0.8; - if (i >= 10) { - s *= 1.414; - } - float len = random(0.5,1.5); - m.add(new Muscle(tc1, tc2, len, random(0.015, 0.06))); - } - float heartbeat = random(40, 80); - c[y*40+x] = new Creature(null, y*40+x+1, new ArrayList(n), new ArrayList(m), 0, true, heartbeat, 1.0, null, null); - c[y*40+x].checkForOverlap(); - c[y*40+x].checkForLoneNodes(); - c[y*40+x].toStableConfiguration(); - c[y*40+x].moveToCenter(); - } - } - creatures = 0; - screenImage.beginDraw(); - screenImage.background(gridBGColor); - screenImage.scale(windowSizeMultiplier); - screenImage.pushMatrix(); - screenImage.scale(10.0/scaleToFixBug); - for (int y = 0; y < 25; y++) { - for (int x = 0; x < 40; x++) { - screenImage.pushMatrix(); - screenImage.translate((x*3+5.5)*scaleToFixBug, (y*2.5+3)*scaleToFixBug, 0); - c[y*40+x].drawCreature(screenImage,true); - screenImage.popMatrix(); - } - } - screenImage.noLights(); - screenImage.popMatrix(); - screenImage.noStroke(); - screenImage.fill(100, 100, 200); - screenImage.rect(900, 664, 260, 40); - screenImage.fill(0); - screenImage.textAlign(CENTER); - screenImage.textFont(font, 24); - screenImage.text("Here are your 1000 randomly generated creatures!!!", windowWidth/2-200, 690); - screenImage.text("Back", windowWidth-250, 690); - screenImage.endDraw(); - setMenu(3); - }else if(menu == 3){ - background(0,0,255); - image(screenImage, 0, 0, 1280, 720); - }else if (menu == 4) { - setGlobalVariables(c[creaturesTested]); - setMenu(5); - if (!stepbystepslow) { - for (int i = 0; i < 1000; i++) { - setGlobalVariables(c[i]); - for (int s = 0; s < 900; s++) { - simulateCurrentCreature(); - } - setAverages(); - setFitness(i); - } - setMenu(6); - } - } - if (menu == 5) { //simulate running - if (timer <= 900) { - keysToMoveCamera(); - simulationImage.beginDraw(); - simulationImage.background(120, 200, 255); - for (int s = 0; s < speed; s++) { - if (timer < 900) { - simulateCurrentCreature(); - } - } - setAverages(); - if (speed < 30) { - for (int s = 0; s < speed; s++) { - moveCamera(); - } - } else { - camX = averageX; - camY = averageY; - camZ = averageZ; - } - float camDist = (height/2.0) / tan(PI*30.0 / 180.0); - simulationImage.pushMatrix(); - simulationImage.camera(camX/camZoom+camDist*sin(camHA)*cos(camVA), - camY/camZoom+camDist*sin(camVA), camZ/camZoom+camDist*cos(camHA)*cos(camVA), - camX/camZoom, camY/camZoom, camZ/camZoom, 0, 1, 0); - - simulationImage.scale(1.0/camZoom/scaleToFixBug); - - drawPosts(simulationImage); - drawGround(simulationImage); - currentCreature.drawCreature(simulationImage,false); - drawArrow(averageX,averageY,averageZ,simulationImage); - simulationImage.popMatrix(); - simulationImage.endDraw(); - image(simulationImage,0,0,width/windowSizeMultiplier, - height/windowSizeMultiplier); - drawBrain(40,20,0,5,currentCreature); - drawStats(windowWidth-10,0,0,0.7); - drawSkipButton(); - drawOtherButtons(); - } - 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 "+fitnessName+":", windowWidth/2, 300); - text(nf(getFitness(),0,2) + " "+fitnessUnit, windowWidth/2, 400); - } else { - timer = 1020; - } - setFitness(creaturesTested); - } - if (timer >= 1020) { - setMenu(4); - creaturesTested++; - if (creaturesTested == 1000) { - setMenu(6); - } - camX = 0; - } - if (timer >= 900) { - timer += speed; - } - } - if (menu == 6) { - //sort - c2 = new ArrayList(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,false,false)); - creatureDatabase.add(c2.get(499).copyCreature(-1,false,false)); - creatureDatabase.add(c2.get(0).copyCreature(-1,false,false)); - - 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).name[0];//(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) { - timer = 100000; // DOY IM IMPATIENT - //cool sorting animation - screenImage.beginDraw(); - screenImage.background(gridBGColor); - screenImage.pushMatrix(); - screenImage.scale(10.0/scaleToFixBug*windowSizeMultiplier); - 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); - screenImage.translate((x3*3+5.5)*scaleToFixBug, (y3*2.5+4)*scaleToFixBug, 0); - cj.drawCreature(screenImage,true); - } - screenImage.popMatrix(); - if (stepbystepslow) { - timer+=5; - }else{ - timer+=20; - } - screenImage.endDraw(); - image(screenImage, 0, 0, 1280, 720); - drawSkipButton(); - if (timer > 60*PI) { - drawScreenImage(1); - setMenu(9); - } - } - 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 { - 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++) { - if(random(0,1) < getSB(gen)){ - float f = float(j)/1000; - float rand = (pow(random(-1, 1), 3)+1)/2; //cube function - slowDies = (f <= rand); - }else{ - slowDies = (random(0,1) < 0.5); - } - 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(j2, cj.copyCreature(cj.id+1000,true,false)); //duplicate - c2.set(999-j2, cj.modified(cj2.id+1000)); //mutated offspring 1 - } - for (int j = 0; j < 1000; j++) { - Creature cj = c2.get(j); - c[cj.id-(gen*1000)-1001] = cj.copyCreature(-1,false,false); - } - drawScreenImage(3); - gen++; - if (stepbystep) { - setMenu(13); - } else { - setMenu(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;; - 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) { - simulationImage.beginDraw(); - simulationImage.clear(); - simulationImage.endDraw(); - for (int k = 0; k < 3; k++) { - fill(220); - rect(760+k*160, 180, 140, 140); - simulationImage.beginDraw(); - simulationImage.pushMatrix(); - simulationImage.translate(830+160*k, 260,0); - simulationImage.scale(60.0/scaleToFixBug); - creatureDatabase.get((genSelected-1)*3+k).drawCreature(simulationImage,true); - simulationImage.popMatrix(); - simulationImage.endDraw(); - } - image(simulationImage,0,0,width,height); - - textAlign(CENTER); - 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(prevStatusWindow == -4); - if (statusWindow >= -3 && !miniSimulation) { - openMiniSimulation(); - } - } - /*if(menu >= 1){ - fill(255); - rect(0,705,100,15); - fill(0); - textAlign(LEFT); - textFont(font,12); - int g = gensToDo; - if(gensToDo >= 10000){ - g = 1000000000-gensToDo; - } - text(g,2,715); - }*/ - overallTimer++; -} -String percentify(float n){ - return nf(n*100,0,2)+"%"; -} -float getSB(int g){ - return 1.0; - //return 0.7+0.3*cos(g*(2*PI)/50.0); -} -void keysToMoveCamera(){ - if(keyPressed){ - if(key == 'w'){ - camVA -= CAMERA_MOVE_SPEED; - } - if(key == 's'){ - camVA += CAMERA_MOVE_SPEED; - } - if(key == 'a'){ - camHA -= CAMERA_MOVE_SPEED; - } - if(key == 'd'){ - camHA += CAMERA_MOVE_SPEED; - } - /*if(key == 'i'){ - for(int i = 2; i < n.size(); i++){ - n.get(i).vy -= 0.03; - } - } - if(key == 'j'){ - for(int i = 2; i < n.size(); i++){ - n.get(i).vx -= 0.03; - } - } - if(key == 'k'){ - for(int i = 2; i < n.size(); i++){ - n.get(i).vy += 0.03; - } - } - if(key == 'l'){ - for(int i = 2; i < n.size(); i++){ - n.get(i).vx += 0.03; - } - }*/ - } - camVA = min(max(camVA,-PI*0.499),-PI*0.001); -} -void keyPressed(){ - if(key == 't'){ - foodAngleChange += 5.0/360.0*(2*PI); - setMenu(1); - } - if(key == 'g'){ - foodAngleChange -= 5.0/360.0*(2*PI); - setMenu(1); - } -} -void drawStats(float x, float y, float z, float size){ - textAlign(RIGHT); - textFont(font, 32); - fill(0); - pushMatrix(); - translate(x,y,z); - scale(size); - text(toRealName(currentCreature.name), 0, 32); - text("Creature ID: "+currentCreature.id, 0, 64); - text("Time: "+nf(timer/60.0,0,2)+" / 15 sec.", 0, 96); - text("Playback Speed: x"+max(1,speed), 0, 128); - String extraWord = "used"; - if(energyDirection == -1){ - extraWord = "left"; - } - text("X: "+nf(averageX/5.0,0,2)+"", 0, 160); - text("Y: "+nf(-averageY/5.0,0,2)+"", 0, 192); - text("Z: "+nf(-averageZ/5.0,0,2)+"", 0, 224); - //text("Energy "+extraWord+": "+nf(energy,0,2)+" yums", 0, 256); - //text("A.N.Nausea: "+nf(averageNodeNausea,0,2)+" blehs", 0, 256); - - popMatrix(); -} -String toRealName(int[] n){ - String[] parts = patronData[n[0]].split(","); - if(parts[1].length() == 0){ - return parts[0]+"'s "+rankify(n[1]+1)+" creature"; - }else{ - return parts[0]+" "+parts[1]+"'s "+rankify(n[1]+1)+" creature"; - } -} -String toRealSpeciesName(int n){ - String[] parts = patronData[n].split(","); - if(parts[1].length() == 0){ - return parts[0]; - }else{ - return parts[0]+" "+parts[1]; - } -} -void drawBrain(float x, float y, float z, float size, Creature c){ - pushMatrix(); - translate(x,y,z); - scale(size); - currentCreature.brain.drawBrain(size,currentCreature); - popMatrix(); -} -void drawSkipButton(){ - fill(0); - rect(0,windowHeight-40,90,40); - fill(255); - textAlign(CENTER); - textFont(font, 32); - text("SKIP",45,windowHeight-8); -} -void drawOtherButtons(){ - fill(0); - rect(120,windowHeight-40,240,40); - fill(255); - textAlign(CENTER); - textFont(font, 32); - text("PB speed: x"+speed,240,windowHeight-8); - fill(0); - rect(windowWidth-120,windowHeight-40,120,40); - fill(255); - textAlign(CENTER); - textFont(font, 32); - text("FINISH",windowWidth-60,windowHeight-8); -} -void setGlobalVariables(Creature thisCreature) { - currentCreature = thisCreature.copyCreature(-1,false,true); - timer = 0; - camZoom = 0.01; - camX = 0; - camY = 0; - camVA = -0.5; - camHA = 0.0; - simulationTimer = 0; - energy = baselineEnergy; - totalNodeNausea = 0; - averageNodeNausea = 0; - cumulativeAngularVelocity = 0; - foodAngle = 0.0; - chomps = 0; - foodX = 0; - foodY = 0; - foodZ = 0; - setFoodLocation(); -} -void setFoodLocation(){ - setAverages(); - foodAngle += currentCreature.foodPositions[chomps][0]; - float sinA = sin(foodAngle); - float cosA = cos(foodAngle); - float furthestNodeForward = 0; - for(int i = 0; i < currentCreature.n.size(); i++){ - Node ni = currentCreature.n.get(i); - float newX = (ni.x-averageX)*cosA-(ni.z-averageZ)*sinA; - if(newX >= furthestNodeForward){ - furthestNodeForward = newX; - } - } - float d = MIN_FOOD_DISTANCE+(MAX_FOOD_DISTANCE-MIN_FOOD_DISTANCE)*currentCreature.foodPositions[chomps][2]; - foodX = foodX+cos(foodAngle)*(furthestNodeForward+d); - foodZ = foodZ+sin(foodAngle)*(furthestNodeForward+d); - foodY = currentCreature.foodPositions[chomps][1]; - startingFoodDistance = getCurrentFoodDistance(); -} -float getCurrentFoodDistance(){ - float closestDist = 9999; - for(int i = 0; i < currentCreature.n.size(); i++){ - Node n = currentCreature.n.get(i); - float distFromFood = dist(n.x,n.y,n.z,foodX,foodY,foodZ)-0.4; - if(distFromFood < closestDist){ - closestDist = distFromFood; - } - } - return closestDist; -} -int[] getNewCreatureName(){ - float indexOfChoice = random(0,TOTAL_PLEDGED); - float runningTotal = 0; - for(int i = 0; i < patronData.length; i++){ - String[] parts = patronData[i].split(","); - runningTotal += Float.parseFloat(parts[3]); - if(runningTotal >= indexOfChoice){ - int[] result = new int[2]; - result[0] = i; - result[1] = CREATURES_PER_PATRON[i]; - CREATURES_PER_PATRON[i]++; - return result; - } - } - return null; -} -String rankify(int s){ - if(s >= 11 && s <= 19){ - return s+"th"; - }else if(s%10 == 1){ - return s+"st"; - }else if(s%10 == 2){ - return s+"nd"; - }else if(s%10 == 3){ - return s+"rd"; - }else{ - return s+"th"; - } -} -float getFitness(){ - Boolean hasNodeOffGround = false; - for(int i = 0; i < currentCreature.n.size(); i++){ - if(currentCreature.n.get(i).y <= -0.2001){ - hasNodeOffGround = true; - } - } - if(hasNodeOffGround){ - float withinChomp = max(1.0-getCurrentFoodDistance()/startingFoodDistance,0); - return chomps+withinChomp;//cumulativeAngularVelocity/(n.size()-2)/pow(averageNodeNausea,0.3);// /(2*PI)/(n.size()-2); //dist(0,0,averageX,averageZ)*0.2; // Multiply by 0.2 because a meter is 5 units for some weird reason. - }else{ - return 0; - } -} -void setFitness(int i){ - c[i].d = getFitness(); +final float windowSizeMultiplier = 1.4; +int SEED = 31; //7; ;( + +PFont font; +ArrayList percentile = new ArrayList(0); +ArrayList barCounts = new ArrayList(0); +ArrayList speciesCounts = new ArrayList(0); +ArrayList topSpeciesCounts = new ArrayList(0); +ArrayList creatureDatabase = new ArrayList(0); +ArrayList rects = new ArrayList(0); +Creature currentCreature; +PGraphics simulationImage; +PGraphics graphImage; +PGraphics screenImage; +PGraphics popUpImage; +PGraphics segBarImage; +boolean haveGround = true; +int histBarsPerMeter = 5; +String fitnessUnit = "chomps"; +String fitnessName = "Chomps"; +float baselineEnergy = 0.0; +int energyDirection = 1; // if 1, it'll count up how much energy is used. if -1, it'll count down from the baseline energy, and when energy hits 0, the creature dies. +final float FRICTION = 4; +float bigMutationChance = 0.03; +float hazelStairs = -1; +float cumulativeAngularVelocity = 0; +boolean saveFramesPerGeneration = true; +color gridBGColor = color(220, 253, 102, 255); +float foodAngleChange = 0.0; +float foodX = 0; +float foodY = 0; +float foodZ = 0; +float foodAngle = 0; +int chomps = 0; + +int lastImageSaved = -1; +float pressureUnit = 500.0/2.37; +float energyUnit = 20; +float nauseaUnit = 5; +int minBar = -10; +int maxBar = 100; +int barLen = maxBar-minBar; +int gensToDo = 0; +float postFontSize = 0.96; +float scaleToFixBug = 1000; +float energy = 0; +float averageNodeNausea = 0; +float totalNodeNausea = 0; +float CAMERA_MOVE_SPEED = 0.03; + +float lineY1 = -0.08; // These are for the lines of text on each node. +float lineY2 = 0.35; + +int windowWidth = 1280; +int windowHeight = 720; +int timer = 0; +float camX = 0; +float camY = 0; +float camZ = 0; +float camHA = 0; +float camVA = -0.5; +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 prevStatusWindow = -4; +int overallTimer = 0; +boolean miniSimulation = false; +int creatureWatching = 0; +int simulationTimer = 0; +int[] creaturesInPosition = new int[1000]; + +float camZoom = 0.015; +float gravity = 0.006;//0.007; +float airFriction = 0.95; +float MIN_FOOD_DISTANCE = 1; +float MAX_FOOD_DISTANCE = 2.5; + +float target; +float force; +float averageX; +float averageY; +float averageZ; +int speed; +boolean stepbystep; +boolean stepbystepslow; +boolean slowDies; +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 +}; +final int BRAIN_WIDTH = 3; +float STARTING_AXON_VARIABILITY = 1.0; +float AXON_START_MUTABILITY = 0.0005; +String[] patronData; +int PATRON_COUNT = 75; +float TOTAL_PLEDGED = 183.39; +int[] CREATURES_PER_PATRON = new int[PATRON_COUNT]; +float startingFoodDistance = 0; + +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)); +} +void drawGround(PGraphics img) { + int stairDrawStart = max(1,(int)(-averageY/hazelStairs)-10); + img.noStroke(); + if (haveGround){ + float groundTileSize = 5.0; + int cx = round(averageX/5); + int cz = round(averageZ/5); + for(int x = cx-5; x < cx+5; x++){ + for(int z = cz-5; z < cz+5; z++){ + float lowX = (groundTileSize*x)*scaleToFixBug; + float highX = (groundTileSize*(x+1))*scaleToFixBug; + float lowZ = (groundTileSize*z)*scaleToFixBug; + float highZ = (groundTileSize*(z+1))*scaleToFixBug; + img.fill(0, 100+((x+z+100)%2)*30, 0); + img.beginShape(); + img.vertex(lowX, 0, lowZ); + img.vertex(highX, 0, lowZ); + img.vertex(highX, 0, highZ); + img.vertex(lowX, 0, highZ); + img.endShape(CLOSE); + } + } + } + /*for (int i = 0; i < rects.size(); i++) { + Rectangle r = rects.get(i); + img.rect(r.x1*scaleToFixBug, r.y1*scaleToFixBug, (r.x2-r.x1)*scaleToFixBug, (r.y2-r.y1)*scaleToFixBug); + } + if(hazelStairs > 0){ + for(int i = stairDrawStart; i < stairDrawStart+20; i++){ + img.fill(255,255,255,128); + img.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.3*scaleToFixBug); + img.fill(255,255,255,255); + img.rect((averageX-20)*scaleToFixBug,-hazelStairs*i*scaleToFixBug,40*scaleToFixBug,hazelStairs*0.15*scaleToFixBug); + } + }*/ +} +float toMuscleUsable(float f){ + return min(max(f,0.8),1.2); +} +void drawPosts(PGraphics img) { + int startPostY = min(-8,(int)(averageY/4)*4-4); + img.noStroke(); + img.textAlign(CENTER); + img.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++) { + img.fill(255); + img.rect((i*5.0-0.1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 0.2*scaleToFixBug, 3.0*scaleToFixBug); + img.rect((i*5.0-1)*scaleToFixBug, (-3.0+postY)*scaleToFixBug, 2.0*scaleToFixBug, 1.0*scaleToFixBug); + img.fill(120); + img.textAlign(CENTER); + img.text(i+" m", i*5.0*scaleToFixBug, (-2.17+postY)*scaleToFixBug); + } + }*/ + img.fill(0,0,255); + img.beginShape(); + float s = 0.4*scaleToFixBug; + float y = -0.001*scaleToFixBug; + img.vertex(0,y,s); + img.vertex(-s,y,0); + img.vertex(0,y,-s); + img.vertex(s,y,0); + img.endShape(CLOSE); + + colorMode(HSB,1.0); + color c = color((timer%40)/40.0,1.0,1.0); + img.fill(c); + img.noStroke(); + img.lights(); + + img.pushMatrix(); + img.translate(foodX*scaleToFixBug,foodY*scaleToFixBug,foodZ*scaleToFixBug); + img.sphere(0.4*scaleToFixBug*0.5); + img.popMatrix(); + + img.noLights(); + + colorMode(RGB,255); + img.fill(0,60,0); + img.pushMatrix(); + img.translate(foodX*scaleToFixBug,0,foodZ*scaleToFixBug); + img.scale(1,0.02,1); + img.sphere(0.4*scaleToFixBug*0.5); + img.popMatrix(); +} +void drawArrow(float x, float y, float z, PGraphics img) { + img.noLights(); + img.pushMatrix(); + img.translate(x*scaleToFixBug,0,z*scaleToFixBug); + img.rotateY(camHA); + img.rotateX(-camVA); + img.textAlign(CENTER); + img.textFont(font, postFontSize*scaleToFixBug); + img.noStroke(); + img.fill(255, 0, 0); + img.beginShape(); + float dist = 2.7*scaleToFixBug; + img.vertex(dist, -3.8*scaleToFixBug, 0); + img.vertex(dist, -2.7*scaleToFixBug, 0); + img.vertex(-dist, -2.7*scaleToFixBug, 0); + img.vertex(-dist, -3.8*scaleToFixBug, 0); + img.endShape(); + img.beginShape(); + img.vertex(0, -2.2*scaleToFixBug); + img.vertex(-0.5*scaleToFixBug, -2.7*scaleToFixBug); + img.vertex(0.5*scaleToFixBug, -2.7*scaleToFixBug); + img.endShape(CLOSE); + String fitnessString = nf(getFitness(),0,2)+" "+fitnessUnit; + img.fill(255); + img.text(fitnessString, 0, -2.91*scaleToFixBug,0.1*scaleToFixBug); + img.popMatrix(); +} +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 = 563.333333333/gen; + float lineX = 136.66666+genSelected*genWidth; + line(lineX, 180, lineX, 500+180); + Integer[] s = speciesCounts.get(genSelected); + textAlign(RIGHT); + 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+3, y, 56, 14); + + fill(0); + text(toRealSpeciesName(i-1)+": "+c, lineX+58, y+11); + + //colorMode(HSB, 1.0); + //fill(getColor(i-1, true)); + //text("S"+floor((i-1)/10)+""+((i-1)%10)+": "+c, lineX+5, y+11); + //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; + } + float light = 1.0; + if (abs(col-0.333) <= 0.18 && adjust) { + light = 0.7; + } + color c = color(col, 1.0, light); + colorMode(RGB, 255); + return c; +} +void drawGraph(int graphWidth, int graphHeight) { + graphImage.beginDraw(); + graphImage.smooth(); + graphImage.background(220); + if (gen >= 1) { + drawLines(130, int(graphHeight*0.05), graphWidth-130, int(graphHeight*0.9)); + drawSegBars(130, 0, graphWidth-130, 150); + } + graphImage.endDraw(); +} +void drawLines(int x, int y, int graphWidth, int graphHeight) { + 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)+" "+fitnessUnit, 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); + } + } +} +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)-2; + if ((unit2+90)%3 < 1) { + return pow(10, floor(unit2/3)); + } else if ((unit2+90)%3 < 2) { + return pow(10, floor((unit2-1)/3))*2; + } else { + return pow(10, floor((unit2-2)/3))*5; + } +} +String showUnit(float i, float unit) { + if (unit < 1) { + return nf(i, 0, 2)+""; + } + else { + return int(i)+""; + } +} +ArrayList quickSort(ArrayList c) { + if (c.size() <= 1) { + return c; + } + else { + ArrayList less = new ArrayList(); + ArrayList more = new ArrayList(); + ArrayList equal = new ArrayList(); + 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 total = new ArrayList(); + total.addAll(quickSort(more)); + total.addAll(equal); + total.addAll(quickSort(less)); + return total; + } +} +void adjustToCenter(int nodeNum) { + float avx = 0; + float lowY = -1000; + for (int i = 0; i < nodeNum; i++) { + Node ni = currentCreature.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 = currentCreature.n.get(i); + ni.x -= avx; + ni.y -= lowY; + } +} +void setAverages() { + averageX = 0; + averageY = 0; + averageZ = 0; + for (int i = 0; i < currentCreature.n.size(); i++) { + Node ni = currentCreature.n.get(i); + averageX += ni.x; + averageY += ni.y; + averageZ += ni.z; + } + averageX = averageX/currentCreature.n.size(); + averageY = averageY/currentCreature.n.size(); + averageZ = averageZ/currentCreature.n.size(); +} +Creature[] c = new Creature[1000]; +ArrayList c2 = new ArrayList(); + +void mouseWheel(MouseEvent event) { + float delta = event.getCount(); + if (menu == 5 || statusWindow >= -3) { + if (delta == -1) { + camZoom *= 0.9090909; + if (camZoom < 0.002) { + camZoom = 0.002; + } + textFont(font, postFontSize); + } else if (delta == 1) { + camZoom *= 1.1; + if (camZoom > 0.1) { + camZoom = 0.1; + } + textFont(font, postFontSize); + } + } +} + +void mousePressed() { + if (gensToDo >= 1) { + gensToDo = 0; + } + float mX = mouseX/windowSizeMultiplier; + float mY = mouseY/windowSizeMultiplier; + 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); + } +} +String zeros(int n, int zeros){ + String s = n+""; + for(int i = s.length(); i < zeros; i++){ + s = "0"+s; + } + return s; +} + +void startASAP() { + setMenu(4); + creaturesTested = 0; + stepbystep = false; + stepbystepslow = false; +} +void mouseReleased() { + drag = false; + miniSimulation = false; + float mX = mouseX/windowSizeMultiplier; + float mY = mouseY/windowSizeMultiplier; + if (menu == 0 && abs(mX-windowWidth/2) <= 200 && abs(mY-400) <= 100) { + setMenu(1);//TODO add extra button click + }else if(menu == 0 && abs(mX -windowWidth/2) <=150 && abs(mY - 600) <=50){ + selectInput("Select a file to load", "fileSelected"); + }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); + speed = 1; + 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 == 1 && gen != -1 && abs(mX - 650) <= 50 && abs(mY - 90) <= 20){ + selectOutput("Select file to save simulation to", "saveSelected"); + }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 == 5 || menu == 4) && mY >= windowHeight-40){ + if(mX < 90){ + for (int s = timer; s < 900; s++) { + simulateCurrentCreature(); + } + timer = 1021; + }else if(mX >= 120 && mX < 360){ + speed *= 2; + if(speed == 1024) speed = 900; + if(speed >= 1800) speed = 1; + }else if(mX >= windowWidth-120){ + for (int s = timer; s < 900; s++) { + simulateCurrentCreature(); + } + timer = 0; + creaturesTested++; + for (int i = creaturesTested; i < 1000; i++) { + setGlobalVariables(c[i]); + for (int s = 0; s < 900; s++) { + simulateCurrentCreature(); + } + setAverages(); + setFitness(i); + } + setMenu(6); + } + } else if(menu == 8 && mX < 90 && mY >= windowHeight-40){ + timer = 100000; + } 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 simulateCurrentCreature(){ + currentCreature.simulate(); + averageNodeNausea = totalNodeNausea/currentCreature.n.size(); + simulationTimer++; + timer++; +} +void drawScreenImage(int stage) { + screenImage.beginDraw(); + screenImage.pushMatrix(); + screenImage.scale(10.0*windowSizeMultiplier/scaleToFixBug); + screenImage.smooth(); + screenImage.background(gridBGColor); + screenImage.noStroke(); + 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++; + screenImage.pushMatrix(); + screenImage.translate((x*3+5.5)*scaleToFixBug, (y*2.5+3)*scaleToFixBug, 0); + cj.drawCreature(screenImage,true); + screenImage.popMatrix(); + } + timer = 0; + screenImage.popMatrix(); + screenImage.noLights(); + + screenImage.pushMatrix(); + screenImage.scale(windowSizeMultiplier); // Arbitrary, do not change. + + screenImage.textAlign(CENTER); + screenImage.textFont(font, 24); + screenImage.fill(100, 100, 200); + screenImage.noStroke(); + 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) { + /*screenImage.pushMatrix(); + screenImage.scale(10.0*windowSizeMultiplier/scaleToFixBug); + + screenImage.translate(x*18+500, y*16+200, 570); + screenImage.scale(0.13); + cj.drawCreature(screenImage,false); + screenImage.popMatrix();*/ + } else { + screenImage.fill(0); + screenImage.beginShape(); + screenImage.vertex(x*30+40, y*25+17,0.01); + screenImage.vertex(x*30+70, y*25+17,0.01); + screenImage.vertex(x*30+70, y*25+42,0.01); + screenImage.vertex(x*30+40, y*25+42,0.01); + screenImage.endShape(); + } + } + } 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.popMatrix(); + screenImage.endDraw(); +} +void drawpopUpImage() { + setAverages(); + moveCamera(); + popUpImage.beginDraw(); + popUpImage.smooth(); + + float camDist = (450/2.0) / tan(PI*30.0 / 180.0); + popUpImage.pushMatrix(); + + popUpImage.camera(camX/camZoom+camDist*sin(camHA)*cos(camVA), + camY/camZoom+camDist*sin(camVA), camZ/camZoom+camDist*cos(camHA)*cos(camVA), + camX/camZoom, camY/camZoom, camZ/camZoom, 0, 1, 0); + + popUpImage.scale(1.0/camZoom/scaleToFixBug); + + if (simulationTimer < 900) { + popUpImage.background(120, 200, 255); + } else { + popUpImage.background(60, 100, 128); + } + drawPosts(popUpImage); + drawGround(popUpImage); + currentCreature.drawCreature(popUpImage,false); + drawArrow(averageX,averageY,averageZ,popUpImage); + popUpImage.noStroke(); + popUpImage.endDraw(); + popUpImage.popMatrix(); +} +void moveCamera(){ + camX += (averageX-camX)*0.2; + camY += (averageY-camY)*0.2; + camZ += (averageZ-camZ)*0.2; +} +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, 0, x+hw, theY, 0); + if (i == 0) theY -= 5; + text(i, x+hw+5, theY+7, 0); + } + textAlign(CENTER); + for (int i = minBar; i <= maxBar; i += 10) { + 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, 0); + line(theX, y, 0, theX, y+hh, 0); + } + 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(boolean isFirstFrame) { + 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) { + keysToMoveCamera(); + int py2 = py-175; + if (py >= 360) { + py2 -= 190; + }else { + py2 += 238; + } + py = min(max(py,0),420); + int px2 = min(max(px-90, 10), 900); + drawpopUpImage(); + pushMatrix(); + translate(0,0,1); + image(popUpImage, px2, py2, 360, 360); + popMatrix(); + drawBrain(px2-130, py2, 1,5, cj); + drawStats(px2+355, py2+239, 1, 0.45); + + simulateCurrentCreature(); + int shouldBeWatching = statusWindow; + if (statusWindow <= -1) { + cj = creatureDatabase.get((genSelected-1)*3+statusWindow+3); + shouldBeWatching = cj.id; + } + if (creatureWatching != shouldBeWatching || isFirstFrame) { + openMiniSimulation(); + } + } +} + +void settings(){ + size((int)(windowWidth*windowSizeMultiplier), (int)(windowHeight*windowSizeMultiplier),P3D); +} + +void setup() { + //fullScreen(); + + String[] prePatronData = loadStrings("PatronReport_2017-06-12.csv"); + patronData = new String[PATRON_COUNT]; + int lineAt = 0; + for(int i = 0; i < prePatronData.length; i++){ + if(i != 0 && prePatronData[i].indexOf("Reward") == -1){ + patronData[lineAt] = prePatronData[i]; + lineAt++; + } + } + for(int i = 0; i < PATRON_COUNT; i++){ + CREATURES_PER_PATRON[i] = 0; + } + frameRate(60); + randomSeed(SEED); + noSmooth(); + + 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); + + simulationImage = createGraphics(width, height, P3D); + graphImage = createGraphics(975, 570); + screenImage = createGraphics(width, height, P3D); + popUpImage = createGraphics(450, 450, P3D); + segBarImage = createGraphics(975, 150); + segBarImage.beginDraw(); + segBarImage.smooth(); + segBarImage.background(220); + segBarImage.endDraw(); + popUpImage.beginDraw(); + popUpImage.smooth(); + popUpImage.background(220); + popUpImage.endDraw(); + + font = loadFont("Helvetica-Bold-96.vlw"); + textFont(font, 96); + textAlign(CENTER); + + /*rects.add(new Rectangle(4,-7,9,-3)); + rects.add(new Rectangle(6,-1,10,10)); + rects.add(new Rectangle(9.5,-1.5,13,10)); + rects.add(new Rectangle(12,-2,16,10)); + rects.add(new Rectangle(15,-2.5,19,10)); + rects.add(new Rectangle(18,-3,22,10)); + rects.add(new Rectangle(21,-3.5,25,10)); + rects.add(new Rectangle(24,-4,28,10)); + rects.add(new Rectangle(27,-4.5,31,10)); + rects.add(new Rectangle(30,-5,34,10)); + rects.add(new Rectangle(33,-5.5,37,10)); + rects.add(new Rectangle(36,-6,40,10)); + rects.add(new Rectangle(39,-6.5,100,10));*/ + + //rects.add(new Rectangle(-100,-100,100,-2.8)); + //rects.add(new Rectangle(-100,0,100,100)); + //Snaking thing below: + /*rects.add(new Rectangle(-400,-10,1.5,-1.5)); + rects.add(new Rectangle(-400,-10,3,-3)); + rects.add(new Rectangle(-400,-10,4.5,-4.5)); + rects.add(new Rectangle(-400,-10,6,-6)); + rects.add(new Rectangle(0.75,-0.75,400,4)); + rects.add(new Rectangle(2.25,-2.25,400,4)); + rects.add(new Rectangle(3.75,-3.75,400,4)); + rects.add(new Rectangle(5.25,-5.25,400,4)); + rects.add(new Rectangle(-400,-5.25,0,4));*/ +} +void draw() { + scale(windowSizeMultiplier); + if (menu == 0) { + background(255); + fill(100, 200, 100); + noStroke(); + rect(windowWidth/2-200, 300, 400, 200); + rect(windowWidth/2-150, 550, 300, 100); + fill(0); + textSize(60); + text("EVOLUTION!", windowWidth/2, 200); + text("START", windowWidth/2, 430); + textSize(26); + text("Load simulation", windowWidth/2, 600); + }else if (menu == 1) { + noStroke(); + fill(0); + background(255, 200, 130); + textFont(font, 32); + textAlign(LEFT); + textFont(font, 96); + text("GEN "+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); + rect(600, 70, 100, 40); + if (gensToDo >= 2) { + fill(128, 255, 128); + } else { + fill(70, 140, 70); + } + rect(990, 120, 230, 40); + fill(0); + //text("Survivor Bias: "+percentify(getSB(genSelected)), 437, 50); + text("Curve: ±"+nf(foodAngleChange/(2*PI)*360,0,2)+" degrees", 420, 50); + 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("Save", 610, 100); + textAlign(CENTER); + textAlign(RIGHT); + text(float(round(percentile.get(min(genSelected, percentile.size()-1))[14]*1000))/1000+" "+fitnessUnit, 700, 160); + drawHistogram(760, 410, 460, 280); + drawGraphImage(); + //if(saveFramesPerGeneration && gen > lastImageSaved){ + // saveFrame("imgs//"+zeros(gen,5)+".png"); + // lastImageSaved = gen; + //} + } + if (gensToDo >= 1) { + gensToDo--; + if (gensToDo >= 1) { + startASAP(); + } + } + }else if (menu == 2) { + creatures = 0; + for (int y = 0; y < 25; y++) { + for (int x = 0; x < 40; x++) { + int nodeNum = int(random(4, 8)); + int muscleNum = int(random(nodeNum, nodeNum*3)); + ArrayList n = new ArrayList(nodeNum); + ArrayList m = new ArrayList(muscleNum); + for (int i = 0; i < nodeNum; i++) { + n.add(new Node(random(-1, 1), random(-1, 1), random(-1, 1), + 0, 0, 0, 0.4, random(0, 1))); //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 = 0; + int tc2 = 0; + 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 s = 0.8; + if (i >= 10) { + s *= 1.414; + } + float len = random(0.5,1.5); + m.add(new Muscle(tc1, tc2, len, random(0.015, 0.06))); + } + float heartbeat = random(40, 80); + c[y*40+x] = new Creature(null, y*40+x+1, new ArrayList(n), new ArrayList(m), 0, true, heartbeat, 1.0, null, null); + c[y*40+x].checkForOverlap(); + c[y*40+x].checkForLoneNodes(); + c[y*40+x].toStableConfiguration(); + c[y*40+x].moveToCenter(); + } + } + creatures = 0; + screenImage.beginDraw(); + screenImage.background(gridBGColor); + screenImage.scale(windowSizeMultiplier); + screenImage.pushMatrix(); + screenImage.scale(10.0/scaleToFixBug); + for (int y = 0; y < 25; y++) { + for (int x = 0; x < 40; x++) { + screenImage.pushMatrix(); + screenImage.translate((x*3+5.5)*scaleToFixBug, (y*2.5+3)*scaleToFixBug, 0); + c[y*40+x].drawCreature(screenImage,true); + screenImage.popMatrix(); + } + } + screenImage.noLights(); + screenImage.popMatrix(); + screenImage.noStroke(); + screenImage.fill(100, 100, 200); + screenImage.rect(900, 664, 260, 40); + screenImage.fill(0); + screenImage.textAlign(CENTER); + screenImage.textFont(font, 24); + screenImage.text("Here are your 1000 randomly generated creatures!!!", windowWidth/2-200, 690); + screenImage.text("Back", windowWidth-250, 690); + screenImage.endDraw(); + setMenu(3); + }else if(menu == 3){ + background(0,0,255); + image(screenImage, 0, 0, 1280, 720); + }else if (menu == 4) { + setGlobalVariables(c[creaturesTested]); + setMenu(5); + if (!stepbystepslow) { + for (int i = 0; i < 1000; i++) { + setGlobalVariables(c[i]); + for (int s = 0; s < 900; s++) { + simulateCurrentCreature(); + } + setAverages(); + setFitness(i); + } + setMenu(6); + } + } + if (menu == 5) { //simulate running + if (timer <= 900) { + keysToMoveCamera(); + simulationImage.beginDraw(); + simulationImage.background(120, 200, 255); + for (int s = 0; s < speed; s++) { + if (timer < 900) { + simulateCurrentCreature(); + } + } + setAverages(); + if (speed < 30) { + for (int s = 0; s < speed; s++) { + moveCamera(); + } + } else { + camX = averageX; + camY = averageY; + camZ = averageZ; + } + float camDist = (height/2.0) / tan(PI*30.0 / 180.0); + simulationImage.pushMatrix(); + simulationImage.camera(camX/camZoom+camDist*sin(camHA)*cos(camVA), + camY/camZoom+camDist*sin(camVA), camZ/camZoom+camDist*cos(camHA)*cos(camVA), + camX/camZoom, camY/camZoom, camZ/camZoom, 0, 1, 0); + + simulationImage.scale(1.0/camZoom/scaleToFixBug); + + drawPosts(simulationImage); + drawGround(simulationImage); + currentCreature.drawCreature(simulationImage,false); + drawArrow(averageX,averageY,averageZ,simulationImage); + simulationImage.popMatrix(); + simulationImage.endDraw(); + image(simulationImage,0,0,width/windowSizeMultiplier, + height/windowSizeMultiplier); + drawBrain(40,20,0,5,currentCreature); + drawStats(windowWidth-10,0,0,0.7); + drawSkipButton(); + drawOtherButtons(); + } + 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 "+fitnessName+":", windowWidth/2, 300); + text(nf(getFitness(),0,2) + " "+fitnessUnit, windowWidth/2, 400); + } else { + timer = 1020; + } + setFitness(creaturesTested); + } + if (timer >= 1020) { + setMenu(4); + creaturesTested++; + if (creaturesTested == 1000) { + setMenu(6); + } + camX = 0; + } + if (timer >= 900) { + timer += speed; + } + } + if (menu == 6) { + //sort + c2 = new ArrayList(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,false,false)); + creatureDatabase.add(c2.get(499).copyCreature(-1,false,false)); + creatureDatabase.add(c2.get(0).copyCreature(-1,false,false)); + + 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).name[0];//(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) { + timer = 100000; // DOY IM IMPATIENT + //cool sorting animation + screenImage.beginDraw(); + screenImage.background(gridBGColor); + screenImage.pushMatrix(); + screenImage.scale(10.0/scaleToFixBug*windowSizeMultiplier); + 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); + screenImage.translate((x3*3+5.5)*scaleToFixBug, (y3*2.5+4)*scaleToFixBug, 0); + cj.drawCreature(screenImage,true); + } + screenImage.popMatrix(); + if (stepbystepslow) { + timer+=5; + }else{ + timer+=20; + } + screenImage.endDraw(); + image(screenImage, 0, 0, 1280, 720); + drawSkipButton(); + if (timer > 60*PI) { + drawScreenImage(1); + setMenu(9); + } + } + 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 { + 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++) { + if(random(0,1) < getSB(gen)){ + float f = float(j)/1000; + float rand = (pow(random(-1, 1), 3)+1)/2; //cube function + slowDies = (f <= rand); + }else{ + slowDies = (random(0,1) < 0.5); + } + 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(j2, cj.copyCreature(cj.id+1000,true,false)); //duplicate + c2.set(999-j2, cj.modified(cj2.id+1000)); //mutated offspring 1 + } + for (int j = 0; j < 1000; j++) { + Creature cj = c2.get(j); + c[cj.id-(gen*1000)-1001] = cj.copyCreature(-1,false,false); + } + drawScreenImage(3); + gen++; + if (stepbystep) { + setMenu(13); + } else { + setMenu(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;; + 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) { + simulationImage.beginDraw(); + simulationImage.clear(); + simulationImage.endDraw(); + for (int k = 0; k < 3; k++) { + fill(220); + rect(760+k*160, 180, 140, 140); + simulationImage.beginDraw(); + simulationImage.pushMatrix(); + simulationImage.translate(830+160*k, 260,0); + simulationImage.scale(60.0/scaleToFixBug); + creatureDatabase.get((genSelected-1)*3+k).drawCreature(simulationImage,true); + simulationImage.popMatrix(); + simulationImage.endDraw(); + } + image(simulationImage,0,0,width,height); + + textAlign(CENTER); + 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(prevStatusWindow == -4); + if (statusWindow >= -3 && !miniSimulation) { + openMiniSimulation(); + } + } + /*if(menu >= 1){ + fill(255); + rect(0,705,100,15); + fill(0); + textAlign(LEFT); + textFont(font,12); + int g = gensToDo; + if(gensToDo >= 10000){ + g = 1000000000-gensToDo; + } + text(g,2,715); + }*/ + overallTimer++; +} +String percentify(float n){ + return nf(n*100,0,2)+"%"; +} +float getSB(int g){ + return 1.0; + //return 0.7+0.3*cos(g*(2*PI)/50.0); +} +void keysToMoveCamera(){ + if(keyPressed){ + if(key == 'w'){ + camVA -= CAMERA_MOVE_SPEED; + } + if(key == 's'){ + camVA += CAMERA_MOVE_SPEED; + } + if(key == 'a'){ + camHA -= CAMERA_MOVE_SPEED; + } + if(key == 'd'){ + camHA += CAMERA_MOVE_SPEED; + } + /*if(key == 'i'){ + for(int i = 2; i < n.size(); i++){ + n.get(i).vy -= 0.03; + } + } + if(key == 'j'){ + for(int i = 2; i < n.size(); i++){ + n.get(i).vx -= 0.03; + } + } + if(key == 'k'){ + for(int i = 2; i < n.size(); i++){ + n.get(i).vy += 0.03; + } + } + if(key == 'l'){ + for(int i = 2; i < n.size(); i++){ + n.get(i).vx += 0.03; + } + }*/ + } + camVA = min(max(camVA,-PI*0.499),-PI*0.001); +} +void keyPressed(){ + if(key == 't'){ + foodAngleChange += 5.0/360.0*(2*PI); + setMenu(1); + } + if(key == 'g'){ + foodAngleChange -= 5.0/360.0*(2*PI); + setMenu(1); + } +} +void drawStats(float x, float y, float z, float size){ + textAlign(RIGHT); + textFont(font, 32); + fill(0); + pushMatrix(); + translate(x,y,z); + scale(size); + text(toRealName(currentCreature.name), 0, 32); + text("Creature ID: "+currentCreature.id, 0, 64); + text("Time: "+nf(timer/60.0,0,2)+" / 15 sec.", 0, 96); + text("Playback Speed: x"+max(1,speed), 0, 128); + String extraWord = "used"; + if(energyDirection == -1){ + extraWord = "left"; + } + text("X: "+nf(averageX/5.0,0,2)+"", 0, 160); + text("Y: "+nf(-averageY/5.0,0,2)+"", 0, 192); + text("Z: "+nf(-averageZ/5.0,0,2)+"", 0, 224); + //text("Energy "+extraWord+": "+nf(energy,0,2)+" yums", 0, 256); + //text("A.N.Nausea: "+nf(averageNodeNausea,0,2)+" blehs", 0, 256); + + popMatrix(); +} +String toRealName(int[] n){ + String[] parts = patronData[n[0]].split(","); + if(parts[1].length() == 0){ + return parts[0]+"'s "+rankify(n[1]+1)+" creature"; + }else{ + return parts[0]+" "+parts[1]+"'s "+rankify(n[1]+1)+" creature"; + } +} +String toRealSpeciesName(int n){ + String[] parts = patronData[n].split(","); + if(parts[1].length() == 0){ + return parts[0]; + }else{ + return parts[0]+" "+parts[1]; + } +} +void drawBrain(float x, float y, float z, float size, Creature c){ + pushMatrix(); + translate(x,y,z); + scale(size); + currentCreature.brain.drawBrain(size,currentCreature); + popMatrix(); +} +void drawSkipButton(){ + fill(0); + rect(0,windowHeight-40,90,40); + fill(255); + textAlign(CENTER); + textFont(font, 32); + text("SKIP",45,windowHeight-8); +} +void drawOtherButtons(){ + fill(0); + rect(120,windowHeight-40,240,40); + fill(255); + textAlign(CENTER); + textFont(font, 32); + text("PB speed: x"+speed,240,windowHeight-8); + fill(0); + rect(windowWidth-120,windowHeight-40,120,40); + fill(255); + textAlign(CENTER); + textFont(font, 32); + text("FINISH",windowWidth-60,windowHeight-8); +} +void setGlobalVariables(Creature thisCreature) { + currentCreature = thisCreature.copyCreature(-1,false,true); + timer = 0; + camZoom = 0.01; + camX = 0; + camY = 0; + camVA = -0.5; + camHA = 0.0; + simulationTimer = 0; + energy = baselineEnergy; + totalNodeNausea = 0; + averageNodeNausea = 0; + cumulativeAngularVelocity = 0; + foodAngle = 0.0; + chomps = 0; + foodX = 0; + foodY = 0; + foodZ = 0; + setFoodLocation(); +} +void setFoodLocation(){ + setAverages(); + foodAngle += currentCreature.foodPositions[chomps][0]; + float sinA = sin(foodAngle); + float cosA = cos(foodAngle); + float furthestNodeForward = 0; + for(int i = 0; i < currentCreature.n.size(); i++){ + Node ni = currentCreature.n.get(i); + float newX = (ni.x-averageX)*cosA-(ni.z-averageZ)*sinA; + if(newX >= furthestNodeForward){ + furthestNodeForward = newX; + } + } + float d = MIN_FOOD_DISTANCE+(MAX_FOOD_DISTANCE-MIN_FOOD_DISTANCE)*currentCreature.foodPositions[chomps][2]; + foodX = foodX+cos(foodAngle)*(furthestNodeForward+d); + foodZ = foodZ+sin(foodAngle)*(furthestNodeForward+d); + foodY = currentCreature.foodPositions[chomps][1]; + startingFoodDistance = getCurrentFoodDistance(); +} +float getCurrentFoodDistance(){ + float closestDist = 9999; + for(int i = 0; i < currentCreature.n.size(); i++){ + Node n = currentCreature.n.get(i); + float distFromFood = dist(n.x,n.y,n.z,foodX,foodY,foodZ)-0.4; + if(distFromFood < closestDist){ + closestDist = distFromFood; + } + } + return closestDist; +} +int[] getNewCreatureName(){ + float indexOfChoice = random(0,TOTAL_PLEDGED); + float runningTotal = 0; + for(int i = 0; i < patronData.length; i++){ + String[] parts = patronData[i].split(","); + runningTotal += Float.parseFloat(parts[3]); + if(runningTotal >= indexOfChoice){ + int[] result = new int[2]; + result[0] = i; + result[1] = CREATURES_PER_PATRON[i]; + CREATURES_PER_PATRON[i]++; + return result; + } + } + return null; +} +String rankify(int s){ + if(s >= 11 && s <= 19){ + return s+"th"; + }else if(s%10 == 1){ + return s+"st"; + }else if(s%10 == 2){ + return s+"nd"; + }else if(s%10 == 3){ + return s+"rd"; + }else{ + return s+"th"; + } +} +float getFitness(){ + Boolean hasNodeOffGround = false; + for(int i = 0; i < currentCreature.n.size(); i++){ + if(currentCreature.n.get(i).y <= -0.2001){ + hasNodeOffGround = true; + } + } + if(hasNodeOffGround){ + float withinChomp = max(1.0-getCurrentFoodDistance()/startingFoodDistance,0); + return chomps+withinChomp;//cumulativeAngularVelocity/(n.size()-2)/pow(averageNodeNausea,0.3);// /(2*PI)/(n.size()-2); //dist(0,0,averageX,averageZ)*0.2; // Multiply by 0.2 because a meter is 5 units for some weird reason. + }else{ + return 0; + } +} +void setFitness(int i){ + c[i].d = getFitness(); +} + +public void fileSelected(File file){ + if(file != null){ + try{ + JSONObject object = loadJSONObject(file.getAbsolutePath()); + loadFromJson(object); + setMenu(1); + randomSeed(SEED); + //redraw(); + }catch(Exception e){ + String[] error = new String[100]; + error[0] = e.toString(); + for(int i = 0; i < e.getStackTrace().length; i++){ + error[i+1] = e.getStackTrace()[i].toString(); + } + saveStrings("error.log", error); + } + } +} + +public void saveSelected(File file){ + if(file != null){ + try{ + JSONObject object = saveToJson(); + saveJSONObject(object, file.getAbsolutePath()); //<>// + }catch(Exception e){ + String[] error = new String[100]; + error[0] = e.toString(); + for(int i = 0; i < e.getStackTrace().length; i++){ + error[i+1] = e.getStackTrace()[i].toString(); + } + saveStrings("error.log", error); + } + } +} + +public JSONObject saveToJson(){ + JSONObject object = new JSONObject(); + object.setInt("seed", SEED); + JSONArray creatureArray = new JSONArray(); + for(int i = 0; i < creatureDatabase.size(); i++){ + if(creatureDatabase.get(i) != null){ + //println(creatureDatabase.get(i)); + creatureArray.setJSONObject(i, creatureDatabase.get(i).saveToJson()); + } + } + object.setJSONArray("creatures", creatureArray); + JSONArray bars = new JSONArray(); + for(int i = 0; i < barCounts.size(); i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < barCounts.get(i).length; j++){ + iArray.setInt(j, barCounts.get(i)[j]); + } + bars.setJSONArray(i,iArray); + } + object.setJSONArray("bars", bars); + JSONArray percentiles = new JSONArray(); + for(int i = 0; i < percentile.size(); i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < percentile.get(i).length; j++){ + iArray.setFloat(j, percentile.get(i)[j]); + } + percentiles.setJSONArray(i,iArray); + } + object.setJSONArray("percentiles", percentiles); + JSONArray species = new JSONArray(); + for(int i = 0; i < speciesCounts.size(); i++){ + JSONArray iArray = new JSONArray(); + for(int j = 0; j < speciesCounts.get(i).length; j++){ + iArray.setInt(j, speciesCounts.get(i)[j]); + } + species.setJSONArray(i,iArray); + } + object.setJSONArray("species", species); + JSONArray cArray = new JSONArray(); + for(int i = 0; i < c.length; i++){ + cArray.setJSONObject(i, c[i].saveToJson()); + } + JSONArray topSpeciesArray = new JSONArray(); + for(int i = 0; i < topSpeciesCounts.size(); i++){ + topSpeciesArray.setInt(i, topSpeciesCounts.get(i)); + } + object.setJSONArray("topSpecies", topSpeciesArray); + object.setJSONArray("c", cArray); + + object.setFloat("foodChange", foodAngleChange); + object.setInt("gen", gen); + return object; +} + +public void loadFromJson(JSONObject parent){ + SEED = parent.getInt("seed"); + creatureDatabase.clear(); + JSONArray creatureArray = parent.getJSONArray("creatures"); + for(int i = 0; i < creatureArray.size(); i++){ + Creature creature = new Creature(new int[2], 0, new ArrayList(), new ArrayList(), 0, false, 0, 0, null, new float[100][3]); + creature.loadFromJson(creatureArray.getJSONObject(i)); + creatureDatabase.add(creature); + } + barCounts.clear(); + JSONArray bars = parent.getJSONArray("bars"); + for(int i = 0; i < bars.size(); i++){ + JSONArray iArray = bars.getJSONArray(i); + Integer[] array = new Integer[iArray.size()]; + for(int j = 0; j < iArray.size(); j++){ + array[j] = iArray.getInt(j); + } + barCounts.add(array); + } + percentile.clear(); + JSONArray percentiles = parent.getJSONArray("percentiles"); + for(int i = 0; i < percentiles.size(); i++){ + JSONArray iArray = percentiles.getJSONArray(i); + Float[] array = new Float[iArray.size()]; + for(int j = 0; j < iArray.size(); j++){ + array[j] = iArray.getFloat(j); + } + percentile.add(array); + } + speciesCounts.clear(); + JSONArray species = parent.getJSONArray("species"); + for(int i = 0; i < species.size(); i++){ + JSONArray iArray = species.getJSONArray(i); + Integer[] array = new Integer[iArray.size()]; + for(int j = 0; j < iArray.size(); j++){ + array[j] = iArray.getInt(j); + } + speciesCounts.add(array); + } + JSONArray cArray = parent.getJSONArray("c"); + c = new Creature[cArray.size()]; + for(int i = 0; i < cArray.size(); i++){ + Creature temp = new Creature(new int[2], 0, new ArrayList(), new ArrayList(), 0, false, 0, 0, null, new float[100][3]); + temp.loadFromJson(cArray.getJSONObject(i)); + c[i] = temp; + c2.add(c[i]); + } + topSpeciesCounts.clear(); + JSONArray topSpeciesArray = parent.getJSONArray("topSpecies"); + for(int i = 0; i < topSpeciesArray.size(); i++){ + topSpeciesCounts.add(topSpeciesArray.getInt(i)); + } + foodAngleChange = parent.getFloat("foodChange"); + gen = parent.getInt("gen"); + genSelected = gen; +} \ No newline at end of file