/* Our Creature class describes each of our boids running around the screen, and is a composite of our Particle and Boid classes. Each of our Creatures is guided by its DNA, and the fitness of each Creature is a combination of what order it reached the target in (1st, 2nd, 3rd, etc) and how close it got to the target. */ public class Creature { // Our vectors for controlling movement Vector3D loc; Vector3D vel; Vector3D accel; // Size of our Creature float r; // Keep track of the fitness score once we calculate it float fitness; // Our DNA, guides our movement DNA genes; // Active is true if we are moving, false if we've hit a barrier or wall boolean active; // What order did we finish in int finishOrder; public Creature(Vector3D loc_, DNA genes_, int finishOrder_) { // setup our movement accel = new Vector3D(0.0, 0.0, 0.0); vel = new Vector3D(0.0, 0.0, 0.0); loc = loc_.copy(); // Set a default size r = 2.0f; // Store our genes genes = genes_; // Our activity state and finish order finishOrder = finishOrder_; active = true; } /* Here we calculate our fitness. We need the following information for our equation: distance -> our distance from the target finishOrder -> what order we finished in The fitness function uses the following formula to weight the fitness properties: fitness(distance, order) = (1.0 / order^1.5) * (1.0 / distance^6) This means that the order will have a much higher impact on our fitness, but the closer we get to the target, the exponentially higher our fitness calculation will be. */ void calcFitness() { // get our distance to the target float dist = Vector3D.distance(loc, target); // if we are within the area of the target, the distance // to the target should be set to 1.0 if (dist < (diam / 2)) { dist = 1.0f; } fitness = (1.0f / pow(finishOrder, 1.5f)) * (1.0f / pow(dist, 6)); } void setFinishOrder(int f) { finishOrder = f; } /* Our simulate method needs to take into account the possibility of running into obstacles */ void simulate(ArrayList obst) { // only keep simulating if we haven't yet run into anything if (active) { update(); // check for obstacles and walls if ( borders() || obstacles(obst) ) { active = false; } } render(); } /* Check to see if we've hit the edge of the screen */ boolean borders() { if ( (loc.getX() < 0) || (loc.getY() < 0) || (loc.getX() > width) || (loc.getY() > height) ) { return true; } else { return false; } } /* Figure out if we've made it to the target yet */ boolean isFinished() { // get our distance to the target float dist = Vector3D.distance(loc, target); if (dist < diam / 2) { active = false; return true; } else { return false; } } /* See if we've hit an obstacle */ boolean obstacles(ArrayList obst) { // Check each obstacle, and see if we've instersected it for (int i = 0; i < obst.size(); i++) { Obstacle o = (Obstacle)obst.get(i); if (o.contains(loc)) { return true; } } return false; } void update() { // only run if we haven't yet finished if (!isFinished()) { // figure out where we are in our screen grid int gx = (int)loc.getX() / gridscale; int gy = (int)loc.getY() / gridscale; // make sure our numbers are within our grid gx = constrain(gx, 0, width / gridscale - 1); gy = constrain(gy, 0, height / gridscale - 1); // get the steering vector from our DNA accel = accel.add(genes.getGene(gx + gy * width / gridscale)); // do our movement accel = accel.multiply(maxforce); vel = vel.add(accel); vel.limit(maxspeed); loc = loc.add(vel); accel.setXYZ(0.0, 0.0, 0.0); } } void render() { // draw our Creature triangle on screen float theta = vel.heading2D() + radians(90); // set our fill based upon our status if (active) { fill(255); } else { fill(255, 25); } noStroke(); // Move into position and draw pushMatrix(); translate(loc.getX(), loc.getY()); rotate(theta); beginShape(TRIANGLES); vertex(0, -r * 2); vertex(-r, r * 2); vertex(r, r * 2); endShape(); popMatrix(); } float getFitness() { return fitness; } DNA getGenes() { return genes; } boolean isActive() { return active; } }