• fullscreen
  • Cell.pde
  • Final.pde
  • Genome.pde
  • Network.pde
  • Plant.pde
  • Spore_v0_2.pde
  • sObject.pde
  • import java.awt.Rectangle;
    import java.util.ArrayList;
    
    public class Cell extends sObject {
    	Network brain;
    	float absX, absY;
    	int energy = 0;
    	int life = 0;
    	boolean reproduce = true;
    	boolean eat = false;
    	boolean dormant = false; // NOT IN USE
    	Genome Gnm;
    
    	public Cell(Network n, Genome g, int x, int y, int en) {
    		brain = n;
    		Gnm = g;
    		energy = en;
    		r = new Rectangle(x, y, 10, 10);
    		absX = x;
    		absY = y;
    	}
    
    	public Plant bestPlant(ArrayList<Plant> plants, Plant p) {
    		Plant bestPlant = plants.get(0);
    		// bestPlant = (bestPlant != p) ? bestPlant : plants.get(1);
    		int bDist = distance(bestPlant);
    		for (int i = 1; i < plants.size(); i++) {
    			Plant nPlant = plants.get(i);
    			int nDist = distance(nPlant);
    			if (nDist < bDist && nPlant != p) {
    				bestPlant = nPlant;
    				bDist = nDist;
    			}
    		}
    		return bestPlant;
    	}
    
    	public Cell bestCell(ArrayList<Cell> cell, Cell c) {
    		// There is always another cell (this)
    		Cell bestCell = (Cell) cell.get(0);
    		int bestCellDist = distance(bestCell);
    		for (int i = 1; i < cell.size(); i++) {
    			Cell nCell = cell.get(i);
    			int nCellDist = distance(nCell);
    			if ((nCellDist < bestCellDist && nCell != c && nCellDist != 0)
    					|| bestCellDist == 0) {
    				bestCell = nCell;
    				bestCellDist = nCellDist;
    			}
    		}
    		return bestCell;
    	}
    
    	public void moveCell() {
    		float run = brain.Output[1].value;
    		float rise = brain.Output[0].value;
    		float speed = Math.abs(run + rise); // Output 2
    		absX += run;
    		absY += rise;
    		energy -= speed * PIXLE_MOVE_COST; // COST TO MOVE
    		if (absX > width) {
    			absX = 0;
    			energy -= R_EN;
    		} else if (absX < 0) {
    			absX = width;
    			energy -= R_EN;
    		}
    		if (absY > height) {
    			absY = 0;
    			energy -= R_EN;
    		} else if (absY < 0) {
    			absY = height;
    			energy -= R_EN;
    		}
    		r.setLocation((int) absX, (int) absY); // MOVE THE CELL
    	}
    
    	public Cell reproduce(Cell other, int energy) {
    		return new Cell(brain.crossMutate(other.brain)[round(random(1))], Gnm.crossMutate(other.Gnm), 
                         (int) (r.getX() + random(-15, 15)), (int) (r.getY() + random(-15, 15)), energy);
    	}
    
    	public boolean die(ArrayList<Cell> cell, ArrayList<Plant> plant) {
    		if (energy <= 0 && (life > Gnm.maxAge || !AGE_LIMIT)) { // LIFE
    			cell.remove(this);
    			if (energy > 0)
    				plant.add(new Plant((int) getX(), (int) getY(), energy, energy / SIZE_CAL + 2));
    			return true;
    		}
    		return false;
    	}
    
    	public void update(ArrayList<Cell> cells, ArrayList<Plant> plants) {
    		life++;
    
    		// GET CLOSEST PLANT
    		Plant bestPlant = bestPlant(plants, null);
    		// Plant nextBestPlant = bestPlant(plant, bestPlant);
    		Cell bestCell = bestCell(cells, this);
    		energy = constrain(energy, -10, MAXENERGY);
    		r.setSize(energy / SIZE_CAL + 2, energy / SIZE_CAL + 2);
    
    		brain.Input[0].value = (bestPlant.getX() - getX()) / width;
    		brain.Input[1].value = (bestPlant.getY() - getY()) / height;
    		brain.Input[2].value = (bestCell.getX() - getX()) / width;
    		brain.Input[3].value = (bestCell.getY() - getY()) / height;
    		brain.Input[4].value = (bestCell.energy) / MAXENERGY;
    		/*
    		 * brain.Input[2].value = getX()/width; brain.Input[3].value =
    		 * getY()/height;
    		 */
    		brain.Input[5].value = energy / MAXENERGY;
    		brain.Input[6].value = life / MAX_AGE_FROMBINARY;
    		//brain.Input[8].value = (bestCell.Gnm.gHue/256);
    		brain.runNet();
    
    		// This function evals and does moving
    		moveCell();
    		energy -= BASE_METABOLISM; // METABOLISM
    
    		// Update Bool
    		reproduce = (brain.Output[2].value >= 0);
    		eat = (brain.Output[3].value >= 0);
    		if (eat)
    			energy -= EAT_ENERGY; // If trying to eat using energy
    
    		// //// EAT
    		Rectangle or = bestPlant.getRect();
    		if (r.intersects(or) || r.contains(or) || or.contains(r)) {
    			energy += bestPlant.energy;
    			plants.remove(bestPlant);
    			// bestPlant.getRect().setLocation((int)random(width),
    			// (int)random(height));
    			return;
    		}
    
    		// Rep, [-1.0, 0.0] do nothing, [0.0, 0.5] find mate and rep, [0.5, 1.0]
    		// asexual
    
    		// //// REPRODUCE
    		boolean reproduced = false;
    		if (bestCell != this) { // Valid Choice
    			Rectangle cr = bestCell.getRect();
    			boolean intersects = r.intersects(cr) || r.contains(cr) || cr.contains(r);
    			if (this.life > this.Gnm.reporductionAge // If can reproduce
    					&& this.energy > ENERGYTOREPRODUCE && this.reproduce) {
    				if (intersects) { // sextual reproduction
    					if (bestCell.energy > ENERGYTOREPRODUCE
    							&& bestCell.life > bestCell.Gnm.reporductionAge) {
    						int energyToGive = (int) (energy * ENERGY_TOGIVE + bestCell.energy
    								* ENERGY_TOGIVE);
    						cells.add(reproduce(bestCell, energyToGive));
    						energy = (int) (energy * ENERGY_TOGIVE - ENERGYTOREPRODUCE);
    						bestCell.energy = (int) (bestCell.energy
    								* ENERGY_TOGIVE - ENERGYTOREPRODUCE);
    						reproduced = true;
    					}
    				} else if (brain.Output[2].value > 0.5) { // asextual
    					int energyToGive = (int) (energy * ENERGY_TOGIVE);
    					cells.add(reproduce(this, energyToGive));
    					energy = energyToGive - ENERGYTOREPRODUCE;
    				}
    			} 
    			if (this.eat && !reproduced
    					&& intersects && (this.energy > bestCell.energy || !bestCell.eat)) { // If eating and larger than other
    				float rnd = random(0.90f);
    				energy += bestCell.energy * rnd;
    				bestCell.energy -= bestCell.energy * (rnd+0.25);
    				if(bestCell.energy < 0) cells.remove(bestCell);
    			}
    		}
    
    		die(cells, plants);
    	}
    }
    
    static boolean doDraw = true;
    static boolean SELFSUSTAIN = false;
    static boolean AGE_LIMIT = true;
    static Random rand = new Random(System.currentTimeMillis());
        
    static float maxMutChange = 0.07f;
    static float mutationRate = 0.005f;
    static float crossOverRate = 0.5f;
    static float NUT_NEEDED = 0.9f;
    static float NUTRITION = 350;
    static float P_INC = 0.00005f;
    
    static float mod = 2.0f;
    static int MAXENERGY = (int) (2500 * mod);
    static int MAX_PLANT = (int) (1000 * mod);
    static int PLNENERGY = (int) (300 * mod);
    static int R_EN = (int) (700 * mod);
    static int START_ENERGY = (int) (1500 * mod);
    static int ENERGYTOREPRODUCE = (int) (600 * mod);
    // Unaffected by mod
    static int BASE_METABOLISM = 2;
    static int PIXLE_MOVE_COST = 3;
    static int EAT_ENERGY = 0;
    
    static int MAXLIFE = 1800;
    static int MAX_AGE_FROMBINARY = 2050;
    static int SIZE_CAL = MAXENERGY / 10;
    static float ENERGY_TOGIVE = 0.5f;
    static int REP_AGE = 50;
    
    
    public class Genome {
    	public int[] a = new int[34];
    	int maxAge;
    	int reporductionAge;
    	int layers; // Not doing anything
    	int nodesPerLayer; // Not doing anything
    	int gHue;
    
    	public Genome() {
    		for (int i = 0; i < a.length; i++) {
    			a[i] = PApplet.round(random(1));
    		}
    		eval();
    	}
    
    	public Genome crossMutate(Genome other) {
    		// Init the new genome
    		Genome newG = new Genome();
    		newG.a = this.a.clone();
    		for (int i = 0; i < a.length; i++) {
    			if (random(1) < crossOverRate) // Crossover
    				newG.a[i] = other.a[i];
    			else if (random(1) < mutationRate) // Mutate
    				newG.a[i] = (newG.a[i] == 1) ? 0 : 1;
    		}
    		newG.eval();
    		return newG;
    	}
    
    	public void eval() {
    		String s;
    		int i = 0, li;
    
    		s = "";
    		for (li = i; i < li + 11; i++)
    			s = s + a[i];
    		maxAge = Integer.parseInt(s, 2);
    
    		s = "";
    		for (li = i; i < li + 8; i++)
    			s = s + a[i];
    		reporductionAge = Integer.parseInt(s, 2);
    
    		s = "";
    		for (li = i; i < li + 3; i++)
    			s = s + a[i];
    		layers = Integer.parseInt(s, 2);
    
    		s = "";
    		for (li = i; i < li + 4; i++)
    			s = s + a[i];
    		nodesPerLayer = Integer.parseInt(s, 2);
    
    		s = "";
    		for (li = i; i < li + 8; i++)
    			s = s + a[i];
    		gHue = Integer.parseInt(s, 2);
    	}
    }
    
    
    public class Network implements Comparable<Object> {
    	public Node[] Input, Output;
    	public Node[][] Hidden;
    	public int fitness;
    
    	public Network(int in, int hdnLrs, int hi, int ou) {
    		Input = new Node[in + 1]; // also add bias
    		Hidden = new Node[hdnLrs][hi + 1];
    		if (hi == 0)
    			Hidden = new Node[0][0]; // If the hidden layers are empty, clear
    		Output = new Node[ou];
    		fitness = 0;
    
    		for (int i = 0; i < Input.length; i++)
    			Input[i] = new Node(); // for each including bias (no diff from
    									// other)
    		for (int i = 0; i < Hidden.length; i++)
    			for (int iii = 0; iii < Hidden[i].length; iii++) {
    				if (iii != Hidden[i].length - 1)
    					Hidden[i][iii] = new Node(
    							randomValues((i > 0) ? Hidden[i - 1].length
    									: Input.length)); // for each but bias
    				else
    					Hidden[i][iii] = new Node(); // Set Hidden bias
    			}
    		for (int i = 0; i < Output.length; i++)
    			Output[i] = new Node(
    					randomValues((Hidden.length > 0) ? Hidden[Hidden.length - 1].length
    							: Input.length));
    	}
    
    	public String toString() {
    		// PLACE HOLDER
    		return "";
    	}
    
    	float[] randomValues(int x) {
    		float[] result = new float[x];
    		for (int i = 0; i < x; i++) {
    			result[i] = random(-1, 1);
    		}
    		return result;
    	}
    
    	public void runNet() {
    		for (int i = 0; i < Hidden.length; i++)
    			for (int j = 0; j < Hidden[i].length; j++)
    				Hidden[i][j].eval((i > 0) ? Hidden[i - 1] : Input);
    
    		for (int i = 0; i < Output.length; i++) {
    			if (Hidden.length > 0)
    				Output[i].eval(Hidden[Hidden.length - 1]);
    			else
    				Output[i].eval(Input);
    		}
    	}
    
    	public void evalFitness(int f) {
    		fitness += f;
    	}
    
    	public void evalFitness() {
    	}
    
    	public int compareTo(Object o) throws ClassCastException {
    		if (!(o instanceof Network)) {
    			throw new ClassCastException("A Network object expected.");
    		}
    		Network n2 = (Network) o;
    		return n2.fitness - this.fitness;
    	}
    
    	public Network[] crossMutate(Network other) {
    		Network childA = this.clone();
    		Network childB = other.clone();
    
    		// Cross this with other (Hidden Layer)
    		for (int i = 0; i < childA.Hidden.length && i < other.Hidden.length; i++)
    			// Each layer
    			for (int j = 0; j < childA.Hidden[i].length
    					&& j < other.Hidden[i].length; j++)
    				// Each node
    				for (int iii = 0; iii < other.Hidden[i][j].weights.length; iii++)
    					// Each weight
    					if (random(1) < crossOverRate)
    						childA.Hidden[i][j].weights[iii] = mutate(other.Hidden[i][j].weights[iii]);
    
    		// Cross other with this (Hidden Layer)
    		for (int i = 0; i < childB.Hidden.length && i < this.Hidden.length; i++)
    			// Each layer
    			for (int j = 0; j < childB.Hidden[i].length
    					&& j < this.Hidden[i].length; j++)
    				// Each node
    				for (int iii = 0; iii < this.Hidden[i][j].weights.length; iii++)
    					// Each weight
    					if (random(1) < crossOverRate)
    						childB.Hidden[i][j].weights[iii] = mutate(this.Hidden[i][j].weights[iii]);
    
    		// Cross this with other (Output Layer)
    		for (int i = 0; i < childA.Output.length && i < other.Output.length; i++)
    			for (int iii = 0; iii < other.Output[i].weights.length; iii++)
    				if (random(1) < crossOverRate)
    					childA.Output[i].weights[iii] = mutate(other.Output[i].weights[iii]);
    
    		// Cross other with this (Output Layer)
    		for (int i = 0; i < childB.Output.length && i < this.Output.length; i++)
    			for (int iii = 0; iii < this.Output[i].weights.length; iii++)
    				if (random(1) < crossOverRate)
    					childB.Output[i].weights[iii] = mutate(this.Output[i].weights[iii]);
    
    		return new Network[] { childA, childB };
    	}
    
    	private float mutate(float val) {
    		if (random(1) < mutationRate)
    			return constrain(val
    					+ (random(-1, 1) * maxMutChange), -1, 1);
    		return val;
    	}
    
    	public Network clone() {
    		int rslt = 0;
    		for (Node[] nAry : Hidden)
    			rslt += nAry.length - 1; // average each, remove extra bias
    		rslt /= Hidden.length;
    		// Input bias
    		Network cloneNet = new Network(Input.length - 1, Hidden.length, rslt, Output.length);
    		for (int i = 0; i < cloneNet.Input.length; i++)
    			cloneNet.Input[i] = new Node();
    		for (int i = 0; i < cloneNet.Hidden.length; i++)
    			for (int j = 0; j < cloneNet.Hidden[i].length; j++)
    				cloneNet.Hidden[i][j] = new Node((float[]) Hidden[i][j].weights
    						.clone());
    		for (int i = 0; i < cloneNet.Output.length; i++)
    			cloneNet.Output[i] = new Node((float[]) Output[i].weights.clone());
    		return cloneNet;
    	}
    
    	public class Node {
    		// default is 1 so it is correct if the node is a bias
    		public float value = 1;
    		public float[] weights;
    
    		/*
    		 * bias = the bias is needed to be able to solve problems that is not
    		 * linearly seperable
    		 */
    
    		// Initialize a bias or an (on)input
    		public Node() {
    			this(new float[0]);
    		}
    
    		// Initialize with weights
    		public Node(float[] w) {
    			weights = w;
    		}
    
    		// If an input or a bias (isn't connected to any other nodes) you can
    		// change the value
    		public void setValue(float v) {
    			if (weights.length == 0)
    				value = v;
    		}
    
    		// Sum the value of the connected Node * it's weight, then run it
    		// through the activation function (sigmoid)
    		public void eval(Node[] ns) {
    			if (weights.length == 0)
    				return;
    			float total = 0;
    			for (int i = 0; i < weights.length; i++)
    				total += ns[i].value * weights[i];
    			value = arctan(total);
    		}
    
    		float arctan(float x) {
    			return (float) ((2 / Math.PI) * Math.atan(Math.PI / 2 * x));
    		}
    
    		float sigmoid(float a) {
    			return (float) (1 / (1 + Math.pow(Math.E, -a)));
    		}
    	}
    }
    
    
    import java.awt.Rectangle;
    import java.util.ArrayList;
    
    public class Plant extends sObject {
    	public int energy = PLNENERGY;
    	int age = 0;
    	int maxAge = (int) random(MAX_PLANT * 0.5f, MAX_PLANT * 1.5f);
    
    	public Plant(int x, int y) {
    		r = new Rectangle(x, y, 10, 10);
    	}
    
    	public Plant(int x, int y, int e, int s) {
    		this(x, y);
    		energy = constrain(e, -PLNENERGY, PLNENERGY);
    		r.setSize(s, s);
    	}
    
    	public Plant(int x, int y, boolean well) {
    		this(x, y);
    		if (well)
    			maxAge = Integer.MAX_VALUE;
    	}
    
    	public void update(ArrayList<Plant> plants) {
    		age++;
    		if (age >= maxAge || energy <= 0) {
    			plants.remove(this);
    			return;
    		}
    		if (random(1) < plant_rate) {
    			double newX = r.getX() + random((float) -r.getWidth() * 10,(float) r.getWidth() * 10);
    			double newY = r.getY() + random((float) -r.getWidth() * 10, (float) r.getWidth() * 10);
    			if(newX < 0 || newX > width || newY < 0 || newY > height)
    				return;
    			Plant p = new Plant((int) newX, (int) newY, energy, (int) r.getWidth());
    			plants.add(p);
    			plant_rate = NUTRITION - (plants.size() * NUT_NEEDED);
    		}
    	}
    }
    
    
    import java.util.ArrayList;
    
    float plant_rate = 0.001f;
    
    ArrayList<Cell> cells;
    /********** With 50 cells there should be about 35 connections for each ******************/
    ArrayList<Plant> plants;
    
    public void setup() {
      size(700, 700);
      rectMode(CENTER);
      colorMode(HSB, 360, 100, 100);
      noStroke();
      background(100);
      reset();
    }
    
    void reset() {
      plants = new ArrayList<Plant>();
      for (int i = 0; i < 300; i++) {
        plants.add(new Plant(round(random(width - 40) + 20), 
        round(random(height - 40) + 20)));
      }
    
      cells = new ArrayList<Cell>();
      for (int i = 0; i < 100; i++) {
        cells.add(new Cell(createNet(), new Genome(), 
        round(random(width - 40) + 20), 
        round(random(height - 40) + 20), START_ENERGY));
      }
    }
    
    Network createNet() {
      return new Network(7, 5, 10, 4);
    }
    
    public void mousePressed() {
      if (mouseButton == LEFT)
        cells.add(new Cell(createNet(), new Genome(), mouseX, mouseY, START_ENERGY));
      else if (mouseButton == RIGHT)
        plants.add(new Plant(mouseX, mouseY));
    }
    
    public void keyPressed() {
      if (key == 'r' && cells.size() >= 2)
        cells.add((Cell) (cells.get(round(random(cells.size() - 1))))
          .reproduce(
        (Cell) cells.get(round(random(cells.size() - 1))), 
        START_ENERGY).setXY((int) random(width), 
        (int) random(height)));
      else if (key == 'n')
        cells.add(new Cell(createNet(), new Genome(), 
        round(random(width - 40) + 20), 
        round(random(height - 40) + 20), START_ENERGY));
      else if (key == 'p')
        plants.add(new Plant((int) random(width), (int) random(height)));
      else if (key == 'e') {
        cells.clear();
        plants.clear();
      } 
      else if (key == 'd')
        doDraw = !doDraw;
      else if (key == 's')
        SELFSUSTAIN = !SELFSUSTAIN;
    }
    
    public void draw() {
      background(0, 0, 40);
      plant_rate = NUTRITION - (plants.size() * NUT_NEEDED);
      if (SELFSUSTAIN) {
        if (cells.size() == 0)
          reset();
        if (random(1) < 0.2 / cells.size() && cells.size() >= 2) {
          Cell c1 = (Cell) cells.get(round(random(cells.size() - 1)));
          Cell c2 = (Cell) cells.get(round(random(cells.size() - 1)));
          cells.add(
          (Cell) c1.reproduce(
          c2, (int) (c1.energy * ENERGY_TOGIVE + c2.energy * ENERGY_TOGIVE)
            ).setXY((int) random(width), (int) random(height)));
        }
        if (random(1) < plant_rate || plants.size() == 0)
          plants.add(new Plant((int) random(width), (int) random(height)));
      }
    
      for (int i = 0; i < plants.size(); i++) {
        Plant p = (Plant) plants.get(i);
        p.update(plants);
        if (doDraw) {
          fill(120, 75, 75);
          rect(p.getX(), p.getY(), p.getWidth(), p.getHeight());
        }
      }
    
      for (int i = 0; i < cells.size(); i++) {
        Cell c = (Cell) cells.get(i);
        c.update(cells, plants);
        if (doDraw) {
          fill(c.Gnm.gHue, 75, 100);
          rect(c.getX(), c.getY(), c.getWidth(), c.getHeight());
        }
      }
    
      /*
    		 * if(frameRate < 50 && millis() > 2000) println("Cells: " +
       		 * cells.size() + " Plant: " + plants.size() + " : " + frameRate);
       		 */
    }
    
    
    import java.awt.Rectangle;
    
    public abstract class sObject {
    	Rectangle r;
    
    	public int distance(sObject other) {
                return PApplet.round(PApplet.dist(this.getX(), this.getY(), other
    	        .getX(), other.getY()));
    	}
    
    	public float getX() {
    		return (float) r.getX();
    	}
    
    	public float getY() {
    		return (float) r.getY();
    	}
    
    	public float getWidth() {
    		return (float) r.getWidth();
    	}
    
    	public float getHeight() {
    		return (float) r.getHeight();
    	}
    
    	public sObject setXY(int x, int y) {
    		r.setLocation(x, y);
    		return this;
    	}
    
    	public Rectangle getRect() {
    		return r;
    	}
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    John E.

    Spore v0.2

    Add to Faves Me Likey@!
    You must login/register to add this sketch to your favorites.

    Watch cells with a Neural Network brain evolve a system for survival

    John E.
    25 Sep 2011
    You need to login/register to comment.