Created
September 25, 2017 19:52
-
-
Save PatrickMurphy/6272daa7d47ee56bde1cdd542d6b634c to your computer and use it in GitHub Desktop.
Microbes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DNA { | |
float[] _dna; | |
DNA() { | |
this._dna = new float[DNACODE.values().length]; | |
} | |
DNA(DNA d1, DNA d2) { | |
this._dna = this.mutate(d1._dna, d2._dna); | |
} | |
DNA(float[] d) { | |
this._dna = d; | |
} | |
float[] mutate(float[] d1, float[] d2) { | |
float[] temp = new float[d1.length]; | |
for (int i = 0; i<d1.length; i++) { | |
temp[i] = (d1[i]+d2[i])/2; | |
} | |
return temp; | |
} | |
float get(DNACODE dcode) { | |
return this.get(dcode.id); | |
} | |
float get(int id) { | |
return _dna[id]; | |
} | |
DNA set(DNACODE dcode, float value) { | |
this.set(dcode.id, value); | |
return this; | |
} | |
DNA set(int id, float value) { | |
this._dna[id] = value; | |
return this; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum DNACODE { | |
AGGRESSION(0), GUILT(1), SPEED(2); | |
private int id; | |
private DNACODE(int id) { | |
this.id = id; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//bacteria - some make own food single cell | |
//arcaea - similar to bacteria, single cell | |
//protists - single or multi cell, some make food, some don't | |
//virus - uses others to reproduce | |
// strech // fungi - stay still? break down existing nuts, single or multi | |
//strech // micro animal - multi cell, specialized cells, sexual reproduce | |
//strech // micro plants - make own food | |
import java.util.Iterator; | |
import java.util.concurrent.CopyOnWriteArrayList; | |
import java.util.Arrays; | |
// generate random microbe, classify using rules above, draw | |
CopyOnWriteArrayList<Microbe> Microbes = new CopyOnWriteArrayList<Microbe>(); | |
CopyOnWriteArrayList<Microbe> AliveMicrobes = new CopyOnWriteArrayList<Microbe>(); | |
int daySpeed=7;//week at a time | |
float day = 0.00273972602*daySpeed; | |
int[][] genderAge = new int[2][6]; | |
int male = 0; | |
int female = 0; | |
int popSize = 15; | |
int microbeAliveCount = 0; | |
float timeElapsed = 0; | |
int tick = 0; | |
double maxTrend = popSize; | |
double minTrend = 0; | |
boolean trendRecorded = false; | |
LinearRegression lr; | |
double[] popTrend = {popSize, popSize, popSize, popSize, popSize, popSize, popSize, popSize, popSize, popSize}; | |
void setup() { | |
//size(800, 800); | |
fullScreen(2); | |
for (int i = 0; i <= popSize; i++) { | |
Microbe test = new Microbe(); | |
Microbes.add(test); | |
AliveMicrobes.add(test); | |
} | |
maxTrend = Math.max(popTrend[0], Math.max(popTrend[1], Math.max(popTrend[2], Math.max(popTrend[3], Math.max(popTrend[4], Math.max(popTrend[5], Math.max(popTrend[6], Math.max(popTrend[7], Math.max(popTrend[8], popTrend[9]))))))))); | |
minTrend = Math.min(popTrend[0], Math.min(popTrend[1], Math.min(popTrend[2], Math.min(popTrend[3], Math.min(popTrend[4], Math.min(popTrend[5], Math.min(popTrend[6], Math.min(popTrend[7], Math.min(popTrend[8], popTrend[9]))))))))); | |
lr = new LinearRegression(popTrend); | |
} | |
void draw() { | |
background(15, 15, 15); | |
//reset | |
tick++; | |
if (second()%5==0) { | |
if (!trendRecorded) { | |
popTrend[0] = popTrend[1]; | |
popTrend[1] = popTrend[2]; | |
popTrend[2] = popTrend[3]; | |
popTrend[3] = popTrend[4]; | |
popTrend[4] = popTrend[5]; | |
popTrend[5] = popTrend[6]; | |
popTrend[6] = popTrend[7]; | |
popTrend[7] = popTrend[8]; | |
popTrend[8] = popTrend[9]; | |
popTrend[9] = microbeAliveCount; | |
trendRecorded = true; | |
maxTrend = Math.max(popTrend[0], Math.max(popTrend[1], Math.max(popTrend[2], Math.max(popTrend[3], Math.max(popTrend[4], Math.max(popTrend[5], Math.max(popTrend[6], Math.max(popTrend[7], Math.max(popTrend[8], popTrend[9]))))))))); | |
minTrend = Math.min(popTrend[0], Math.min(popTrend[1], Math.min(popTrend[2], Math.min(popTrend[3], Math.min(popTrend[4], Math.min(popTrend[5], Math.min(popTrend[6], Math.min(popTrend[7], Math.min(popTrend[8], popTrend[9]))))))))); | |
lr = new LinearRegression(popTrend); | |
} | |
} else { | |
trendRecorded = false; | |
} | |
male = 0; | |
female = 0; | |
Arrays.fill(genderAge[0], 0); | |
Arrays.fill(genderAge[1], 0); | |
for (int i = 0; i < AliveMicrobes.size(); i++) { | |
Microbe h = Microbes.get(i); | |
h.update(); | |
h.display(); | |
genderAge[(h.gender?0:1)][h.getAgeGroup()]++; | |
male += (h.gender?0:1); | |
female += (h.gender?1:0); | |
} | |
drawGUI(); | |
} | |
void drawGUI() { | |
textSize(11); | |
fill(255, 255, 255); | |
text("Most Kills: "+maxKillCount, 10, 13); | |
text("Population: "+microbeAliveCount, 10, 24); | |
timeElapsed += day; | |
text("Years: "+timeElapsed, 10, 35); | |
text("Highest Generation: "+maxGen, 10, 46); | |
fill(255, 255, 255); | |
text("Male: "+male + " Female: " + female, 10, 57); | |
int offset_genderbars = 150; | |
textSize(8); | |
text("0-9: ", width-offset_genderbars, 11); | |
text("10-18: ", width-offset_genderbars, 23); | |
text("19-36: ", width-offset_genderbars, 36); | |
text("37-54: ", width-offset_genderbars, 48); | |
text("55-72: ", width-offset_genderbars, 60); | |
text("73+: ", width-offset_genderbars, 72); | |
noStroke(); | |
fill(255, 255, 255, 80); | |
rect((float)((width/2.0)-75.0), 0, 150.0, 50); | |
fill(255, 255, 255, 255); | |
text(maxTrend+"", ((width/2.0)-75.0)-20, 8); | |
text(minTrend+"", ((width/2.0)-75.0)-15, 50); | |
//first point | |
float xOff = 150.0/9.0; | |
float xx1 = (width/2.0)-75.0; | |
float yy1 = (float)(50-((popTrend[0]-minTrend)/(maxTrend-minTrend))*50); | |
float xx2 = xx1+xOff; | |
float yy2 = (float)(50-((popTrend[1]-minTrend)/(maxTrend-minTrend))*50); | |
float xx3 = xx2+xOff; | |
float yy3 = (float)(50-((popTrend[2]-minTrend)/(maxTrend-minTrend))*50); | |
float xx4 = xx3+xOff; | |
float yy4 = (float)(50-((popTrend[3]-minTrend)/(maxTrend-minTrend))*50); | |
float xx5 = xx4+xOff; | |
float yy5 = (float)(50-((popTrend[4]-minTrend)/(maxTrend-minTrend))*50); | |
float xx6 = xx5+xOff; | |
float yy6 = (float)(50-((popTrend[5]-minTrend)/(maxTrend-minTrend))*50); | |
float xx7 = xx6+xOff; | |
float yy7 = (float)(50-((popTrend[6]-minTrend)/(maxTrend-minTrend))*50); | |
float xx8 = xx7+xOff; | |
float yy8 = (float)(50.0-((popTrend[7]-minTrend)/(maxTrend-minTrend))*50); | |
float xx9 = xx8+xOff; | |
float yy9 = (float)(50.0-((popTrend[8]-minTrend)/(maxTrend-minTrend))*50); | |
float xx10 = xx9+xOff; | |
float yy10 = (float)(50.0-((popTrend[9]-minTrend)/(maxTrend-minTrend))*50); | |
stroke(255); | |
line(xx1, yy1, xx2, yy2); | |
line(xx2, yy2, xx3, yy3); | |
line(xx3, yy3, xx4, yy4); | |
line(xx4, yy4, xx5, yy5); | |
line(xx5, yy5, xx6, yy6); | |
line(xx6, yy6, xx7, yy7); | |
line(xx7, yy7, xx8, yy8); | |
line(xx8, yy8, xx9, yy9); | |
line(xx9, yy9, xx10, yy10); | |
stroke(255/2); | |
line(xx1, (float)(50-((lr.getValue(1)-minTrend)/(maxTrend-minTrend))*50), xx10, (float)(50-((lr.getValue(10)-minTrend)/(maxTrend-minTrend))*50)); | |
noStroke(); | |
fill(255, 105, 180, 255*.75); | |
float barScale = (-50.0/(microbeAliveCount/2.0)); | |
float barScale2 = (50.0/(microbeAliveCount/2.0)); | |
rect(width-65, 10-3.5, barScale*((float)genderAge[0][0]), 5); | |
rect(width-65, 22-3.5, barScale*((float)genderAge[0][1]), 5); | |
rect(width-65, 34-3.5, barScale*((float)genderAge[0][2]), 5); | |
rect(width-65, 46-3.5, barScale*((float)genderAge[0][3]), 5); | |
rect(width-65, 58-3.5, barScale*((float)genderAge[0][4]), 5); | |
rect(width-65, 70-3.5, barScale*((float)genderAge[0][5]), 5); | |
// boy | |
fill(0, 191, 255, 255*.75); | |
rect(width-63, 10-3.5, barScale2*(genderAge[1][0]), 5); | |
rect(width-63, 22-3.5, barScale2*(genderAge[1][1]), 5); | |
rect(width-63, 34-3.5, barScale2*(genderAge[1][2]), 5); | |
rect(width-63, 46-3.5, barScale2*(genderAge[1][3]), 5); | |
rect(width-63, 58-3.5, barScale2*(genderAge[1][4]), 5); | |
rect(width-63, 70-3.5, barScale2*(genderAge[1][5]), 5); | |
stroke(255); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LinearRegression { | |
int MAXN = 10; | |
int n = 0; | |
double sumx = 0.0, sumy = 0.0, sumx2 = 0.0; | |
double beta1 = 0.0; | |
double beta0 = 0.0; | |
double xbar = 0; | |
double ybar = 0; | |
double xxbar = 0.0, yybar = 0.0, xybar = 0.0; | |
double[] x = {1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0}; | |
double[] y = new double[10]; | |
LinearRegression(double[] y1) { | |
// load into this array format | |
this.y = y1; | |
for (n = 0; n<MAXN; n++) { | |
sumx += x[n]; | |
sumx2 += x[n] * x[n]; | |
sumy += y[n]; | |
} | |
xbar = sumx / n; | |
ybar = sumy / n; | |
// second pass: compute summary statistics | |
for (int i = 0; i < n; i++) { | |
xxbar += (x[i] - xbar) * (x[i] - xbar); | |
yybar += (y[i] - ybar) * (y[i] - ybar); | |
xybar += (x[i] - xbar) * (y[i] - ybar); | |
} | |
beta1 = xybar / xxbar; | |
beta0 = ybar - beta1 * xbar; | |
} | |
void analyze(){ | |
// analyze results | |
int df = n - 2; | |
double rss = 0.0; // residual sum of squares | |
double ssr = 0.0; // regression sum of squares | |
for (int i = 0; i < n; i++) { | |
double fit = beta1*x[i] + beta0; | |
rss += (fit - y[i]) * (fit - y[i]); | |
ssr += (fit - ybar) * (fit - ybar); | |
} | |
double R2 = ssr / yybar; | |
double svar = rss / df; | |
double svar1 = svar / xxbar; | |
double svar0 = svar/n + xbar*xbar*svar1; | |
println("Trend Down: " + trendingDown()); | |
println("Trend up: " + trendingUp()); | |
println("R^2 = " + R2); | |
println("std error of beta_1 = " + Math.sqrt(svar1)); | |
println("std error of beta_0 = " + Math.sqrt(svar0)); | |
svar0 = svar * sumx2 / (n * xxbar); | |
println("std error of beta_0 = " + Math.sqrt(svar0)); | |
println("SSTO = " + yybar); | |
println("SSE = " + rss); | |
println("SSR = " + ssr); | |
} | |
double getBeta1() { | |
return beta1; | |
} | |
double getBeta0() { | |
return beta0; | |
} | |
double getValue(double x){ | |
return beta1 * x + beta0; | |
} | |
boolean trendingUp(){ | |
return y[9] < getValue(11.0); | |
} | |
boolean trendingDown(){ | |
return y[9] > getValue(11.0); | |
} | |
String toString() { | |
return "y = " + beta1 + " * x + " + beta0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Iterator; | |
int microbeCount = 0; | |
//int microbeAliveCount = 0; | |
int maxGen = 0; | |
int maxKillCount = -1; | |
class Microbe extends Mover { | |
//float day = 0.00273972602; | |
//population[i][1] = population[i][1]+(day*7); // add about a day to age | |
float[] dna = new float[DNACODE.values().length]; | |
boolean gender; | |
float age = 18; | |
float speed = 10; | |
float heading = 0; | |
boolean alive; | |
int id =0; | |
int lastBaby = 0; | |
int strengthMod = 1; | |
float aggression; | |
float guilt = 0; | |
float babyCount = 0; | |
int killCount = 0; | |
ArrayList<Microbe> removelist = new ArrayList<Microbe>(); | |
int generation = 0; | |
float babyThreshold = 16; | |
float oldAgeThreshold = 85; | |
DNA DNA; | |
Microbe() { | |
this(new PVector(random(width), random(height)), 1, 2, random(0, 360), random(1000)); | |
} | |
Microbe(PVector loc, float age, float speed, float heading, float agg) { | |
this(loc, age, heading, (new DNA()).set(DNACODE.SPEED, speed).set(DNACODE.AGGRESSION, agg).set(DNACODE.GUILT, random(-1, 1))); | |
} | |
Microbe(Microbe m1, Microbe m2) { | |
this(m1.location.copy(), 1, m1.location.copy().heading(), new DNA(m1.DNA, m2.DNA)); | |
// new Microbe(new PVector(mom.location.x, mom.location.y), 1, (h2.getSpeed()+this.getSpeed())/2+random(-.5, 1), random(360), (h2.aggression+this.aggression)/2+random(-50, 150)); | |
} | |
Microbe(PVector loc, float age, float heading, DNA dna) { | |
super(loc, 3); | |
this.DNA = dna; | |
this.aggression = dna.get(DNACODE.AGGRESSION); | |
this.speed = dna.get(DNACODE.SPEED); | |
this.guilt = dna.get(DNACODE.GUILT); | |
this.heading = heading; | |
this.gender = (random(50) >= 25? true : false); | |
this.age = age; | |
this.alive = true; | |
this.id = microbeCount++; | |
this.lastBaby = -1; | |
this.strengthMod = 1; | |
microbeAliveCount++; | |
} | |
boolean isAdult() { | |
return age>=18; | |
} | |
float getSpeed() { | |
float mod =1; | |
if (!isAdult()) { | |
mod = 1.4; | |
} else if (age>=60) { | |
mod = 0.3; | |
} | |
return (speed*mod); | |
} | |
float getRadius() { | |
return this.age/1.5; | |
} | |
int getAgeGroup() { | |
int group = 0; | |
//0-9 | |
//10-18 | |
if (this.age > 9) { | |
group = 1; | |
} | |
//19-36 | |
if (this.age > 18) { | |
group = 2; | |
} | |
//37-54 | |
if (this.age > 36) { | |
group = 3; | |
} | |
//55-72 | |
if (this.age > 54) { | |
group = 4; | |
} | |
//73+ | |
if (this.age > 72) { | |
group = 5; | |
} | |
return group; | |
} | |
float getStrength() { | |
float mod =1; | |
if (!isAdult()) { | |
mod = 1.4; | |
} else if (age>=60) { | |
mod = 0.3; | |
} else if (age < 25) { | |
mod = 1.7; // prime of life | |
} | |
return (float)Math.pow((age*mod)-(speed/2), 2)*strengthMod; | |
} | |
boolean checkCollision(Microbe h) { | |
float dist = (float)Math.sqrt(Math.pow(this.location.x-h.location.x, 2)+Math.pow(this.location.y-h.location.y, 2)); | |
return dist<(this.getRadius()+h.getRadius()); | |
} | |
void breed(Microbe h2) { | |
Microbe mom = h2; | |
if (this.gender) { | |
mom = this; | |
} | |
if (mom.lastBaby == -1 || millis()-mom.lastBaby>((babyThreshold*1000))) { | |
mom.babyCount++; | |
h2.babyCount++; | |
mom.lastBaby = millis(); | |
Microbe t = new Microbe(this, h2); | |
t.generation = (mom.generation>h2.generation?mom.generation:h2.generation)+1; | |
if (t.generation >= maxGen) { | |
maxGen = t.generation; | |
} | |
System.out.println("Mom: "+mom + " child: " + t); | |
Microbes.add(t); | |
AliveMicrobes.add(t); | |
} | |
} | |
void interact(Microbe h2) { | |
if (h2.isAdult() && this.isAdult()) { | |
if (h2.gender ^ this.gender) { | |
// not same gender && adult, breed | |
this.breed(h2); | |
if (this.guilt <= -0.85) { | |
this.attack(h2); | |
} | |
} else { | |
// same gender adult | |
if (this.guilt <= -.5 || this.aggression > 750 || (this.aggression > 400 && h2.aggression > 400)) {//750 dont matter, ima kill you, either less than 400 leave eachother alone | |
this.attack(h2); | |
} | |
} | |
} | |
if (this.guilt <= -.87) { | |
this.attack(h2); | |
} | |
} | |
void attack(Microbe m2) { | |
if (m2.getStrength()>=this.getStrength()) { | |
m2.speed += .3; | |
m2.strengthMod += .1; | |
m2.killCount++; | |
if (m2.killCount > maxKillCount) { | |
maxKillCount = m2.killCount; | |
} | |
println("Aggressor: "+m2+" killed " + this); | |
die(); | |
} | |
} | |
void die() { | |
microbeAliveCount--; | |
if (maxKillCount <= this.killCount) { | |
maxKillCount = 1; | |
} | |
this.alive = false; | |
removelist.add(this); | |
} | |
void checkBounds() { | |
this.location.x = this.location.x % width; | |
this.location.y = this.location.y % height; | |
if (this.location.y < 0) { | |
this.location.y = height; | |
} | |
if (this.location.x <= 0) { | |
this.location.x = width; | |
} | |
} | |
void move() { | |
move_vector(); | |
} | |
void move_xy() { | |
if (random(100)<3) { | |
this.heading = (this.heading + random(-6, 12))%360; | |
} | |
float vx = getSpeed() * cos(radians(heading)); | |
float vy = getSpeed() * sin(radians(heading)); | |
this.location.x += vx; | |
this.location.y += vy; | |
} | |
void move_vector() { | |
PVector adj = new PVector(0, 0); | |
if (random(100)<3) { | |
//this.heading = (this.heading + random(-6, 12))%360; | |
adj.add(PVector.fromAngle(random(-6, 12))); | |
} | |
adj.setMag(getSpeed()); | |
//float vx = getSpeed() * cos(radians(heading)); | |
//float vy = getSpeed() * sin(radians(heading)); | |
//this.location.x += vx; | |
//this.location.y += vy; | |
applyForce(adj); | |
heading = location.heading(); | |
} | |
//void move_hunt(){ | |
// | |
//} | |
void checkHits() { | |
for (Iterator<Microbe> it = AliveMicrobes.iterator(); it.hasNext(); ) { | |
Microbe h2 = it.next(); | |
if (h2.id!=this.id && h2.alive && this.checkCollision(h2)) { | |
this.interact(h2); | |
} | |
} | |
} | |
void age() { | |
this.age += (day); // add about a day to age | |
if (this.age >= oldAgeThreshold) { | |
if (random(100)<=(20/(killCount/1.5))) { | |
die(); | |
} | |
} | |
} | |
void update() { | |
super.update(); | |
super.velocity.limit(this.DNA.get(DNACODE.SPEED)); | |
if (alive) { | |
this.checkHits(); | |
this.move(); | |
this.checkBounds(); | |
this.age(); | |
} | |
Microbes.removeAll(removelist); | |
AliveMicrobes.removeAll(removelist); | |
} | |
void display_setFill() { | |
float alpha = 255; | |
if (age >= oldAgeThreshold) { | |
alpha = 255-((age-oldAgeThreshold)*40); | |
} else if (age >= 60) { | |
alpha = 220; | |
} | |
if (this.gender) { | |
//girl | |
fill(255, 105, 180, alpha); | |
} else { | |
fill(0, 191, 255, alpha); | |
} | |
if (!this.isAdult()) { | |
fill(255, 255, 255);// white childs | |
} | |
} | |
void display() { | |
if (alive) { | |
this.display_setFill(); | |
pushMatrix(); | |
translate(this.location.x, this.location.y); | |
rotate(super.velocity.heading()-1.5708); | |
float x1 =0; | |
float y1 =0; | |
float radius = getRadius(); | |
float cx = x1; | |
float cy = (float) (y1+((Math.sqrt(3)/3)*radius)); | |
float bx = x1-(radius/2); | |
float by = (float)(y1-((Math.sqrt(3)/6)*radius)); | |
float ax = x1+(radius/2); | |
float ay = (float)(y1-((Math.sqrt(3)/6)*radius)); | |
triangle(cx, cy, bx, by, ax, ay); | |
ellipse(x1, y1, 3, 3); | |
ellipse(cx, cy, 3, 3); | |
if (maxKillCount <= this.killCount) { | |
fill(224, 193, 1, 255/2); | |
ellipse(x1, y1, radius, radius); | |
} | |
popMatrix(); | |
} | |
} | |
String toString() { | |
return "#"+this.id+ " a "+ this.age +" yrold " + (this.gender?"female":"male") + " with a speed of " + this.getSpeed() + " and strength of " + this.getStrength() + " agg: " + this.aggression; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Mover { | |
PVector location, acceleration, velocity; | |
float mass; | |
Mover(PVector pos, float m) { | |
mass = m; | |
location = pos; | |
velocity = new PVector(0, 0); | |
acceleration = new PVector(0, 0); | |
} | |
void applyForce(PVector force) { | |
PVector f = PVector.div(force, mass); | |
acceleration.add(f); | |
} | |
boolean inBounds(){ | |
return (location.x < width+mass && location.x > -mass && location.y < height+mass && location.y > -mass); | |
} | |
void update() { | |
velocity.add(acceleration); | |
location.add(velocity); | |
acceleration.mult(0); | |
} | |
float distance(Mover m){ | |
return dist(m.location.x,m.location.y,this.location.x,this.location.y); | |
} | |
void display() { | |
fill(color(200,0,0),127); | |
ellipse(location.x,location.y,mass*1.6,mass*1.6); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment