Skip to content

Instantly share code, notes, and snippets.

@PatrickMurphy
Created September 25, 2017 19:52
Show Gist options
  • Save PatrickMurphy/6272daa7d47ee56bde1cdd542d6b634c to your computer and use it in GitHub Desktop.
Save PatrickMurphy/6272daa7d47ee56bde1cdd542d6b634c to your computer and use it in GitHub Desktop.
Microbes
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;
}
}
enum DNACODE {
AGGRESSION(0), GUILT(1), SPEED(2);
private int id;
private DNACODE(int id) {
this.id = id;
}
}
//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);
}
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;
}
}
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;
}
}
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