public class Settings {
    HashMap<String, Object> settings;

    public static String SEED_TYPE = "seedType";
    public static String LIFE_MODE = "competitiveMode";
    public static String MUTATION_ENABLED = "mutationEnabled";
    public static String MUTATION_RATE = "mutationRate";
    public static String MAX_LIFESPAN = "maxLifespan";
    public static String TARGET_LIFESPAN = "targetLifespan";
    public static String MIN_CHILDBEARING_AGE = "minAgeToHaveChildren";
    public static String CHILD_TWO_PARENT_AGE = "childTwoParentAge";
    public static String CHILD_THREE_PARENT_AGE = "childThreeParentAge";
    //Determines whether seeds are sprouted immediately upon detection,
    //Or a step later so seed cells can be displayed before the sprout is.
    public static String SPROUT_DELAYED_MODE = "sproutDelayedMode";
    public static String BACKGROUND_THEME = "backgroundTheme";
    public static String COLOR_MODEL = "colorModel";
    public static String AUTO_SPLIT_COLORS = "autoSplitColors";
    public static String PRIMARY_HUE_DEGREES = "primaryHueDegrees";
    public static String HUE_RANGE = "hueRange";


    public Settings() {
        settings = new HashMap<String, Object>();
        initSettings();
    }

    public void initSettings() {
        set(Settings.SEED_TYPE, SeedType.Bentline1_RPentomino.toString());

        set(Settings.LIFE_MODE, "competitive1");

        set(Settings.MUTATION_ENABLED, true);
        set(Settings.MUTATION_RATE, 5); //0-100

        set(Settings.MAX_LIFESPAN, 75);
        set(Settings.TARGET_LIFESPAN, 37);

        set(Settings.MIN_CHILDBEARING_AGE, 0);

        set(Settings.SPROUT_DELAYED_MODE, false);
        set(Settings.AUTO_SPLIT_COLORS, true);

        set(Settings.COLOR_MODEL, "AngleColorModel");
        set(Settings.BACKGROUND_THEME, "black");
        set(Settings.PRIMARY_HUE_DEGREES, 0);
        set(Settings.HUE_RANGE, 100);
    }

    public void set(String s, Object o) {
        Object previous = settings.get(s);

        // for loading settings from file, cast loaded values based on class of default values
        if (previous!=null && o instanceof String) {
            if (previous instanceof Integer) {
                o = Integer.valueOf((String) o);
            }
            if (previous instanceof Boolean) {
                o = Boolean.valueOf((String) o);
            }
        }

        settings.put(s, o);
    }

    public boolean getBoolean(String s) {
        return (Boolean) settings.get(s);
    }

    public int getInt(String s) {
        return (Integer) settings.get(s);
    }

    public String getString(String s) {
        return (String) settings.get(s);
    }
} public class GameModel {
    private Echosystem echosystem;
    private GameClock clock;
    private GameStep gameStep;
    private GameThread gameThread;
    private Settings settings;
    private Stats stats;

    public GameModel(Settings settings, ReentrantReadWriteLock interactionLock) {
        this.settings = settings;
        this.clock = new GameClock();
        echosystem = new Echosystem(clock);
        gameStep = new GameStep(this);
        gameThread = new GameThread(this, interactionLock);
        stats = new Stats(this);
    }

    public Echosystem getEchosystem() {
        return echosystem;
    }

    public Board getBoard() {
        return echosystem.getBoard();
    }

    public int getTime() {
        return clock.getTime();
    }

    public Stats getStats() {
        return stats;
    }

    public Settings getSettings() {
        return settings;
    }

    public GameThread getGameThread() {
        return gameThread;
    }

    private GameClock getClock() {
        return clock;
    }

    private void incrementTime() {
        clock.increment();
    }

    public void performGameStep() {
        incrementTime();
        gameStep.perform();
    }

    public void resetGame() {
        getEchosystem().resetCells();
        EchosystemUtils.pruneEmptyOrganisms(getEchosystem());
        getEchosystem().clearRetiredOrgs();
        getStats().reset();
        getClock().reset();
    }

    public void setPlayGame(boolean playGame) {
        gameThread.setPlayGame(playGame);
    }

    public boolean isPlaying() {
        return gameThread.isPlaying();
    }

    public void setGameStepListener(GameStepListener l) {
        if (gameThread == null) {
            return;
        }
        gameThread.addGameStepListener(l);
    }
} public class Echosystem {
    List<Organism> organisms;
    Deque<Organism> retiredOrganisms;

    Board board;

    int defaultOrgLifespan;
    int retirementTimeSpan;

    GameClock clock;

    public int sequentialOrganismId = 0;

    public Echosystem(GameClock clock) {
        this.organisms = new ArrayList<Organism>();
        this.retiredOrganisms = new ArrayDeque<Organism>();
        this.board = new Board();
        this.clock = clock;

        this.defaultOrgLifespan = 15;
        this.retirementTimeSpan = 1000;
    }

    public Collection<Organism> getOrganisms() {
        return organisms;
    }

    public Deque<Organism> getRetiredOrganisms() {
        return retiredOrganisms;
    }

    public Board getBoard() {
        return board;
    }

    public GameClock getClock() {
        return clock;
    }

    public int getTime() {
        return clock.getTime();
    }

    /*
     * Convenience method. Maybe slow, and shouldn't be used often. Should be
     * replaced by custom iterator.
     * 
     * @Return all the cells
     */
    public ArrayList<Cell> getCells() {
        ArrayList<Cell> cellList = new ArrayList<Cell>();
        for (Organism o : getOrganisms()) {
            cellList.addAll(o.getCells());
        }

        return cellList;
    }

    public void resetCells() {
        ArrayList<Cell> cells = new ArrayList<Cell>(getCells());
        for (Cell c : cells) {
            removeCell(c);
        }

        this.setDefaultOrgLifespan(15);
    }

    public boolean removeCell(Cell c) {
        return removeCell(c, true);
    }

    public boolean removeCell(Cell c, boolean updateBoard) {
        if (c == null) {
            return false;
        }

        boolean result = c.getOrganism().removeCell(c);

        if (updateBoard) {
            // We don't want to update the board if we just reset it.
            getBoard().removeCell(c);
        }

        return result;
    }

    public Cell addCell(int x, int y, Organism org) {
        Cell c = org.addCell(x, y);
        if (c != null) {
            getBoard().setCell(c);
        }
        return c;
    }

    /*
     * Add a cell that's been created but not yet added
     */
    public void addCell(Cell c) {
        c.getOrganism().addCell(c);
        getBoard().setCell(c);
    }

    /*
     * Create a cell but don't add it
     */
    public Cell createCell(int x, int y, Organism o) {
        return o.createCell(x, y);
    }

    public void removeCell(int x, int y) {
        Cell c = getBoard().getCell(x, y);
        if (c != null) {
            removeCell(c);
        }
    }

    public void setDefaultOrgLifespan(int orgLifespan) {
        this.defaultOrgLifespan = orgLifespan;
    }

    public int getDefaultOrgLifespan() {
        return defaultOrgLifespan;
    }

    public int getRetirementTimeSpan() {
        return retirementTimeSpan;
    }

    public void setRetirementTimeSpan(int retirementTimeSpan) {
        this.retirementTimeSpan = retirementTimeSpan;
    }

    public Organism createOrganism(int x, int y, Organism parent, Seed seed) {
        sequentialOrganismId++;

        Organism newOrg = new Organism(sequentialOrganismId, getClock(), x, y, parent, seed);
        if (parent == null) {
            newOrg.setLifespan(getDefaultOrgLifespan());
        }

        organisms.add(newOrg);
        return newOrg;

    }

    public void removeOrganism(Organism o) {
        for (Cell c : o.getCells()) {
            getBoard().removeCell(c);
        }
        organisms.remove(o);
    }

    /*
     * A "retired" organism is actually dead, maybe the function should be renamed.
     * Retired organisms hang around in a separate list for statistics purposes.
     */
    public void retireOrganism(Organism o) {
        for (Cell c : o.getCells()) {
            getBoard().removeCell(c);
        }
        o.setAlive(false);
        o.setTimeOfDeath(getTime());

        organisms.remove(o);
        retiredOrganisms.add(o);
    }

    public void removeRetired(Organism o) {
        retiredOrganisms.remove(o);
    }

    public void clearRetiredOrgs() {
        getRetiredOrganisms().clear();
    }
} public class Genome {
    
    // map of age -> list of mutations at age
    List<List<Mutation>> genome;

    public Genome() {
        this.genome = new ArrayList<>();
    }

    public Genome(Genome sourceGenome) {
        this.genome = new ArrayList<>();
        // Clone mutations
        for (int age = 0; age < sourceGenome.getAgeRange(); age++) {
            List<Mutation> sourceMutationsAtAge = sourceGenome.getMutationsAtAge(age);
            ArrayList<Mutation> clonedMutationsAtAge = sourceMutationsAtAge != null
                    ? new ArrayList<Mutation>(sourceMutationsAtAge)
                    : null;
            setMutationsAtAge(age, clonedMutationsAtAge);
        }
    }

    public Genome clone() {
        return new Genome(this);
    }

    public int getAgeRange() {
        int i = genome.size() - 1;
        while (i >= 0 && getMutationCountAtAge(i)==0) {
            i--;
        }
        return i + 1;
    }

    public int getMutationCountAtAge(int age) {
        if (age >= genome.size() || genome.get(age) == null) {
            return 0;
        }
        return genome.get(age).size();
    }

    public List<Mutation> getMutationsAtAge(int age) {
        if (age >= genome.size()) {
            return null;
        }
        return genome.get(age);
    }

    public void setMutationsAtAge(int age, List<Mutation> mutationsAtAge) {
        while (genome.size()<=age) {
            genome.add(null);
        }
        genome.set(age, mutationsAtAge);
    }

    public Mutation getMutation(int age, int mutationIndex) {
        List<Mutation> mutationsAtAge = getMutationsAtAge(age);
        if (mutationsAtAge == null) {
            return null;
        }
        return mutationsAtAge.get(mutationIndex);
    }

    public void addMutation(Mutation m) {
        List<Mutation> mutationsAtAge = getMutationsAtAge(m.getOrganismAge());
        if (mutationsAtAge == null) {
            mutationsAtAge = new ArrayList<Mutation>();
            setMutationsAtAge(m.getOrganismAge(), mutationsAtAge);
        }
        mutationsAtAge.add(m);
    }

    public boolean removeMutation(Mutation m) {
        List<Mutation> mutationsAtAge = getMutationsAtAge(m.getOrganismAge());
        if (mutationsAtAge != null) {
            return mutationsAtAge.remove(m);
        }
        return false;
    }
} public class Mutation extends Object {
        
    private Point location; //Un-rotated mutation location
    int organismAge; //How old is the organism when this mutation is affective
    int gameTime; //When was this mutation created, used for statistics.
    
    public Mutation(Point location, int organismAge, int gameTime) {
        this.location = location; 
        this.organismAge = organismAge;
        this.gameTime = gameTime;
    }
    
    public Point getLocation() {
        return location;
    }
    
    public int getOrganismAge() {
        return organismAge;
    }
    
    public int getGameTime() {
        return gameTime;
    }
    
    @Override
    public int hashCode() {
        return location.hashCode();
    }
    
    @Override
    public boolean equals(Object obj) {
        Mutation m2 = (Mutation) obj;
        return getLocation().equals(m2.getLocation()) && getOrganismAge()==m2.getOrganismAge();
    }
} public class Organism {
    private int id;
    private ArrayList<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.setLocation(x, y);
        this.seed = seed;
        this.genome = new Genome();
        this.cells = new ArrayList<Cell>();
        this.timeOfDeath = -1;  
        this.attributes = new OrgAttributes(this);
   
        if (parent!=null) {
            parent.addChild(this);
            this.genome = parent.getGenome().clone();
            this.lifespan = parent.lifespan;
        }
    }
    
    public int getId() {
        return id;
    }

    public void setLocation(int x, int y) {
        this.x = x;
        this.y = y;
        this.location = new Point(x,y);
    }

    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;
    }
    
    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 List<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);
    }
    
    /*
     * Create cell but don't add it;
     * @param x - the x coordinate of the cell
     * @param y - the y coordinate of the cell
     * @return Create but don't add the cell
     */    
    public Cell createCell(int x, int y) {
        Cell c = new Cell(x, y, this);
        return c;
    }

    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);            
    }

    /* 
     * @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 o) {
        return this.id == o.id;
    }
} public static enum SeedType { 
               
    L2_RPentomino ("L2 to R-Pentomino"),
    L2B1_RPentomino ("L2B1 to R-Pentomino"), 
    Square2_RPentomino ("2x2 Box to R-Pentomino"), 
    Glider_RPentomino ("Glider to R-Pentomino"), 
    RPentomino_RPentomino ("R-Pentomino to R-Pentomino"),        
    Boxlid_RPentomino ("Boxlid to R-Pentomino"), 
    Boxlid3_RPentomino ("Boxlid3 to R-Pentomino"),        
    Boxhat_RPentomino ("Boxhat to R-Pentomino"),
    Bentline1_RPentomino ("Bentline1 to R-Pentomino"),
    Bentline1_Plus ("Bentline1 to Plus"),
    Bentline1m_RPentomino ("Bentline1m to R-Pentomino"),
    Bentline_U3x3 ("Bentline to U 3x3"),
    BentlineMargin35_RPentomino ("BentlineM35 to R-Pentomino"),
    Onebit_RPentomino ("Onebit to R-Pentomino"), 
    OnebitB1_RPentomino ("OnebitB1 to R-Pentomino"),  
    Test_Pattern ("Test Pattern");
                   
    private final String name;
        
    private SeedType(String name) {
        this.name = name;
    }

    public String toString() {
       return this.name;
    }
        
    public static SeedType get(String name) {
        for (SeedType st : SeedType.values()) {
            if (st.toString().equals(name)) {
                return st;
            }
        }
        return null;
    }

    /*
     * 8 way symmetry. Looks the same way under rotation and also mirroring.
     * 
     * @return true if the seed has 8 way symmetry.
     */
    public boolean isSymmetric8() {
        switch (this) {
            case Square2_RPentomino:
                return true;
            case Onebit_RPentomino:
                return true;
            case OnebitB1_RPentomino:
                return true;
                // case Test_Pattern : return true;
            default:
                return false;
        }
    }

    /*
     * 4 way symmetry. Like a line which is symmetric under mirroring, and 2
     * rotations.
     * 
     * @return true if the seed has 4 way symmetry.
     */
    public boolean isSymmetric4() {
        switch (this) {
            default:
                return false;
        }
    }

    /*
     * 2 way symmetry. All 4 rotations are different, but mirror images look
     * the same as a rotation
     * 
     * @return true if the seed has 2 way symmetry.
     */
    public boolean isSymmetric2() {
        switch (this) {
            case L2_RPentomino:
                return true;
            case L2B1_RPentomino:
                return true;
            default:
                return false;
        }
    }
}    public class SproutStep extends Step {
    SeedType seedType;
    boolean isSproutDelayedMode = false;
    HashMap<Organism,ArrayList<Seed>> savedSeeds;
    
    public SproutStep(GameModel gameModel) {
        super(gameModel);       
    }
    
    public void setSeedType(SeedType seedType) {
        this.seedType = seedType;
    }

    public SeedType getSeedType() {
        return seedType;
    }    

    public void perform() {
        setSeedType(SeedType.get(getSettings().getString(Settings.SEED_TYPE)));
        this.isSproutDelayedMode = getSettings().getBoolean(Settings.SPROUT_DELAYED_MODE);
        if (isSproutDelayedMode) {
            if (this.savedSeeds!=null) {
                sproutSeeds(this.savedSeeds);
            }
            savedSeeds = findSeeds();
        }
        else { 
            //simple way of doing things, makes it harder to display seeds;
            HashMap<Organism,ArrayList<Seed>> seeds = findSeeds();
            sproutSeeds(seeds);
        }
    }

    public boolean checkMinAgeToHaveChildren(Organism org, int seedCount) {
        return org.getAge()>=getSettings().getInt(Settings.MIN_CHILDBEARING_AGE);
    }

    private void sproutSeeds(HashMap<Organism,ArrayList<Seed>> seeds) {
        for (Organism o: seeds.keySet()) {
            if(!o.isAlive()) {
                continue;
            }
            ArrayList<Seed> seedList = seeds.get(o);

            if (!checkMinAgeToHaveChildren(o, seedList.size())) {
                continue;
            }

            for (Seed s : seedList) {
                Point seedOnPosition = s.getSeedOnPosition();

                Cell c = getBoard().getCell(seedOnPosition);

                if (c==null && !isSproutDelayedMode) {
                    //Should almost never happen, only if seeds overlapped.
                    continue;
                }

                SproutUtils.sproutSeed(s, o, getEchosystem());
            }
        }
    }
            
    private HashMap<Organism,ArrayList<Seed>> findSeeds() {
        List<Organism> organisms = new ArrayList<>(getEchosystem().getOrganisms());
        HashMap<Organism,ArrayList<Seed>> seeds = new HashMap<Organism,ArrayList<Seed>>();
        // initialize the hash map so we don't need a synchronous way to add keys by multiple threads
        for (Organism o : organisms) {
            seeds.put(o,new ArrayList<>(5));
        }

        // Split finding seeds into multiple threads for multi-core CPU processing
        int PARTITION_SIZE = 20;
        List<Thread> threads = new ArrayList<>();
        for (int orgPartition=0; orgPartition<organisms.size();orgPartition+=PARTITION_SIZE) {
            final int orgPartitionFinal = orgPartition;
            Thread t = new Thread(new Runnable() {
                public void run() {
                    for (int oi = orgPartitionFinal; oi<orgPartitionFinal+PARTITION_SIZE && oi<organisms.size();oi++) {
                        Organism o = organisms.get(oi);
                        for (Cell c : o.getCells()) {
                            Seed s = checkAndMarkSeed(c);
                            if (s!=null) {
                                seeds.get(o).add(s);
                            }
                        }
                    }
                }
            });
            t.start();
            threads.add(t);
        }
        try {
            for (Thread t : threads) {
                t.join();
            }
        }
        catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        return seeds;
    }          

    private Seed checkAndMarkSeed(Cell topLeftCell) {
        for (Seed s : SeedFactory.getSeedRotations(getSeedType())) {
            
            Point seedOnBit = s.getSeedOnBit();
            
            int x = topLeftCell.x-seedOnBit.x;
            int y = topLeftCell.y-seedOnBit.y;
            
            if (x<0||y<0) {
                continue;
            }
            
            s.setPosition(x, y);                      
            s.setParentPosition(topLeftCell.getOrganism().getLocation());
            
            if(checkAndMarkSeed(s)) {          
                return s;
            }
        }
        return null;

    }

    public boolean checkAndMarkSeed(Seed seed) {

        ArrayList<Cell> seedCells = new ArrayList<Cell>();        
        Organism seedOrg = null;
        int i = seed.getPosition().x;
        int j = seed.getPosition().y;
        int seedWidth = seed.getSeedWidth();
        int seedHeight = seed.getSeedHeight();
        int border = seed.getSeedBorder();

        //Check seed bounds
        if( i+seedWidth>=getBoard().getWidth() || j+seedHeight>=getBoard().getHeight()) {            
            return false;
        }

        //Check seed;
        for (int si=0;si<seedWidth;si++) {
            for (int sj=0;sj<seedHeight;sj++) {                              
                Cell c = getBoard().getCell(i+si,j+sj);                
                if (seed.getSeedBit(si,sj)) {

                    if (c==null) {
                        return false;                        
                    }
                    //else
                    if (seedOrg==null) {
                        seedOrg = c.getOrganism();
                    }
                    if (!c.getOrganism().equals(seedOrg)) {
                        return false;
                    }
                    seedCells.add(c);                                                                
                }
                else {
                    if (c!=null) {
                        return false;
                    }
                }
            }            
        }

        //Check border
        for (int si=-border;si<seedWidth+border;si++) { 
            for (int sj=-border;sj<seedHeight+border;sj++) {                       
                if(si<=-1 || sj<=-1 || si>=(seedWidth) || sj>=seedHeight) {                    
                    if(i+si>=0 && j+sj>=0 && 
                            i+si<=getBoard().getWidth()-1 &&  
                            j+sj<=getBoard().getHeight()-1) {
                        if (getBoard().getCell(i+si,j+sj)!=null) {
                            return false;
                        }
                    }
                }
            }
        }

        for (Cell c: seedCells) {
            c.setMarkedAsSeed(true);
        }
        return true;
    }
} public class BattleColorUtils {
    public static final int COLOR_COUNT = 3;

    public static ArrayList<Entry<Integer, Integer>> getColorCounts(Echosystem echosystem) {
        HashMap<Integer, Integer> colorCountsMap = new HashMap<>();
        for (int k = 0; k < COLOR_COUNT; k++) {
            colorCountsMap.put(k, 0);
        }
        for (Organism o : echosystem.getOrganisms()) {
            Integer v = colorCountsMap.get(o.getAttributes().colorKind);
            colorCountsMap.put(o.getAttributes().colorKind, v == null ? 1 : v + 1);
        }
        ArrayList<Entry<Integer, Integer>> colorCounts = new ArrayList<>(colorCountsMap.entrySet());
        Collections.sort(colorCounts, (e1, e2) -> (e1.getValue() - e2.getValue()));
        return colorCounts;
    }

    public static void joinColor(Echosystem echosystem, int fromKind, int toKind) {
        for (Organism o : echosystem.getOrganisms()) {
            if (o.getAttributes().colorKind == fromKind) {
                updateOrganismColor(o, toKind);
            }
        }
    }

    public static void splitColor(Echosystem echosystem, int splitKind, int emptyKind) {
        int xSum = 0;
        int sSize = 0;
        for (Organism o : echosystem.getOrganisms()) {
            if (o.getAttributes().colorKind == splitKind) {
                xSum += o.x;
                sSize += 1;
            }
        }

        int avgX = xSum / sSize;
        for (Organism o : echosystem.getOrganisms()) {
            if (o.getAttributes().colorKind == splitKind) {
                if ((avgX > echosystem.getBoard().getWidth() / 2 && o.x > avgX)
                        || (avgX <= echosystem.getBoard().getWidth() / 2 && o.x < avgX)) {
                    updateOrganismColor(o, emptyKind);
                }
            }
        }
    }

    public static void updateOrganismColor(Organism o, int colorKind) {
        // we update the colorKind for all ancestors, because some ancestors get
        // rendered as tail
        o.getAttributes().colorKind = colorKind;
        Organism p = o.getParent();
        while (p != null) {
            p.getAttributes().colorKind = colorKind;
            p = p.getParent();
        }
    }
} public class SproutUtils {

    public static Organism sproutRandomSeed(SeedType seedType, Echosystem echosystem, Point location) {
        int x;
        int y;
        if (location!=null) {
            x = location.x;
            y = location.y;
        }
        else {
            x = (new Random()).nextInt(echosystem.getBoard().getWidth());
            y = (new Random()).nextInt(echosystem.getBoard().getHeight());
        }

        List<Seed> seedRotations = SeedFactory.getSeedRotations(seedType);
        Seed s = seedRotations.get((new Random()).nextInt(seedRotations.size()));

        SeedSproutPattern pattern = s.getSeedSproutPattern();
        final int seedWidth = pattern.getSeedPattern().getWidth();
        final int seedHeight = pattern.getSeedPattern().getWidth();
        final BitPattern currentSproutPattern = pattern.getSproutPattern();
        SeedSproutPattern newPattern = new SeedSproutPattern() {
            {
                this.seedPattern = new BitPattern(new int[seedWidth][seedHeight]);

                this.sproutPattern = currentSproutPattern;

                this.sproutOffset = new Point(0, 0);
            }
        };
        s.setPosition(x, y);
        s.setParentPosition(new Point(120, 120));
        Seed randomSeed = new Seed(newPattern, s.getRotation());

        randomSeed.setPosition(x, y);
        randomSeed.setParentPosition(new Point(x + 1, y + 1));

        return sproutSeed(randomSeed, null, echosystem);
    }

    public static Organism sproutSeed(Seed seed, Organism seedOrg, Echosystem echosystem) {
        Point sproutPosition = seed.getSproutPosition();
        Point sproutCenter = seed.getSproutCenter();

        int seedX = seed.getPosition().x;
        int seedY = seed.getPosition().y;

        int sproutX = sproutPosition.x;
        int sproutY = sproutPosition.y;

        int newOrgX = sproutCenter.x;
        int newOrgY = sproutCenter.y;

        int seedWidth = seed.getSeedWidth();
        int seedHeight = seed.getSeedHeight();
 
        int sproutWidth = seed.getSproutWidth();
        int sproutHeight = seed.getSproutHeight();

        if (seedX < 0 || seedY < 0
                || seedX + seedWidth > echosystem.getBoard().getWidth() - 1
                || seedY + seedHeight > echosystem.getBoard().getHeight() - 1
                || sproutX + sproutWidth > echosystem.getBoard().getWidth() - 1
                || sproutY + sproutHeight > echosystem.getBoard().getHeight() - 1
                || sproutX < 0 || sproutY < 0) {
            
            //Clear the seed flag from cells before returning null
            if (seedOrg!=null) {
                for (Cell c : seedOrg.getCells()) {
                    c.setMarkedAsSeed(false);
                }
            }
            return null;
        }

        Organism newOrg = echosystem.createOrganism(newOrgX, newOrgY, seedOrg, seed);

        //Remove old seed
        for (int x=0;x<seedWidth;x++) {
            for (int y=0; y<seedHeight;y++) {
                if (seed.getSeedBit(x, y)) {
                    Cell rc = echosystem.getBoard().getCell(seedX+x, seedY+y);

                    echosystem.removeCell(rc);
                    //getBoard().clearCell(seedX+x,seedY+y);              
                }
            }
        }

        for (int si = 0;si<sproutWidth;si++) {
            for (int sj = 0;sj<sproutHeight;sj++) {

                int i = sproutX+si;
                int j = sproutY+sj;

                Cell c = echosystem.getBoard().getCell(i, j);

                if (c!=null) {   
                    //Only happens when border improperly configured 
                    //and/or successively sprouted seeds overlap each other
                    echosystem.removeCell(c);
                    //getBoard().clearCell(i, j);
                }

                if (seed.getSproutBit(si, sj)) {
                    Cell newC = echosystem.addCell(i,j,newOrg);
                }
                else {
                    //Should be ok
                }
            }
        }
        return newOrg;
    }
}
import java.awt.Point;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
import com.sproutlife.Settings;
import com.sproutlife.model.GameModel;
import com.sproutlife.model.echosystem.Echosystem;
import com.sproutlife.model.echosystem.Genome;
import com.sproutlife.model.echosystem.Mutation;
import com.sproutlife.model.echosystem.Organism;
import com.sproutlife.model.seed.SeedFactory.SeedType;
import com.sproutlife.model.step.SproutStep;
import com.sproutlife.model.utils.BattleColorUtils;
import com.sproutlife.model.utils.SproutUtils;
package com.sproutlife.io;



public class GenomeIo {
    public static int VERSION = 1;

    public static void saveGenome(File file, GameModel gameModel) throws IOException {