/* BLUBBS v. 0.9 BETA * AUTHOR: Jim George * DATE: 4/18/2006 * * This processing applet is my first attempt to create a Cellular Automota (virtual life-form). * The life forms, called Blubbs, are each represented by an every growing and fading colored ellipse. A Blubb contains * 'genetic material', which is stored as its color value. The genes' value is used as the Blubb's display color, and * its inverse as its outline. As the Blubb ages, it gets larger and moves faster (a random walk), until it encounters * another Blubb. When two Blubb's encounter they examine each other's genetic material to find if the other would be * a suitable mate. If so, they move towards each other. Otherwise, they repel. Blubbs' attractivness to one another * is based on an overall difference in color values. The formula is as follows: * Attractivness(a,b = | a's red - b's red | + | a's green - b's green | + | a's blue - b's blue | * The current threshold is Attractivness > 200 attracts. If two Blubbs center's collide, they mate by producing four offspring * containing random compositions of the parents' genes, the two parents die after mating. In this way, genetic diversity is * rewarded by multiplicity, and genetic similarity is discouraged. Also, for each of the offspring's color values, there * is a .5% chance that it will mutate to a random color. This may or may not benifit the organism, depending on the * potential mates it encounters, but allows for randomness in the system which leads to genetic diversity. There are also a * limited number of Blubb souls, and if two Blubb's mate but there are no souls left to give, the offspring are not * introduced to the system, preventing overpopulation. The world that the Blubb's live in is an initial gradient * containing all shades of gray (and therefor the potential for 2^24 or 16 million different Blubbs. Initially, 25 Blubbs are * spawned by incarnating 25 random pixels into Blubb's with the pixel color as its genetic material and initial location of the pixel. * If all Blubb's die, the simulation is reset. */ //constants final int MAX_SOULS = 75; //maximum number of living blubbs at any given time final int INITIAL_BLUBBS = 25; //initial blubbs spawned from start image final int DIVERSITY_THESHOLD = 200; //mate selectivitiy value. If the differene between 2 blubbs is less than this number they will not mate final int SPAWN_DISTANCE = 75; //distance from the point of convergance of two mating blubbs that their offspring are spawned final int MAX_AGE = 100; //how old blubbs get befor they kick the blucket final double MUTATION = .005; //chance for mutation //fields ArrayList newBorns; //arraylist containing newly born blubbs LinkedList blubbs; //data structure containing all blubs int souls; //free souls to be used PImage map; //starting image //set up the map void setup() { blubbs = new LinkedList(); newBorns = new ArrayList(); size(480,360); frameRate(30); //draw ellipses from the center point out ellipseMode(CENTER); //turn on anti-aliasing smooth(); //load the map map = loadImage("map.png"); } //draw method void draw(){ //if all blubbs are dead, spawn new ones if(blubbs.size() == 0){ place(); } //get all the blubbs //for each, draw a representation of it and call its move method ListIterator it = blubbs.listIterator(0); Blubb b; while(it.hasNext()){ b = (Blubb)it.next(); b.move(); //we draw a stroke with the exact inverse of our blubb's color. Used for visual appeal. strokeWeight(1.5); stroke(color(255 - red(b.genes),255 - green(b.genes),255-blue(b.genes),alpha(b.genes))); fill(b.genes); ellipse(b.x, b.y, b.age, b.age); } //iterate through the blubbs again, pruning the dead ones. it = blubbs.listIterator(0); while(it.hasNext()){ if( ((Blubb)it.next()).dead ){ it.remove(); souls++; } } //add this cycle's newborns to the world. for(int i = 0; i < newBorns.size(); i++){ if(souls > 0){ mutate((Blubb)(newBorns.get(i))); it.add(newBorns.get(i)); souls--; } } //delete the newborns. newBorns.clear(); } //this method has a .5% chance of randomly altering any given blubb's color values public void mutate(Blubb b){ b.genes = color(Math.random() > .005 ? red(b.genes) : (int)(Math.random()*256), Math.random() > .005 ? green(b.genes) : (int)(Math.random()*256), Math.random() > .005 ? blue(b.genes) : (int)(Math.random()*256)); } //spawn new blubbs in the map called only on an empty blubb list public void place(){ noStroke(); image(map,0,0); loadPixels(); souls = MAX_SOULS - INITIAL_BLUBBS; for(int i = 0; i < INITIAL_BLUBBS; i++) { int x = (int)(Math.random()*width); int y = (int)(Math.random()*height); blubbs.add(new Blubb(x,y, pixels[y*width+x])); } } //the blubb class public class Blubb{ int x; //center x position int y; //center y position int age; //how many cycles the blubb has been alive. Represented by diameter and alpha value boolean dead; //flag for pruning a dead blubb color genes; //the blubb's genetic material (color information) //our constructor. takes a center x,y coord. and a color for genetic info Blubb(int x, int y, color genes){ this.x = x; this.y = y; this.genes = genes; age = 0; dead = false; } //method to move the blubb. We age one and take a step //somewhere depending on our surroundings public void move(){ //calculate the new alpha value, see if its dead this.genes = color(red(this.genes), green(this.genes), blue(this.genes), MAX_AGE-age); //bail if this guy is too old if(++this.age == MAX_AGE){ this.dead = true; return; } //wrap around if it has moved outside the dimensions if(this.x > width) this.x -= width; if(this.y > height) this.y -= height; if(this.x < 0) this.x = width + x; if(this.y < 0) this.y = height + y; //create a new iterator to find mates. //for each fellow blubb, check to see if it is overlapping, if so //check it's genetic material against our own. If it is above the threshold //we move towards it. If not run away. No inbreeding! ListIterator it = blubbs.listIterator(0); while(it.hasNext()){ Blubb b = (Blubb)it.next(); if(b != this && this.overlaps(b)){ int mult = diversity(b) >= DIVERSITY_THESHOLD ? 1 : -1; //make the move if(this.x != b.x) this.x += (b.x - this.x)/abs(this.x - b.x)*mult; if(this.y != b.y) this.y += (b.y - this.y)/abs(this.y - b.y)*mult; //we mated! Spawn kiddies if(b.x == this.x && b.y == this.y && !this.dead && !b.dead){ //sound.play(); newBorns.add(new Blubb(this.x + SPAWN_DISTANCE, this.y + SPAWN_DISTANCE, mate(b))); newBorns.add(new Blubb(this.x + SPAWN_DISTANCE, this.y - SPAWN_DISTANCE, mate(b))); newBorns.add(new Blubb(this.x - SPAWN_DISTANCE, this.y + SPAWN_DISTANCE, mate(b))); newBorns.add(new Blubb(this.x - SPAWN_DISTANCE, this.y - SPAWN_DISTANCE, mate(b))); //our lives' purposes are over b.dead = true; this.dead = true; } //look no further. return; } } // if we didn't find a mate keep looking this.x += (int)(Math.random()*age-age/2); this.y += (int)(Math.random()*age-age/2); } //method to randomly intermingle our genes with another Blubb's. private color mate(Blubb b){ return color( (Math.random() < .5 ? red(b.genes) : red(this.genes) ), (Math.random() < .5 ? green(b.genes) : green(this.genes) ), (Math.random() < .5 ? blue(b.genes) : blue(this.genes) ) ) ; } //attractivness check. Compares color value's, returning on overall distance. private int diversity(Blubb b){ return (int)((Math.abs(red(b.genes) - red(this.genes))) + (Math.abs(green(b.genes) - green(this.genes))) + (Math.abs(blue(b.genes) - blue(this.genes)))); } //check to see if there is a blubb around us. private boolean overlaps(Blubb b){ int dx = (this.x - b.x); int dy = (this.y - b.y); return sqrt((dx*dx) + (dy*dy)) - (this.age/2 + b.age/2) < 0; } }