/******************************************************************************* * Copyright (c) 2016 Alex Shapiro - github.com/shpralex * This program and the accompanying materials * are made available under the terms of the The MIT License (MIT) * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. *******************************************************************************/ package com.sproutlife.model.echosystem; import java.awt.Point; import java.util.ArrayList; import java.util.HashSet; import java.util.Random; import java.util.Set; import com.sproutlife.model.GameClock; import com.sproutlife.model.seed.Seed; /** * Organisms are a collection of Cells. They keep track of the location where * they were born, the seed from which they sprouted, their parents and * children. They have a genome which stores their mutations. * * Organisms have a lot going on, and some experimental attributes are placed in * OrgAttributes to keep the code a bit cleaner. * * @author Alex Shapiro */ public class Organism { private int id; private HashSet<Cell> cells; private Organism parent; private ArrayList<Organism> children; private Genome genome; private Seed seed; //The clock lets us know the organism's age, so we can just track when it was born public GameClock clock; // Lifespan isn's age, but a self destruct value above // which the organism is removed public int lifespan; private int born; //Game time when the organism was born. private boolean alive = true; private int timeOfDeath; private Point location; public int x; //duplicate location for shorter code public int y; //duplicate location for shorter code //Keep track of random and experimental attributes in separate class private OrgAttributes attributes; public Organism(int id, GameClock clock, int x, int y, Organism parent, Seed seed) { this.id = id; this.clock = clock; this.born = clock.getTime(); this.parent = parent; this.children = new ArrayList<Organism>(); this.x = x; this.y = y; this.location = new Point(x,y); this.seed = seed; this.genome = new Genome(); this.cells = new HashSet<Cell>(); this.timeOfDeath = -1; this.attributes = new OrgAttributes(this); if (parent!=null) { parent.addChild(this); this.genome = parent.getGenome().clone(); this.lifespan = parent.lifespan; } this.genome.setSeed(this.seed); } public int getId() { return id; } public Point getLocation() { return location; } public Organism getParent() { return parent; } public void setParent(Organism parent) { this.parent = parent; } public ArrayList<Organism> getChildren() { return children; } public void addChild(Organism childOrg) { children.add(childOrg); } public void setSeed(Seed seed) { this.seed = seed; this.genome.setSeed(seed); } public Seed getSeed() { return seed; } public GameClock getClock() { return clock; } public int getLifespan() { return lifespan; } public void setLifespan(int lifespan) { this.lifespan = lifespan; } public int getBorn() { return born; } /* * @return return the time since the organism was born, even if it's dead */ public int getTimeSinceBorn() { return getClock().getTime() - this.born; } /* * @return if the organism is alive, return the time since it was born, * otherwise, return the age at which the organism died. */ public int getAge() { if (isAlive()) { return getTimeSinceBorn(); } else { return this.timeOfDeath - this.born; } } public OrgAttributes getAttributes() { return attributes; } public Genome getGenome() { return genome; } public Mutation addMutation(int x, int y) { return getGenome().addMutation(x, y, getAge(), clock.getTime()); } public ArrayList<Point> getMutationPoints(int time) { ArrayList<Point> adjustOffsets = getGenome().getMutationPoints(time); if (adjustOffsets==null) { return null; } for (Point p : adjustOffsets) { p.x += getLocation().x; p.y += getLocation().y; } return adjustOffsets; } public Set<Cell> getCells() { return cells; } public Cell addCell(int x, int y) { Cell c = new Cell(x, y, this); addCell(c); return c; } /* * Add a cell that's been created * * @param c - cell to add */ public void addCell(Cell c) { cells.add(c); getAttributes().territory.add(c); int dist = (c.x-this.x)*(c.x-this.x)+(c.y-this.y)*(c.y-this.y); if (getAttributes().territoryRadius<dist) { getAttributes().territoryRadius = dist; } this.getAttributes().maxCells = Math.max(size(), getAttributes().maxCells); } /* * Create cell but don't add it; * @param x - the x coordinate of the cell * @param y - the y coordinate of the cell * @param parents - the parents of the cell * @return Create but don't add the cell */ public Cell createCell(int x, int y, ArrayList<Cell> parents) { //Potentially check that parents are same type as organism; Cell c = new Cell(x, y, parents); //cells.add(c); return c; } public boolean containsCell(int x, int y) { for (Cell c: cells) { if (c.x ==x && c.y==y) { return true; } } return false; } public Cell getCell(int x, int y) { for (Cell c: cells) { if (c.x ==x && c.y==y) { return c; } } return null; } public boolean removeCell(Cell c) { return cells.remove(c); } public boolean removeCell(int x, int y) { Cell c = getCell(x,y); if (c!=null) { return removeCell(c); } return false; } /* * @return the number of cells an organism has */ public int size() { return cells.size(); } public boolean removeFromTerritory(Cell c) { boolean result = getAttributes().territory.remove(c); return result; } public void setAlive(boolean alive) { this.alive = alive; } public boolean isAlive() { return alive && !(getTimeOfDeath()>0); } public int getTimeOfDeath() { return timeOfDeath; } public void setTimeOfDeath(int timeOfDeath) { this.timeOfDeath = timeOfDeath; } public boolean equals(Organism t) { // TODO Auto-generated method stub return this.id == t.id; } private HashSet<Organism> getAncestorsAndMe(Organism o, int dist) { HashSet<Organism> ancestors = new HashSet<Organism>(); for (int d=0;d<=dist;d++) { if (o!=null) { ancestors.add(o); o = o.getParent(); } else { break; } } return ancestors; } /* * @param o2 - check if organism #2 is in my family * * @param dist - degrees of separation, TODO: for now it's actually 2x * degrees of separation and should be refactored. * * @return return true if we are related */ public boolean isFamily(Organism o2, int dist) { HashSet<Organism> myAncestors = getAncestorsAndMe(this, dist); HashSet<Organism> otherAncestors = getAncestorsAndMe(o2, dist); for (Organism o :myAncestors) { if (otherAncestors.contains(o)) { return true; } } return false; } }