public SpeciesBreedingPool(GenomeNEAT genome, int id)
 {
     agentList      = new List <Agent>();
     templateGenome = genome;
     speciesID      = id;
     nextAgentIndex = 0;
 }
示例#2
0
    public GenomeNEAT InitializeRandomBrain(int numInputNodes, int numOutputNodes)
    {
        sourceGenome = new GenomeNEAT(numInputNodes, numOutputNodes); // create blank genome with no connections
        sourceGenome.CreateMinimumRandomConnections();                // Add links to each output node froma  random input node

        return(sourceGenome);
    }
    public void SortAgentIntoBreedingPool(Agent agent)
    {
        bool speciesGenomeMatch = false;

        for (int s = 0; s < speciesBreedingPoolList.Count; s++)
        {
            float geneticDistance = GenomeNEAT.MeasureGeneticDistance(agent.brainGenome, speciesBreedingPoolList[s].templateGenome, 0.425f, 0.425f, 0.15f, 0f, 0f, 1f);

            if (geneticDistance < 1f)   //speciesSimilarityThreshold) {  // !!! figure out how/where to place this attribute or get a ref from crossoverManager
            {
                speciesGenomeMatch = true;
                //agent.speciesID = speciesBreedingPoolList[s].speciesID; // this is done inside the AddNewAgent method below v v v
                speciesBreedingPoolList[s].AddNewAgent(agent);
                //Debug.Log("SortAgentIntoBreedingPool dist: " + geneticDistance.ToString() + ", speciesIDs: " + agent.speciesID.ToString() + ", " + speciesBreedingPoolList[s].speciesID.ToString());
                break;
            }
        }
        if (!speciesGenomeMatch)
        {
            //Debug.Log("POPULATION SortAgentIntoBreedingPool NO MATCH!!! -- creating new BreedingPool ");
            SpeciesBreedingPool newSpeciesBreedingPool = new SpeciesBreedingPool(agent.brainGenome, GetNextSpeciesID()); // creates new speciesPool modeled on this agent's genome
            newSpeciesBreedingPool.AddNewAgent(agent);                                                                   // add this agent to breeding pool
            speciesBreedingPoolList.Add(newSpeciesBreedingPool);                                                         // add new speciesPool to the population's list of all active species
        }
    }
示例#4
0
 public Species(Agent newAgent)
 {
     id = nextID;
     nextID++;
     templateGenome = newAgent.brainGenome;
     AddNewMember(newAgent);
 }
示例#5
0
    public override object Read(ES2Reader reader)
    {
        GenomeNEAT data = new GenomeNEAT();

        Read(reader, data);
        return(data);
    }
 public Species(Agent newAgent) {
     id = nextID;
     nextID++;
     templateGenome = newAgent.brainGenome;
     AddNewMember(newAgent);
     
     
 }
示例#7
0
    public GenomeNEAT InitializeNewBrain(int numInputNodes, int numHiddenNodes, int numOutputNodes, float connectedness, bool randomWeights)
    {
        sourceGenome = new GenomeNEAT(numInputNodes, numHiddenNodes, numOutputNodes); // create blank genome with no connections
        sourceGenome.CreateInitialConnections(connectedness, randomWeights);

        //Debug.Log("InitializeBlankBrain #nodes: " + sourceGenome.nodeNEATList.Count.ToString() + ", #links: " + sourceGenome.linkNEATList.Count.ToString());

        return(sourceGenome);
    }
示例#8
0
    public override void Write(object obj, ES2Writer writer)
    {
        GenomeNEAT data = (GenomeNEAT)obj;

        // Add your writer.Write calls here.
        writer.Write(0); // Version 0 is current version number
        // Make sure to edit Read() function to properly handle version control!
        // VERSION 0:
        writer.Write(data.nodeNEATList);
        writer.Write(data.linkNEATList);
    }
示例#9
0
    public override void Read(ES2Reader reader, object c)
    {
        GenomeNEAT data = (GenomeNEAT)c;
        // Add your reader.Read calls here to read the data into the object.
        // Read the version number.
        int fileVersion = reader.Read <int>();

        // VERSION 0:
        if (fileVersion >= 0)
        {
            data.nodeNEATList = reader.ReadList <GeneNodeNEAT>();
            data.linkNEATList = reader.ReadList <GeneLinkNEAT>();
        }
    }
示例#10
0
    public GenomeNEAT InitializeBlankBrain(int numInputNodes, int numOutputNodes)
    {
        sourceGenome = new GenomeNEAT(numInputNodes, numOutputNodes); // create blank genome with no connections

        /*int numNewLinks = 0;
         * int numNewNodes = 0;
         * for(int i = 0; i < numNewLinks; i++) {
         *  sourceGenome.AddNewRandomLink(0);
         * }
         * for (int j = 0; j < numNewNodes; j++) {
         *  sourceGenome.AddNewRandomNode(0);
         * }*/

        //Debug.Log("InitializeBlankBrain #nodes: " + sourceGenome.nodeNEATList.Count.ToString() + ", #links: " + sourceGenome.linkNEATList.Count.ToString());

        return(sourceGenome);
    }
    public void PerformBodyMutation(ref CritterGenome bodyGenome, ref GenomeNEAT brainGenome) {
        int numBodyMutations = 0;
        //float attachDirMutationMultiplier = 0.1f;
        //float restAngleDirMutationMultiplier = 0.1f;
        float jointAngleTypeMultiplier = 0.5f;
        List<int> nodesToDelete = new List<int>();        
        for (int i = 0; i < bodyGenome.CritterNodeList.Count; i++) {
            
            // Segment Proportions:
            if(CheckForMutation(segmentProportionChance * bodyMutationBlastModifier)) {  // X
                bodyGenome.CritterNodeList[i].dimensions.x = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].dimensions.x);
                numBodyMutations++;
            }
            if (CheckForMutation(segmentProportionChance * bodyMutationBlastModifier)) {  // Y
                bodyGenome.CritterNodeList[i].dimensions.y = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].dimensions.y);
                numBodyMutations++;
            }
            if (CheckForMutation(segmentProportionChance * bodyMutationBlastModifier)) {  // Z
                bodyGenome.CritterNodeList[i].dimensions.z = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].dimensions.z);
                numBodyMutations++;
            }
            if(i > 0) {   // NOT THE ROOT SEGMENT!:
                // REMOVE SEGMENT!!!!
                if(CheckForMutation(removeSegmentChance * bodyMutationBlastModifier)) {  // Can't delete Root Segment
                    // Add this nodeID into queue for deletion at the end of mutation, to avoid shortening NodeList while traversing it:
                    nodesToDelete.Add(i);                    
                }
                else {
                    // Segment Attach Settings:
                    if (CheckForMutation(segmentAttachSettingsChance * bodyMutationBlastModifier)) {  // Position X
                        bodyGenome.CritterNodeList[i].jointLink.attachDir = MutateBodyVector3Normalized(bodyGenome.CritterNodeList[i].jointLink.attachDir);
                        numBodyMutations++;
                    }
                    if (CheckForMutation(segmentAttachSettingsChance * bodyMutationBlastModifier)) {  // Orientation X
                        bodyGenome.CritterNodeList[i].jointLink.restAngleDir = MutateBodyVector3Normalized(bodyGenome.CritterNodeList[i].jointLink.restAngleDir);
                        numBodyMutations++;
                    }

                    // Joint Settings:
                    if (CheckForMutation(jointSettingsChance * bodyMutationBlastModifier)) {
                        bodyGenome.CritterNodeList[i].jointLink.jointLimitPrimary = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].jointLink.jointLimitPrimary);
                    }
                    if (CheckForMutation(jointSettingsChance * bodyMutationBlastModifier)) {
                        bodyGenome.CritterNodeList[i].jointLink.jointLimitSecondary = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].jointLink.jointLimitSecondary);
                    }
                    if (CheckForMutation(jointSettingsChance * bodyMutationBlastModifier)) {
                        bodyGenome.CritterNodeList[i].jointLink.recursionScalingFactor = MutateBodyFloatMult(bodyGenome.CritterNodeList[i].jointLink.recursionScalingFactor);
                    }
                    // Joint Hinge TYPE!!!!  --- how to handle this???
                    if (CheckForMutation(jointSettingsChance * jointAngleTypeMultiplier * bodyMutationBlastModifier)) {
                        int jointTypeInt = Mathf.RoundToInt(UnityEngine.Random.Range(0f, 4f));
                        bodyGenome.CritterNodeList[i].jointLink.jointType = (CritterJointLink.JointType)jointTypeInt;
                        Debug.Log(i.ToString() + " Mutated to JointType: " + bodyGenome.CritterNodeList[i].jointLink.jointType);
                    }

                    // RECURSION:
                    if (CheckForMutation(recursionChance * bodyMutationBlastModifier)) {
                        int addRemove = Mathf.RoundToInt(UnityEngine.Random.Range(0f, 1f)) * 2 - 1;  // either -1 or 1
                                                                                                     //addRemove = 1; // TEMPORARY!!! testing add recursion
                        if (bodyGenome.CritterNodeList[i].jointLink.numberOfRecursions + addRemove < 0 || bodyGenome.CritterNodeList[i].jointLink.numberOfRecursions + addRemove > 3) {
                            // Do Nothing, tried to change numRecursions beyond the CAP
                        }
                        else {  // It's all good -- PROCEED:
                            bodyGenome.CritterNodeList[i].jointLink.numberOfRecursions += addRemove;
                            numBodyMutations++;

                            if (addRemove > 0) {  // Created an additional Recursion -- Need to ADD new BrainNodes:
                                                  //int numNodesBefore = brainGenome.nodeNEATList.Count;                            
                                                  //brainGenome.AdjustBrainAfterBodyChange(bodyGenome);
                                                  //int numNodesAfter = brainGenome.nodeNEATList.Count;
                                                  //Debug.Log("MUTATION RECURSION! b4#: " + numNodesBefore.ToString() + ", after#: " + numNodesAfter.ToString());
                            }
                            else {  // REMOVED a Recursion -- Need to REMOVE BrainNodes:
                                    //brainGenome.AdjustBrainRemovedRecursion(bodyGenome, i);
                            }
                            
                        }
                        // NEED TO FIX BRAIN!!! -- how to preserve existing wiring while adding new neurons that may re-order?
                        // Do I need to save a reference to segment/nodes within each input/output neuron?
                    }

                    // SYMMETRY
                    if (CheckForMutation(symmetryChance * bodyMutationBlastModifier)) {
                        int symmetryType = Mathf.RoundToInt(UnityEngine.Random.Range(0f, 2f));  // 0=none,1=x, or 2=y
                        Debug.Log("Mutated Symmetry! " + bodyGenome.CritterNodeList[i].jointLink.symmetryType.ToString() + " => " + ((CritterJointLink.SymmetryType)symmetryType).ToString());
                        bodyGenome.CritterNodeList[i].jointLink.symmetryType = (CritterJointLink.SymmetryType)symmetryType;                        
                    }
                    // EXTRA -- recursion forward, only attach to tail
                }

                // CREATE NEW ADD-ON!!  On this segment
                if (CheckForMutation(newAddonChance * bodyMutationBlastModifier)) {
                    // Which types can be created automatically??
                    // 
                    int randomAddon = Mathf.RoundToInt(UnityEngine.Random.Range(0f, 1f));
                    switch (randomAddon) {
                        case 0:
                            AddonOscillatorInput newOscillatorInput = new AddonOscillatorInput(i, GetNextAddonInnov());
                            bodyGenome.addonOscillatorInputList.Add(newOscillatorInput);
                            break;
                        case 1:
                            AddonValueInput newValueInput = new AddonValueInput(i, GetNextAddonInnov());
                            bodyGenome.addonValueInputList.Add(newValueInput);
                            break;
                        default:
                            break;
                    }
                    Debug.Log("NEW ADDON! " + randomAddon.ToString() + ", segmentNode: " + i.ToString());
                }
            }            
        }

        for(int i = 0; i < nodesToDelete.Count; i++) {
            Debug.Log("DELETE SEGMENT NODE! " + i.ToString());
            bodyGenome.DeleteNode(nodesToDelete[i]);
            bodyGenome.RenumberNodes();
        }
        List<int> nodesToAddTo = new List<int>(); // After Deleting nodes, check all remaining nodes for a chance to have a child segment added:
        for (int i = 0; i < bodyGenome.CritterNodeList.Count; i++) {
            if (CheckForMutation(newSegmentChance * bodyMutationBlastModifier)) {
                nodesToAddTo.Add(i);
            }
        }
        for (int i = 0; i < nodesToAddTo.Count; i++) {
            Vector3 attachDir = Vector3.Slerp(new Vector3(0f, 0f, 1f), UnityEngine.Random.onUnitSphere, UnityEngine.Random.Range(0f, 1f)); //ConvertWorldSpaceToAttachDir(selectedSegment, rightClickWorldPosition);
            int nextID = bodyGenome.CritterNodeList.Count;
            bodyGenome.AddNewNode(bodyGenome.CritterNodeList[nodesToAddTo[i]], attachDir, new Vector3(0f, 0f, 0f), nextID, GetNextNodeInnov());

            // Init joint type:
            bodyGenome.CritterNodeList[bodyGenome.CritterNodeList.Count - 1].jointLink.jointType = (CritterJointLink.JointType)Mathf.RoundToInt(UnityEngine.Random.Range(1, 4));

            // Auto-Add-ons: -- New Segment starts with a joint angle sensor and joint Motor by default:
            AddonJointAngleSensor newJointAngleSensor = new AddonJointAngleSensor(nextID, GetNextAddonInnov());
            bodyGenome.addonJointAngleSensorList.Add(newJointAngleSensor);
            // Motor:
            AddonJointMotor newJointMotor = new AddonJointMotor(nextID, GetNextAddonInnov());
            bodyGenome.addonJointMotorList.Add(newJointMotor);

            Debug.Log("New SEGMENT! : " + nodesToAddTo[i].ToString());
        }        

        // Addon Mutation:
        PerformAddonMutation(ref bodyGenome, ref brainGenome);
        //Debug.Log("NumBodyMutations: " + numBodyMutations.ToString());
    }
示例#12
0
    public static float MeasureGeneticDistance(GenomeNEAT genomeA, GenomeNEAT genomeB, float neuronCoefficient, float linkCoefficient, float weightCoefficient, float normNeuron, float normLink, float normWeight)
    {
        int indexA = 0;
        int indexB = 0;

        float weightDeltaTotal = 0f;
        float maxGenes         = Mathf.Max((float)genomeA.linkNEATList.Count, (float)genomeB.linkNEATList.Count);
        float matchingGenes    = 0f;
        //float disjointGenes = 0f;
        //float excessGenes = 0f;
        float linkGenes       = 0f;
        float neuronGenes     = 0f;
        int   matchingNeurons = 0;
        //float smallestWeightValue = 0f;
        float largestWeightDelta = 0f;


        // matching Links
        // matching nodes/neurons
        // matching activation functions?
        // difference in weights
        // difference in add-on settings?
        // -- NEED TO GO BY BODY GENOME?????

        // LINKS:::
        for (int i = 0; i < genomeA.linkNEATList.Count + genomeB.linkNEATList.Count; i++)
        {
            if (indexA < genomeA.linkNEATList.Count)
            {
                if (indexB < genomeB.linkNEATList.Count)   // both good

                {
                    if (genomeA.linkNEATList[indexA].innov < genomeB.linkNEATList[indexB].innov)
                    {
                        // disjoint A
                        linkGenes++;
                        indexA++;
                    }
                    else if (genomeA.linkNEATList[indexA].innov > genomeB.linkNEATList[indexB].innov)
                    {
                        // disjoint B
                        linkGenes++;
                        indexB++;
                    }
                    else if (genomeA.linkNEATList[indexA].innov == genomeB.linkNEATList[indexB].innov)
                    {
                        // match!
                        float weightDelta = Mathf.Abs(genomeA.linkNEATList[indexA].weight - genomeB.linkNEATList[indexB].weight);
                        weightDeltaTotal += weightDelta;
                        matchingGenes++;
                        //Debug.Log("!@$#!$#!@$#!@$# MeasureGeneticDistance! innov MATCHING GENE: weightA: " + genomeA.linkNEATList[indexA].weight.ToString() + ", weightB: " + genomeB.linkNEATList[indexB].weight.ToString());
                        indexA++;
                        indexB++;

                        largestWeightDelta = Mathf.Max(largestWeightDelta, weightDelta);
                    }
                }
                else   // A is good, B is finished
                {
                    linkGenes++;
                    indexA++;
                }
            }
            else                                         // A done
            {
                if (indexB < genomeB.linkNEATList.Count) // A is finished, B is good
                {
                    linkGenes++;
                    indexB++;
                }
                else   // both done
                {
                    break;
                }
            }
        }
        for (int i = 0; i < genomeA.nodeNEATList.Count; i++)
        {
            Int3 nodeID = new Int3(genomeA.nodeNEATList[i].sourceAddonInno, genomeA.nodeNEATList[i].sourceAddonRecursionNum, genomeA.nodeNEATList[i].sourceAddonChannelNum);
            if (genomeB.GetNodeIndexFromInt3(nodeID) != -1)  // if genomeB has the same neuron:
            {
                matchingNeurons++;
            }
        }
        int totalNeurons = (genomeA.nodeNEATList.Count + genomeB.nodeNEATList.Count);
        int totalLinks   = (genomeA.linkNEATList.Count + genomeB.linkNEATList.Count);

        if (totalNeurons > 0)
        {
            neuronGenes = Mathf.Lerp((float)((totalNeurons) - matchingNeurons * 2), (float)((totalNeurons) - matchingNeurons * 2) / (float)(totalNeurons), normNeuron); // get proportion of neurons that match between the two genomes, should be 0-1
        }
        if (totalLinks > 0)
        {
            linkGenes = Mathf.Lerp(((float)(totalLinks) - matchingGenes * 2f), ((float)(totalLinks) - matchingGenes * 2f) / (float)(totalLinks), normLink);
        }
        //float weightSpan = largestWeightValue - smallestWeightValue;

        float distance = 0f;

        if (true)    // maxGenes > 0f
        //float excessComponent = excessCoefficient * Mathf.Lerp(excessGenes, excessGenes / maxGenes, normExcess);
        //float disjointComponent = disjointCoefficient * Mathf.Lerp(disjointGenes, disjointGenes / maxGenes, normDisjoint);
        //float weightComponent = weightCoefficient * Mathf.Lerp(weightDeltaTotal, weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f), normWeight);
        {
            float totalPie        = neuronCoefficient + linkCoefficient + weightCoefficient;
            float weightComponent = 0f;
            if (largestWeightDelta > 0f)
            {
                weightComponent = Mathf.Lerp(weightDeltaTotal / largestWeightDelta, weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f) / largestWeightDelta, normWeight);
            }
            // OLD //distance = excessComponent + disjointComponent + weightComponent;
            distance = neuronGenes * (neuronCoefficient / totalPie) + linkGenes * (linkCoefficient / totalPie) + weightComponent * (weightCoefficient / totalPie);
            //Debug.Log("MeasureGeneticDistance! neuronGenes: " + (neuronGenes).ToString() + " (" + genomeA.nodeNEATList.Count.ToString() + "," + genomeB.nodeNEATList.Count.ToString() +
            //    "), linkGenes: " + (linkGenes).ToString() + " (" + genomeA.linkNEATList.Count.ToString() + "," + genomeB.linkNEATList.Count.ToString() +
            //    "), weight: " + weightComponent.ToString() + ", largestWeightDelta: " + largestWeightDelta.ToString() + ", weightDeltaTotal: " + weightDeltaTotal.ToString() + "weightMatches: " + matchingGenes.ToString() +
            //    ", distance: " + distance.ToString());
            //Debug.Log("MeasureGeneticDistance! disjoint: " + (disjointGenes).ToString() + ", excess: " + (excessGenes).ToString() + ", weight: " + (weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f)).ToString() + ", distance: " + distance.ToString());
        }
        // else stays 0f;
        return(distance);
    }
    public void PerformAddonMutation(ref CritterGenome bodyGenome, ref GenomeNEAT brainGenome) {
             
        // Chance to Remove an existing Add-on:
        // Chance to Mutate Add-On Settings:
        for (int i = bodyGenome.addonPhysicalAttributesList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonPhysicalAttributesList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPhysicalAttributesList[i].bounciness[0] = MutateBodyFloatMult(bodyGenome.addonPhysicalAttributesList[i].bounciness[0], 0f, 1f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPhysicalAttributesList[i].dynamicFriction[0] = MutateBodyFloatMult(bodyGenome.addonPhysicalAttributesList[i].dynamicFriction[0], 0f, 1f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPhysicalAttributesList[i].staticFriction[0] = MutateBodyFloatMult(bodyGenome.addonPhysicalAttributesList[i].staticFriction[0], 0f, 1f);
                }
                /*if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezePositionX[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezePositionX[0]);
                }
                if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezePositionY[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezePositionY[0]);
                }
                if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezePositionZ[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezePositionZ[0]);
                }
                if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezeRotationX[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezeRotationX[0]);
                }
                if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezeRotationY[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezeRotationY[0]);
                }
                if (CheckForMutation(addonSettingsChance)) {
                    bodyGenome.addonPhysicalAttributesList[i].freezeRotationZ[0] = MutateBodyBool(bodyGenome.addonPhysicalAttributesList[i].freezeRotationZ[0]);
                }*/
            }
        }
        for (int i = bodyGenome.addonJointAngleSensorList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonJointAngleSensorList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonJointAngleSensorList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonJointAngleSensorList[i].sensitivity[0], 0.001f, 100f);
                }
                // #################  How to handle MEasureVel Checkbox?? It adds a Neuron!!!!!!!!!
                // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   
            }
        }
        for (int i = bodyGenome.addonContactSensorList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonContactSensorList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonContactSensorList[i].contactSensitivity[0] = MutateBodyFloatMult(bodyGenome.addonContactSensorList[i].contactSensitivity[0], 0.01f, 10f);
                }
            }            
        }
        for (int i = bodyGenome.addonRaycastSensorList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonRaycastSensorList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonRaycastSensorList[i].forwardVector[0] = MutateBodyVector3Normalized(bodyGenome.addonRaycastSensorList[i].forwardVector[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonRaycastSensorList[i].maxDistance[0] = MutateBodyFloatMult(bodyGenome.addonRaycastSensorList[i].maxDistance[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonCompassSensor1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonCompassSensor1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonCompassSensor1DList[i].forwardVector[0] = MutateBodyVector3Normalized(bodyGenome.addonCompassSensor1DList[i].forwardVector[0]);
                }
            }                               
        }
        for (int i = bodyGenome.addonCompassSensor3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonCompassSensor3DList.RemoveAt(i);
            }
            else {

            }

        }
        for (int i = bodyGenome.addonPositionSensor1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonPositionSensor1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPositionSensor1DList[i].forwardVector[0] = MutateBodyVector3Normalized(bodyGenome.addonPositionSensor1DList[i].forwardVector[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPositionSensor1DList[i].relative[0] = MutateBodyBool(bodyGenome.addonPositionSensor1DList[i].relative[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPositionSensor1DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonPositionSensor1DList[i].sensitivity[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonPositionSensor3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonPositionSensor3DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPositionSensor3DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonPositionSensor3DList[i].sensitivity[0], 0.01f, 100f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonPositionSensor3DList[i].relative[0] = MutateBodyBool(bodyGenome.addonPositionSensor3DList[i].relative[0]);
                }
            }            
        }
        for (int i = bodyGenome.addonRotationSensor1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonRotationSensor1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonRotationSensor1DList[i].localAxis[0] = MutateBodyVector3Normalized(bodyGenome.addonRotationSensor1DList[i].localAxis[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonRotationSensor1DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonRotationSensor1DList[i].sensitivity[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonRotationSensor3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonRotationSensor3DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonRotationSensor3DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonRotationSensor3DList[i].sensitivity[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonVelocitySensor1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonVelocitySensor1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonVelocitySensor1DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonVelocitySensor1DList[i].sensitivity[0], 0.01f, 100f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonVelocitySensor1DList[i].forwardVector[0] = MutateBodyVector3Normalized(bodyGenome.addonVelocitySensor1DList[i].forwardVector[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonVelocitySensor1DList[i].relative[0] = MutateBodyBool(bodyGenome.addonVelocitySensor1DList[i].relative[0]);
                }
            }            
        }
        for (int i = bodyGenome.addonVelocitySensor3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonVelocitySensor3DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonVelocitySensor3DList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonVelocitySensor3DList[i].sensitivity[0], 0.01f, 100f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonVelocitySensor3DList[i].relative[0] = MutateBodyBool(bodyGenome.addonVelocitySensor3DList[i].relative[0]);
                }
            }            
        }
        for (int i = bodyGenome.addonAltimeterList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonAltimeterList.RemoveAt(i);
            }
            else {

            }
        }
        for (int i = bodyGenome.addonEarBasicList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonEarBasicList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonEarBasicList[i].sensitivity[0] = MutateBodyFloatMult(bodyGenome.addonEarBasicList[i].sensitivity[0], 0.01f, 100f);
                }
            }                              
        }
        for (int i = bodyGenome.addonGravitySensorList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonGravitySensorList.RemoveAt(i);
            }
            else {

            }
        }
        for (int i = bodyGenome.addonOscillatorInputList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonOscillatorInputList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonOscillatorInputList[i].amplitude[0] = MutateBodyFloatMult(bodyGenome.addonOscillatorInputList[i].amplitude[0], 0.01f, 100f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonOscillatorInputList[i].frequency[0] = MutateBodyFloatMult(bodyGenome.addonOscillatorInputList[i].frequency[0], 0.01f, 50f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonOscillatorInputList[i].offset[0] = MutateBodyFloatMult(bodyGenome.addonOscillatorInputList[i].offset[0], -50f, 50f);
                }
            }            
        }
        for (int i = bodyGenome.addonValueInputList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonValueInputList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonValueInputList[i].value[0] = MutateBodyFloatMult(bodyGenome.addonValueInputList[i].value[0], -100f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonTimerInputList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonTimerInputList.RemoveAt(i);
            }
            else {

            }
        }
        //=======================  OUTPUTS ===================== OUTPUTS ======================= OUTPUTS =====================================================
        for (int i = bodyGenome.addonJointMotorList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonJointMotorList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonJointMotorList[i].motorForce[0] = MutateBodyFloatMult(bodyGenome.addonJointMotorList[i].motorForce[0], 0.01f, 200f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonJointMotorList[i].motorSpeed[0] = MutateBodyFloatMult(bodyGenome.addonJointMotorList[i].motorSpeed[0], 0.01f, 200f);
                }
            }            
        }
        for (int i = bodyGenome.addonThrusterEffector1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonThrusterEffector1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonThrusterEffector1DList[i].forwardVector[0] = MutateBodyVector3Normalized(bodyGenome.addonThrusterEffector1DList[i].forwardVector[0]);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonThrusterEffector1DList[i].maxForce[0] = MutateBodyFloatMult(bodyGenome.addonThrusterEffector1DList[i].maxForce[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonThrusterEffector3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonThrusterEffector3DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonThrusterEffector3DList[i].maxForce[0] = MutateBodyFloatMult(bodyGenome.addonThrusterEffector3DList[i].maxForce[0], 0.01f, 100f);
                }
            }            
        }        
        for (int i = bodyGenome.addonTorqueEffector1DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonTorqueEffector1DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonTorqueEffector1DList[i].maxTorque[0] = MutateBodyFloatMult(bodyGenome.addonTorqueEffector1DList[i].maxTorque[0], 0.01f, 100f);
                }
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonTorqueEffector1DList[i].axis[0] = MutateBodyVector3Normalized(bodyGenome.addonTorqueEffector1DList[i].axis[0]);
                }
            }            
        }
        for (int i = bodyGenome.addonTorqueEffector3DList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonTorqueEffector3DList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonTorqueEffector3DList[i].maxTorque[0] = MutateBodyFloatMult(bodyGenome.addonTorqueEffector3DList[i].maxTorque[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonMouthBasicList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonMouthBasicList.RemoveAt(i);
            }
            else {

            }
        }
        for (int i = bodyGenome.addonNoiseMakerBasicList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonNoiseMakerBasicList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonNoiseMakerBasicList[i].amplitude[0] = MutateBodyFloatMult(bodyGenome.addonNoiseMakerBasicList[i].amplitude[0], 0.01f, 100f);
                }
            }            
        }
        for (int i = bodyGenome.addonStickyList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonStickyList.RemoveAt(i);
            }
            else {

            }
        }
        for (int i = bodyGenome.addonWeaponBasicList.Count - 1; i >= 0; i--) {
            if (CheckForMutation(removeAddonChance * bodyMutationBlastModifier)) {
                // DELETE THIS ADD-ON!!!
                bodyGenome.addonWeaponBasicList.RemoveAt(i);
            }
            else {
                if (CheckForMutation(addonSettingsChance * bodyMutationBlastModifier)) {
                    bodyGenome.addonWeaponBasicList[i].strength[0] = MutateBodyFloatMult(bodyGenome.addonWeaponBasicList[i].strength[0], 0.01f, 100f);
                }
            }            
        }

        // UPDATE BRAIN/BODY:
        brainGenome.AdjustBrainAfterBodyChange(ref bodyGenome);
    }
    public Population BreedPopulation(ref Population sourcePopulation, int currentGeneration) {

        #region Pre-Crossover, Figuring out how many agents to breed etc.
        int LifetimeGeneration = currentGeneration + sourcePopulation.trainingGenerations;
        int totalNumWeightMutations = 0;
        //float totalWeightChangeValue = 0f;
        // go through species list and adjust fitness
        List<SpeciesBreedingPool> childSpeciesPoolsList = new List<SpeciesBreedingPool>(); // will hold agents in an internal list to facilitate crossover
        
        for (int s = 0; s < sourcePopulation.speciesBreedingPoolList.Count; s++) {            
            SpeciesBreedingPool newChildSpeciesPool = new SpeciesBreedingPool(sourcePopulation.speciesBreedingPoolList[s].templateGenome, sourcePopulation.speciesBreedingPoolList[s].speciesID);  // create Breeding Pools
            // copies the existing breeding pools but leaves their agentLists empty for future children
            childSpeciesPoolsList.Add(newChildSpeciesPool);            // Add to list of pools          
        }

        sourcePopulation.RankAgentArray(); // based on modified species fitness score, so compensated for species sizes
        
        Agent[] newAgentArray = new Agent[sourcePopulation.masterAgentArray.Length];

        // Calculate total fitness score:
        float totalScore = 0f;
        if (survivalByRaffle) {
            for (int a = 0; a < sourcePopulation.populationMaxSize; a++) { // iterate through all agents
                totalScore += sourcePopulation.masterAgentArray[a].fitnessScoreSpecies;
            }
        }

        // Figure out How many Agents survive
        int numSurvivors = Mathf.RoundToInt(survivalRate * (float)sourcePopulation.populationMaxSize);
        //Depending on method, one at a time, select an Agent to survive until the max Number is reached
        int newChildIndex = 0;
        // For ( num Agents ) {
        for (int i = 0; i < numSurvivors; i++) {
            // If survival is by fitness score ranking:
            if (survivalByRank) {
                // Pop should already be ranked, so just traverse from top (best) to bottom (worst)
                newAgentArray[newChildIndex] = sourcePopulation.masterAgentArray[newChildIndex];
                SpeciesBreedingPool survivingAgentBreedingPool = sourcePopulation.GetBreedingPoolByID(childSpeciesPoolsList, newAgentArray[newChildIndex].speciesID);
                survivingAgentBreedingPool.AddNewAgent(newAgentArray[newChildIndex]);
                //SortNewAgentIntoSpecies(newAgentArray[newChildIndex], childSpeciesList); // sorts this surviving agent into next generation's species'
                newChildIndex++;
            }
            // if survival is completely random, as a control:
            if (survivalStochastic) {
                int randomAgent = UnityEngine.Random.Range(0, numSurvivors - 1);
                // Set next newChild slot to a randomly-chosen agent within the survivor faction -- change to full random?
                newAgentArray[newChildIndex] = sourcePopulation.masterAgentArray[randomAgent];
                SpeciesBreedingPool survivingAgentBreedingPool = sourcePopulation.GetBreedingPoolByID(childSpeciesPoolsList, newAgentArray[newChildIndex].speciesID);
                survivingAgentBreedingPool.AddNewAgent(newAgentArray[newChildIndex]);
                //SortNewAgentIntoSpecies(newAgentArray[newChildIndex], childSpeciesList); // sorts this surviving agent into next generation's species'
                newChildIndex++;
            }
            // if survival is based on a fitness lottery:
            if (survivalByRaffle) {  // Try when Fitness is normalized from 0-1
                float randomSlicePosition = UnityEngine.Random.Range(0f, totalScore);
                float accumulatedFitness = 0f;
                for (int a = 0; a < sourcePopulation.populationMaxSize; a++) { // iterate through all agents
                    accumulatedFitness += sourcePopulation.masterAgentArray[a].fitnessScoreSpecies;
                    // if accum fitness is on slicePosition, copy this Agent
                    //Debug.Log("NumSurvivors: " + numSurvivors.ToString() + ", Surviving Agent " + a.ToString() + ": AccumFitness: " + accumulatedFitness.ToString() + ", RafflePos: " + randomSlicePosition.ToString() + ", TotalScore: " + totalScore.ToString() + ", newChildIndex: " + newChildIndex.ToString());
                    if (accumulatedFitness >= randomSlicePosition) {
                        newAgentArray[newChildIndex] = sourcePopulation.masterAgentArray[a]; // add to next gen's list of agents
                        SpeciesBreedingPool survivingAgentBreedingPool = sourcePopulation.GetBreedingPoolByID(childSpeciesPoolsList, newAgentArray[newChildIndex].speciesID);
                        survivingAgentBreedingPool.AddNewAgent(newAgentArray[newChildIndex]);
                        //SortNewAgentIntoSpecies(newAgentArray[newChildIndex], childSpeciesList); // sorts this surviving agent into next generation's species'
                        newChildIndex++;
                        break;
                    }
                }
            }
        }

        // Figure out how many new agents must be created to fill up the new population:
        int numNewChildAgents = sourcePopulation.populationMaxSize - numSurvivors;
        int numEligibleBreederAgents = Mathf.RoundToInt(breedingRate * (float)sourcePopulation.populationMaxSize);
        int currentRankIndex = 0;

        // Once the agents are ranked, trim the BreedingPools of agents that didn't make the cut for mating:
        if(useSpeciation) {
            for (int s = 0; s < sourcePopulation.speciesBreedingPoolList.Count; s++) {
                int index = 0;
                int failsafe = 0;
                int numAgents = sourcePopulation.speciesBreedingPoolList[s].agentList.Count;
                while (index < numAgents) {
                    if (index < sourcePopulation.speciesBreedingPoolList[s].agentList.Count) {
                        if (sourcePopulation.speciesBreedingPoolList[s].agentList[index].fitnessRank >= numEligibleBreederAgents) {
                            sourcePopulation.speciesBreedingPoolList[s].agentList.RemoveAt(index);
                        }
                        else {
                            index++;
                        }
                    }
                    else {
                        break;
                    }
                    failsafe++;
                    if (failsafe > 500) {
                        Debug.Log("INFINITE LOOP! hit failsafe 500 iters -- Trimming BreedingPools!");
                        break;
                    }
                }
                //Debug.Log("BreedPopulation -- TRIMSpeciesPool# " + s.ToString() + ", id: " + sourcePopulation.speciesBreedingPoolList[s].speciesID.ToString() + ", Count: " + sourcePopulation.speciesBreedingPoolList[s].agentList.Count.ToString());
                //
            }
        }        

        float totalScoreBreeders = 0f;
        if (breedingByRaffle) {  // calculate total fitness scores to determine lottery weights
            for (int a = 0; a < numEligibleBreederAgents; a++) { // iterate through all agents
                totalScoreBreeders += sourcePopulation.masterAgentArray[a].fitnessScoreSpecies;
            }
        }
        #endregion

        // Iterate over numAgentsToCreate :
        int newChildrenCreated = 0;
        while (newChildrenCreated < numNewChildAgents) {
            //		Find how many parents random number btw min/max:
            int numParentAgents = 2; // UnityEngine.Random.Range(minNumParents, maxNumParents);
            int numChildAgents = 1; // defaults to one child, but:
            if (numNewChildAgents - newChildrenCreated >= 2) {  // room for two more!
                numChildAgents = 2;                
            }

            Agent[] parentAgentsArray = new Agent[numParentAgents]; // assume 2 for now? yes, so far....
            
            List<GeneNodeNEAT>[] parentNodeListArray = new List<GeneNodeNEAT>[numParentAgents];
            List<GeneLinkNEAT>[] parentLinkListArray = new List<GeneLinkNEAT>[numParentAgents];

            Agent firstParentAgent = SelectAgentFromPopForBreeding(sourcePopulation, numEligibleBreederAgents, ref currentRankIndex);
            parentAgentsArray[0] = firstParentAgent;
            List<GeneNodeNEAT> firstParentNodeList = firstParentAgent.brainGenome.nodeNEATList;
            List<GeneLinkNEAT> firstParentLinkList = firstParentAgent.brainGenome.linkNEATList;
            //List<GeneNodeNEAT> firstParentNodeList = new List<GeneNodeNEAT>();
            //List<GeneLinkNEAT> firstParentLinkList = new List<GeneLinkNEAT>();
            //firstParentNodeList = firstParentAgent.brainGenome.nodeNEATList;
            //firstParentLinkList = firstParentAgent.brainGenome.linkNEATList;
            parentNodeListArray[0] = firstParentNodeList;
            parentLinkListArray[0] = firstParentLinkList;

            Agent secondParentAgent;
            SpeciesBreedingPool parentAgentBreedingPool = sourcePopulation.GetBreedingPoolByID(sourcePopulation.speciesBreedingPoolList, firstParentAgent.speciesID);
            if (useSpeciation) {
                //parentAgentBreedingPool
                float randBreedOutsideSpecies = UnityEngine.Random.Range(0f, 1f);
                if (randBreedOutsideSpecies < interspeciesBreedingRate) { // Attempts to Found a new species
                                                                          // allowed to breed outside its own species:
                    secondParentAgent = SelectAgentFromPopForBreeding(sourcePopulation, numEligibleBreederAgents, ref currentRankIndex);
                }
                else {
                    // Selects mate only from within its own species:
                    secondParentAgent = SelectAgentFromPoolForBreeding(parentAgentBreedingPool);
                }
            }
            else {
                secondParentAgent = SelectAgentFromPopForBreeding(sourcePopulation, numEligibleBreederAgents, ref currentRankIndex);
            }           
            
            parentAgentsArray[1] = secondParentAgent;
            List<GeneNodeNEAT> secondParentNodeList = secondParentAgent.brainGenome.nodeNEATList;
            List<GeneLinkNEAT> secondParentLinkList = secondParentAgent.brainGenome.linkNEATList;
            //List<GeneNodeNEAT> secondParentNodeList = new List<GeneNodeNEAT>();
            //List<GeneLinkNEAT> secondParentLinkList = new List<GeneLinkNEAT>();
            //secondParentNodeList = secondParentAgent.brainGenome.nodeNEATList;
            //secondParentLinkList = secondParentAgent.brainGenome.linkNEATList;
            parentNodeListArray[1] = secondParentNodeList;
            parentLinkListArray[1] = secondParentLinkList;
           
            //		Iterate over ChildArray.Length :  // how many newAgents created
            for (int c = 0; c < numChildAgents; c++) { // for number of child Agents in floatArray[][]:
                Agent newChildAgent = new Agent();
                
                List<GeneNodeNEAT> childNodeList = new List<GeneNodeNEAT>();
                List<GeneLinkNEAT> childLinkList = new List<GeneLinkNEAT>();
                
                GenomeNEAT childBrainGenome = new GenomeNEAT();
                childBrainGenome.nodeNEATList = childNodeList;
                childBrainGenome.linkNEATList = childLinkList;

                int numEnabledLinkGenes = 0;

                if (useCrossover) {                    
                    int nextLinkInnoA = 0;
                    int nextLinkInnoB = 0;
                    //int nextBodyNodeInno = 0;
                    //int nextBodyAddonInno = 0;

                    int failsafeMax = 500;
                    int failsafe = 0;
                    int parentListIndexA = 0;
                    int parentListIndexB = 0;
                    //int parentBodyNodeIndex = 0;
                    bool parentDoneA = false;
                    bool parentDoneB = false;
                    bool endReached = false;

                    int moreFitParent = 0;  // which parent is more Fit
                    if (parentAgentsArray[0].fitnessScoreSpecies < parentAgentsArray[1].fitnessScoreSpecies) {
                        moreFitParent = 1;
                    }
                    else if (parentAgentsArray[0].fitnessScoreSpecies == parentAgentsArray[1].fitnessScoreSpecies) {
                        moreFitParent = Mathf.RoundToInt(UnityEngine.Random.Range(0f, 1f));
                    }

                    //  MATCH UP Links between both agents, if they have a gene with matching Inno#, then mixing can occur                    
                    while (!endReached) {
                        failsafe++;
                        if(failsafe > failsafeMax) {
                            Debug.Log("failsafe reached!");
                            break;
                        }
                        // inno# of next links:
                        if(parentLinkListArray[0].Count > parentListIndexA) {
                            nextLinkInnoA = parentLinkListArray[0][parentListIndexA].innov;
                        }
                        else {
                            parentDoneA = true;
                        }
                        if (parentLinkListArray[1].Count > parentListIndexB) {
                            nextLinkInnoB = parentLinkListArray[1][parentListIndexB].innov;
                        }
                        else {
                            parentDoneB = true;
                        }

                        int innoDelta = nextLinkInnoA - nextLinkInnoB;  // 0=match, neg= Aextra, pos= Bextra
                        if (parentDoneA && !parentDoneB) {
                            innoDelta = 1;
                        }
                        if (parentDoneB && !parentDoneA) {
                            innoDelta = -1;
                        }
                        if (parentDoneA && parentDoneB) {  // reached end of both parent's linkLists
                            endReached = true;
                            break;
                        }

                        if (innoDelta < 0) {  // Parent A has an earlier link mutation
                            //Debug.Log("newChildIndex: " + newChildIndex.ToString() + ", IndexA: " + parentListIndexA.ToString() + ", IndexB: " + parentListIndexB.ToString() + ", innoDelta < 0 (" + innoDelta.ToString() + ") --  moreFitP: " + moreFitParent.ToString() + ", nextLinkInnoA: " + nextLinkInnoA.ToString() + ", nextLinkInnoB: " + nextLinkInnoB.ToString());
                            if (moreFitParent == 0) {  // Parent A is more fit:
                                GeneLinkNEAT newChildLink = new GeneLinkNEAT(parentLinkListArray[0][parentListIndexA].fromNodeID, parentLinkListArray[0][parentListIndexA].toNodeID, parentLinkListArray[0][parentListIndexA].weight, parentLinkListArray[0][parentListIndexA].enabled, parentLinkListArray[0][parentListIndexA].innov, parentLinkListArray[0][parentListIndexA].birthGen);
                                childLinkList.Add(newChildLink);
                                if (parentLinkListArray[0][parentListIndexA].enabled)
                                    numEnabledLinkGenes++;
                            }
                            else {
                                if(CheckForMutation(crossoverRandomLinkChance)) {  // was less fit parent, but still passed on a gene!:
                                    GeneLinkNEAT newChildLink = new GeneLinkNEAT(parentLinkListArray[0][parentListIndexA].fromNodeID, parentLinkListArray[0][parentListIndexA].toNodeID, parentLinkListArray[0][parentListIndexA].weight, parentLinkListArray[0][parentListIndexA].enabled, parentLinkListArray[0][parentListIndexA].innov, parentLinkListArray[0][parentListIndexA].birthGen);
                                    childLinkList.Add(newChildLink);
                                }
                            }
                            parentListIndexA++;
                        }
                        if (innoDelta > 0) {  // Parent B has an earlier link mutation
                            //Debug.Log("newChildIndex: " + newChildIndex.ToString() + ", IndexA: " + parentListIndexA.ToString() + ", IndexB: " + parentListIndexB.ToString() + ", innoDelta > 0 (" + innoDelta.ToString() + ") --  moreFitP: " + moreFitParent.ToString() + ", nextLinkInnoA: " + nextLinkInnoA.ToString() + ", nextLinkInnoB: " + nextLinkInnoB.ToString());
                            if (moreFitParent == 1) {  // Parent B is more fit:
                                GeneLinkNEAT newChildLink = new GeneLinkNEAT(parentLinkListArray[1][parentListIndexB].fromNodeID, parentLinkListArray[1][parentListIndexB].toNodeID, parentLinkListArray[1][parentListIndexB].weight, parentLinkListArray[1][parentListIndexB].enabled, parentLinkListArray[1][parentListIndexB].innov, parentLinkListArray[1][parentListIndexB].birthGen);
                                childLinkList.Add(newChildLink);
                                if (parentLinkListArray[1][parentListIndexB].enabled)
                                    numEnabledLinkGenes++;
                            }
                            else {
                                if (CheckForMutation(crossoverRandomLinkChance)) {  // was less fit parent, but still passed on a gene!:
                                    GeneLinkNEAT newChildLink = new GeneLinkNEAT(parentLinkListArray[1][parentListIndexB].fromNodeID, parentLinkListArray[1][parentListIndexB].toNodeID, parentLinkListArray[1][parentListIndexB].weight, parentLinkListArray[1][parentListIndexB].enabled, parentLinkListArray[1][parentListIndexB].innov, parentLinkListArray[1][parentListIndexB].birthGen);
                                    childLinkList.Add(newChildLink);
                                }
                            }
                            parentListIndexB++;
                        }
                        if (innoDelta == 0) {  // Match!
                            float randParentIndex = UnityEngine.Random.Range(0f, 1f);
                            float newWeightValue;
                            if (randParentIndex < 0.5) {
                                // ParentA wins:
                                newWeightValue = parentLinkListArray[0][parentListIndexA].weight;
                            }
                            else {  // ParentB wins:
                                newWeightValue = parentLinkListArray[1][parentListIndexB].weight;
                            }
                            //Debug.Log("newChildIndex: " + newChildIndex.ToString() + ", IndexA: " + parentListIndexA.ToString() + ", IndexB: " + parentListIndexB.ToString() + ", innoDelta == 0 (" + innoDelta.ToString() + ") --  moreFitP: " + moreFitParent.ToString() + ", nextLinkInnoA: " + nextLinkInnoA.ToString() + ", nextLinkInnoB: " + nextLinkInnoB.ToString() + ", randParent: " + randParentIndex.ToString() + ", weight: " + newWeightValue.ToString());
                            GeneLinkNEAT newChildLink = new GeneLinkNEAT(parentLinkListArray[0][parentListIndexA].fromNodeID, parentLinkListArray[0][parentListIndexA].toNodeID, newWeightValue, parentLinkListArray[0][parentListIndexA].enabled, parentLinkListArray[0][parentListIndexA].innov, parentLinkListArray[0][parentListIndexA].birthGen);
                            childLinkList.Add(newChildLink);
                            if (parentLinkListArray[0][parentListIndexA].enabled)
                                numEnabledLinkGenes++;

                            parentListIndexA++;
                            parentListIndexB++;
                        }

                    }
                    // once childLinkList is built -- use nodes of the moreFit parent:
                    for (int i = 0; i < parentNodeListArray[moreFitParent].Count; i++) { 
                        // iterate through all nodes in the parent List and copy them into fresh new geneNodes:
                        GeneNodeNEAT clonedNode = new GeneNodeNEAT(parentNodeListArray[moreFitParent][i].id, parentNodeListArray[moreFitParent][i].nodeType, parentNodeListArray[moreFitParent][i].activationFunction, parentNodeListArray[moreFitParent][i].sourceAddonInno, parentNodeListArray[moreFitParent][i].sourceAddonRecursionNum, false, parentNodeListArray[moreFitParent][i].sourceAddonChannelNum);
                        childNodeList.Add(clonedNode);
                    }

                    if (useMutation) {
                        // BODY MUTATION:
                        //PerformBodyMutation(ref childBodyGenome, ref childBrainGenome);
                        // NEED TO ADJUST BRAINS IF MUTATION CHANGES #NODES!!!!

                        // BRAIN MUTATION:
                        if (numEnabledLinkGenes < 1)
                            numEnabledLinkGenes = 1;
                        for (int k = 0; k < childLinkList.Count; k++) {
                            float mutateChance = mutationBlastModifier * masterMutationRate / (1f + (float)numEnabledLinkGenes * 0.15f);
                            if (LifetimeGeneration - childLinkList[k].birthGen < newLinkBonusDuration) {
                                float t = 1 - ((LifetimeGeneration - childLinkList[k].birthGen) / (float)newLinkBonusDuration);
                                // t=0 means age of gene is same as bonusDuration, t=1 means it is brand new
                                mutateChance = Mathf.Lerp(mutateChance, mutateChance * newLinkMutateBonus, t);
                            }
                            if (CheckForMutation(mutateChance)) {  // Weight Mutation!
                                //Debug.Log("Weight Mutation Link[" + k.ToString() + "] weight: " + childLinkList[k].weight.ToString() + ", mutate: " + MutateFloat(childLinkList[k].weight).ToString());
                                childLinkList[k].weight = MutateFloat(childLinkList[k].weight);
                                totalNumWeightMutations++;
                            }
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationRemoveLinkChance)) {
                            //Debug.Log("Remove Link Mutation Agent[" + newChildIndex.ToString() + "]");
                            childBrainGenome.RemoveRandomLink();
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationAddNodeChance)) {   // Adds a new node
                            //Debug.Log("Add Node Mutation Agent[" + newChildIndex.ToString() + "]");
                            childBrainGenome.AddNewRandomNode(LifetimeGeneration, GetNextAddonInnov());
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationRemoveNodeChance)) {   // Adds a new node
                            //Debug.Log("Add Node Mutation Agent[" + newChildIndex.ToString() + "]");
                            childBrainGenome.RemoveRandomNode();
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationAddLinkChance)) { // Adds new connection
                            //Debug.Log("Add Link Mutation Agent[" + newChildIndex.ToString() + "]");
                            if (CheckForMutation(existingNetworkBias)) {
                                childBrainGenome.AddNewExtraLink(existingFromNodeBias, LifetimeGeneration);
                            }
                            else {
                                childBrainGenome.AddNewRandomLink(LifetimeGeneration);
                            }
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationActivationFunctionChance)) {
                            TransferFunctions.TransferFunction newFunction;
                            int randIndex = Mathf.RoundToInt(UnityEngine.Random.Range(0f, childNodeList.Count - 1));
                            int randomTF = (int)UnityEngine.Random.Range(0f, 12f);

                            switch (randomTF) {
                                case 0:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 1:
                                    newFunction = TransferFunctions.TransferFunction.Linear;
                                    break;
                                case 2:
                                    newFunction = TransferFunctions.TransferFunction.Gaussian;
                                    break;
                                case 3:
                                    newFunction = TransferFunctions.TransferFunction.Abs;
                                    break;
                                case 4:
                                    newFunction = TransferFunctions.TransferFunction.Cos;
                                    break;
                                case 5:
                                    newFunction = TransferFunctions.TransferFunction.Sin;
                                    break;
                                case 6:
                                    newFunction = TransferFunctions.TransferFunction.Tan;
                                    break;
                                case 7:
                                    newFunction = TransferFunctions.TransferFunction.Square;
                                    break;
                                case 8:
                                    newFunction = TransferFunctions.TransferFunction.Threshold01;
                                    break;
                                case 9:
                                    newFunction = TransferFunctions.TransferFunction.ThresholdNegPos;
                                    break;
                                case 10:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 11:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 12:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                default:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                            }
                            if (childNodeList[randIndex].nodeType != GeneNodeNEAT.GeneNodeType.Out) {  // keep outputs -1 to 1 range
                                Debug.Log("ActivationFunction Mutation Node[" + randIndex.ToString() + "] prev: " + childNodeList[randIndex].activationFunction.ToString() + ", new: " + newFunction.ToString());
                                childNodeList[randIndex].activationFunction = newFunction;
                            }
                        }
                    }
                    else {
                        Debug.Log("Mutation Disabled!");
                    }

                    // THE BODY   ==========!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!======================================================================================
                    CritterGenome childBodyGenome = new CritterGenome();  // create new body genome for Child
                    // This creates the ROOT NODE!!!!
                    // Clone Nodes & Addons from more fit parent to create new child body genome
                    // crossover is on, so check for matching Nodes and Add-ons (based on Inno#'s) to determine when to mix Settings/Attributes:                    
                    // Iterate over the nodes of the more fit parent:
                    for (int i = 0; i < parentAgentsArray[moreFitParent].bodyGenome.CritterNodeList.Count; i++) {
                        int currentNodeInno = parentAgentsArray[moreFitParent].bodyGenome.CritterNodeList[i].innov;
                        if (i == 0) {  // if this is the ROOT NODE:
                            childBodyGenome.CritterNodeList[0].CopySettingsFromNode(parentAgentsArray[moreFitParent].bodyGenome.CritterNodeList[0]);
                            // The root node was already created during the Constructor method of the CritterGenome,
                            // ... so instead of creating a new one, just copy the settings
                        }
                        else {  // NOT the root node, proceed normally:
                                // Create new cloned node defaulted to the settings of the source( more-fit parent's) Node:
                            CritterNode clonedCritterNode = parentAgentsArray[moreFitParent].bodyGenome.CritterNodeList[i].CloneThisCritterNode();
                            
                            // Check other parent for same node:
                            for (int j = 0; j < parentAgentsArray[1 - moreFitParent].bodyGenome.CritterNodeList.Count; j++) {
                                if (parentAgentsArray[1 - moreFitParent].bodyGenome.CritterNodeList[j].innov == currentNodeInno) {
                                    // CROSSOVER NODE SETTINGS HERE!!!  ---- If random dice roll > 0.5, use less fit parent's settings, otherwise leave as default
                                    BodyCrossover(ref clonedCritterNode, parentAgentsArray[1 - moreFitParent].bodyGenome.CritterNodeList[j]);                                    
                                }
                            }
                            childBodyGenome.CritterNodeList.Add(clonedCritterNode);
                        }                        
                    }
                    // ADD-ONS!!!!!!!!!!!!!!!!!!!!!!
                    BreedCritterAddons(ref childBodyGenome, ref parentAgentsArray[moreFitParent].bodyGenome, ref parentAgentsArray[1 - moreFitParent].bodyGenome);
                    newChildAgent.bodyGenome = childBodyGenome;  // ?????
                    if (useMutation) {
                        // BODY MUTATION:
                        PerformBodyMutation(ref childBodyGenome, ref childBrainGenome);
                    }
                }
                else { // no crossover:                    
                    
                    //===============================================================================================
                    for (int i = 0; i < parentNodeListArray[0].Count; i++) {
                        // iterate through all nodes in the parent List and copy them into fresh new geneNodes:
                        GeneNodeNEAT clonedNode = new GeneNodeNEAT(parentNodeListArray[0][i].id, parentNodeListArray[0][i].nodeType, parentNodeListArray[0][i].activationFunction, parentNodeListArray[0][i].sourceAddonInno, parentNodeListArray[0][i].sourceAddonRecursionNum, false, parentNodeListArray[0][i].sourceAddonChannelNum);
                        childNodeList.Add(clonedNode);
                    }
                    for (int j = 0; j < parentLinkListArray[0].Count; j++) {
                        //same thing with connections
                        GeneLinkNEAT clonedLink = new GeneLinkNEAT(parentLinkListArray[0][j].fromNodeID, parentLinkListArray[0][j].toNodeID, parentLinkListArray[0][j].weight, parentLinkListArray[0][j].enabled, parentLinkListArray[0][j].innov, parentLinkListArray[0][j].birthGen);
                        childLinkList.Add(clonedLink);
                        if (parentLinkListArray[0][j].enabled)
                            numEnabledLinkGenes++;
                    }
                    // MUTATION:
                    if (useMutation) {
                        // BODY MUTATION:
                        //childBrainGenome.nodeNEATList = childNodeList
                        //PerformBodyMutation(ref childBodyGenome, ref childBrainGenome);

                        // BRAIN MUTATION:
                        if (numEnabledLinkGenes < 1)
                            numEnabledLinkGenes = 1;
                        for (int k = 0; k < childLinkList.Count; k++) {
                            float mutateChance = mutationBlastModifier * masterMutationRate / (1f + (float)numEnabledLinkGenes * 0.15f);
                            if (LifetimeGeneration - childLinkList[k].birthGen < newLinkBonusDuration) {
                                float t = 1 - ((LifetimeGeneration - childLinkList[k].birthGen) / (float)newLinkBonusDuration);
                                // t=0 means age of gene is same as bonusDuration, t=1 means it is brand new
                                mutateChance = Mathf.Lerp(mutateChance, mutateChance * newLinkMutateBonus, t);
                            }
                            if (CheckForMutation(mutateChance)) {  // Weight Mutation!
                                //Debug.Log("Weight Mutation Link[" + k.ToString() + "] weight: " + childLinkList[k].weight.ToString() + ", mutate: " + MutateFloat(childLinkList[k].weight).ToString());
                                childLinkList[k].weight = MutateFloat(childLinkList[k].weight);
                                totalNumWeightMutations++;
                            }
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationRemoveLinkChance)) {
                            //Debug.Log("Remove Link Mutation Agent[" + newChildIndex.ToString() + "]");
                            childBrainGenome.RemoveRandomLink();
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationAddNodeChance)) {   // Adds a new node
                            //Debug.Log("Add Node Mutation Agent[" + newChildIndex.ToString() + "]");
                            childBrainGenome.AddNewRandomNode(LifetimeGeneration, GetNextAddonInnov());
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationAddLinkChance)) { // Adds new connection
                            //Debug.Log("Add Link Mutation Agent[" + newChildIndex.ToString() + "]");
                            if(CheckForMutation(existingNetworkBias)) {
                                childBrainGenome.AddNewExtraLink(existingFromNodeBias, LifetimeGeneration);
                            }
                            else {
                                childBrainGenome.AddNewRandomLink(LifetimeGeneration);
                            }
                        }
                        if (CheckForMutation(mutationBlastModifier * mutationActivationFunctionChance)) {
                            TransferFunctions.TransferFunction newFunction;
                            int randIndex = Mathf.RoundToInt(UnityEngine.Random.Range(0f, childNodeList.Count - 1));
                            int randomTF = (int)UnityEngine.Random.Range(0f, 12f);

                            switch (randomTF) {
                                case 0:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 1:
                                    newFunction = TransferFunctions.TransferFunction.Linear;
                                    break;
                                case 2:
                                    newFunction = TransferFunctions.TransferFunction.Gaussian;
                                    break;
                                case 3:
                                    newFunction = TransferFunctions.TransferFunction.Abs;
                                    break;
                                case 4:
                                    newFunction = TransferFunctions.TransferFunction.Cos;
                                    break;
                                case 5:
                                    newFunction = TransferFunctions.TransferFunction.Sin;
                                    break;
                                case 6:
                                    newFunction = TransferFunctions.TransferFunction.Tan;
                                    break;
                                case 7:
                                    newFunction = TransferFunctions.TransferFunction.Square;
                                    break;
                                case 8:
                                    newFunction = TransferFunctions.TransferFunction.Threshold01;
                                    break;
                                case 9:
                                    newFunction = TransferFunctions.TransferFunction.ThresholdNegPos;
                                    break;
                                case 10:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 11:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                case 12:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                                default:
                                    newFunction = TransferFunctions.TransferFunction.RationalSigmoid;
                                    break;
                            }
                            if (childNodeList[randIndex].nodeType != GeneNodeNEAT.GeneNodeType.Out) {  // keep outputs -1 to 1 range
                                Debug.Log("ActivationFunction Mutation Node[" + randIndex.ToString() + "] prev: " + childNodeList[randIndex].activationFunction.ToString() + ", new: " + newFunction.ToString());
                                childNodeList[randIndex].activationFunction = newFunction;
                            }
                        }
                        //for (int t = 0; t < childNodeList.Count; t++) {
                            
                        //}
                    }
                    else {
                        Debug.Log("Mutation Disabled!");
                    }

                    //   THE BODY!!!!! ++++++++++++++++++++++================+++++++++++++++++++===============+++++++++++++++++++===================+++++++++++++++++==============
                    CritterGenome childBodyGenome = new CritterGenome();  // create new body genome for Child                 
                    // Iterate over the nodes of the more fit parent:
                    for (int i = 0; i < parentAgentsArray[0].bodyGenome.CritterNodeList.Count; i++) {
                        int currentNodeInno = parentAgentsArray[0].bodyGenome.CritterNodeList[i].innov;
                        if (i == 0) {  // if this is the ROOT NODE:
                            childBodyGenome.CritterNodeList[0].CopySettingsFromNode(parentAgentsArray[0].bodyGenome.CritterNodeList[0]);
                            // The root node was already created during the Constructor method of the CritterGenome,
                            // ... so instead of creating a new one, just copy the settings
                        }
                        else {  // NOT the root node, proceed normally:
                                // Create new cloned node defaulted to the settings of the source( more-fit parent's) Node:
                            CritterNode clonedCritterNode = parentAgentsArray[0].bodyGenome.CritterNodeList[i].CloneThisCritterNode();
                            childBodyGenome.CritterNodeList.Add(clonedCritterNode);
                        }
                    }
                    // ADD-ONS!!!!!!!!!!!!!!!!!!!!!!
                    BreedCritterAddons(ref childBodyGenome, ref parentAgentsArray[0].bodyGenome, ref parentAgentsArray[0].bodyGenome);
                    newChildAgent.bodyGenome = childBodyGenome;
                    if (useMutation) {
                        // BODY MUTATION:
                        PerformBodyMutation(ref childBodyGenome, ref childBrainGenome);
                    }
                }
                
                newChildAgent.brainGenome = childBrainGenome;
                //newChildAgent.brainGenome.nodeNEATList = childNodeList;
                //newChildAgent.brainGenome.linkNEATList = childLinkList;
                BrainNEAT childBrain = new BrainNEAT(newChildAgent.brainGenome);
                childBrain.BuildBrainNetwork();
                newChildAgent.brain = childBrain;
                //Debug.Log("NEW CHILD numNodes: " + newChildAgent.brainGenome.nodeNEATList.Count.ToString() + ", #Neurons: " + newChildAgent.brain.neuronList.Count.ToString());
                //newChildAgent.bodyGenome.PreBuildCritter(0.8f);
                // Species:
                if (useSpeciation) {
                    float randAdoption = UnityEngine.Random.Range(0f, 1f);
                    if (randAdoption < adoptionRate) { // Attempts to Found a new species
                        bool speciesGenomeMatch = false;
                        for (int s = 0; s < childSpeciesPoolsList.Count; s++) {
                            float geneticDistance = GenomeNEAT.MeasureGeneticDistance(newChildAgent.brainGenome, childSpeciesPoolsList[s].templateGenome, neuronWeight, linkWeight, weightWeight, normalizeExcess, normalizeDisjoint, normalizeLinkWeight);

                            if (geneticDistance < speciesSimilarityThreshold) {
                                speciesGenomeMatch = true;
                                //agent.speciesID = speciesBreedingPoolList[s].speciesID; // this is done inside the AddNewAgent method below v v v 
                                childSpeciesPoolsList[s].AddNewAgent(newChildAgent);
                                //Debug.Log(" NEW CHILD (" + newChildIndex.ToString() + ") SortAgentIntoBreedingPool dist: " + geneticDistance.ToString() + ", speciesIDs: " + newChildAgent.speciesID.ToString() + ", " + childSpeciesPoolsList[s].speciesID.ToString() + ", speciesCount: " + childSpeciesPoolsList[s].agentList.Count.ToString());
                                break;
                            }
                        }
                        if (!speciesGenomeMatch) {

                            SpeciesBreedingPool newSpeciesBreedingPool = new SpeciesBreedingPool(newChildAgent.brainGenome, sourcePopulation.GetNextSpeciesID()); // creates new speciesPool modeled on this agent's genome

                            newSpeciesBreedingPool.AddNewAgent(newChildAgent);  // add this agent to breeding pool
                            childSpeciesPoolsList.Add(newSpeciesBreedingPool);  // add new speciesPool to the population's list of all active species

                            //Debug.Log(" NEW CHILD (" + newChildIndex.ToString() + ") SortAgentIntoBreedingPool NO MATCH!!! -- creating new BreedingPool " + newSpeciesBreedingPool.speciesID.ToString() + ", newChildAgentSpeciesID: " + newChildAgent.speciesID.ToString());
                        }
                    }
                    else {  // joins parent species automatically:
                        SpeciesBreedingPool newSpeciesBreedingPool = sourcePopulation.GetBreedingPoolByID(childSpeciesPoolsList, parentAgentBreedingPool.speciesID);
                        newSpeciesBreedingPool.AddNewAgent(newChildAgent);  // add this agent to breeding pool
                                                                            //Debug.Log(" NEW CHILD (" + newChildIndex.ToString() + ") NO ADOPTION SortAgentIntoBreedingPool speciesIDs: " + newChildAgent.speciesID.ToString() + ", " + newSpeciesBreedingPool.speciesID.ToString() + ", speciesCount: " + newSpeciesBreedingPool.agentList.Count.ToString());
                    }
                }
                else {  // joins parent species automatically:
                    SpeciesBreedingPool newSpeciesBreedingPool = sourcePopulation.GetBreedingPoolByID(childSpeciesPoolsList, parentAgentBreedingPool.speciesID);
                    newSpeciesBreedingPool.AddNewAgent(newChildAgent);  // add this agent to breeding pool                                                                        
                }

                newChildAgent.parentFitnessScoreA = sourcePopulation.masterAgentArray[newChildIndex].fitnessScore;
                newAgentArray[newChildIndex] = newChildAgent;

                newChildIndex++;  // new child created!
                newChildrenCreated++;
            }
        }

        /*Debug.Log("Finished Crossover! childSpeciesPoolsList:");
        for (int i = 0; i < sourcePopulation.speciesBreedingPoolList.Count; i++) {
            string poolString = " Child Species ID: " + sourcePopulation.speciesBreedingPoolList[i].speciesID.ToString();
            for (int j = 0; j < sourcePopulation.speciesBreedingPoolList[i].agentList.Count; j++) {
                poolString += ", member# " + j.ToString() + ", species: " + sourcePopulation.speciesBreedingPoolList[i].agentList[j].speciesID.ToString() + ", fitRank: " + sourcePopulation.speciesBreedingPoolList[i].agentList[j].fitnessRank.ToString();
            }
            Debug.Log(poolString);
        }*/

        // Clear out extinct species:
        int listIndex = 0;
        for (int s = 0; s < childSpeciesPoolsList.Count; s++) {
            if (listIndex >= childSpeciesPoolsList.Count) {
                Debug.Log("end childSpeciesPoolsList " + childSpeciesPoolsList.Count.ToString() + ", index= " + listIndex.ToString());
                break;
            }
            else {
                if (childSpeciesPoolsList[listIndex].agentList.Count == 0) {  // if empty:
                    //Debug.Log("Species " + childSpeciesPoolsList[listIndex].speciesID.ToString() + " WENT EXTINCT!!! --- childSpeciesPoolsList[" + listIndex.ToString() + "] old Count: " + childSpeciesPoolsList.Count.ToString() + ", s: " + s.ToString());
                    childSpeciesPoolsList.RemoveAt(listIndex);
                    //s--;  // see if this works                    
                }
                else {
                    listIndex++;
                }
            }
        }
        
        Debug.Log("Finished Crossover! totalNumWeightMutations: " + totalNumWeightMutations.ToString() + ", mutationBlastModifier: " + mutationBlastModifier.ToString() + ", bodyMutationBlastModifier: " + bodyMutationBlastModifier.ToString() + ", LifetimeGeneration: " + LifetimeGeneration.ToString() + ", currentGeneration: " + currentGeneration.ToString() + ", sourcePopulation.trainingGenerations: " + sourcePopulation.trainingGenerations.ToString());
        sourcePopulation.masterAgentArray = newAgentArray;
        sourcePopulation.speciesBreedingPoolList = childSpeciesPoolsList;

        /*Debug.Log("Finished Crossover! sourcePopulation.speciesBreedingPoolList:");
        for (int i = 0; i < sourcePopulation.speciesBreedingPoolList.Count; i++) {
            string poolString = "New Species ID: " + sourcePopulation.speciesBreedingPoolList[i].speciesID.ToString();
            for (int j = 0; j < sourcePopulation.speciesBreedingPoolList[i].agentList.Count; j++) {
                poolString += ", member# " + j.ToString() + ", species: " + sourcePopulation.speciesBreedingPoolList[i].agentList[j].speciesID.ToString() + ", fitRank: " + sourcePopulation.speciesBreedingPoolList[i].agentList[j].fitnessRank.ToString();
            }
            Debug.Log(poolString);
        }*/

        return sourcePopulation;
    }
 public BrainNEAT(GenomeNEAT newGenome) {
     sourceGenome = newGenome;
     visitedNodes = new List<NeuronNEAT>();
     completedNodes = new List<NeuronNEAT>();
 }
 public void InitializeBrainFromGenome(GenomeNEAT newGenome) {
     sourceGenome = newGenome;
 }
    public GenomeNEAT InitializeBlankBrain(int numInputNodes, int numOutputNodes) {
        
        sourceGenome = new GenomeNEAT(numInputNodes, numOutputNodes); // create blank genome with no connections
        /*int numNewLinks = 0;
        int numNewNodes = 0;
        for(int i = 0; i < numNewLinks; i++) {
            sourceGenome.AddNewRandomLink(0);
        }
        for (int j = 0; j < numNewNodes; j++) {
            sourceGenome.AddNewRandomNode(0);
        }*/

        //Debug.Log("InitializeBlankBrain #nodes: " + sourceGenome.nodeNEATList.Count.ToString() + ", #links: " + sourceGenome.linkNEATList.Count.ToString());

        return sourceGenome;
    }
    public GenomeNEAT InitializeNewBrain(CritterGenome critterBodyGenome, int numHiddenNodes, float connectedness, bool randomWeights) {

        // OLD //sourceGenome = new GenomeNEAT(numInputNodes, numHiddenNodes, numOutputNodes); // create blank genome with no connections
        sourceGenome = new GenomeNEAT(critterBodyGenome, numHiddenNodes); // create blank genome with no connections
        sourceGenome.CreateInitialConnections(connectedness, randomWeights);

        //Debug.Log("InitializeBlankBrain #nodes: " + sourceGenome.nodeNEATList.Count.ToString() + ", #links: " + sourceGenome.linkNEATList.Count.ToString());

        return sourceGenome;
    }
示例#19
0
 public void InitializeBrainFromGenome(GenomeNEAT newGenome)
 {
     sourceGenome = newGenome;
 }
    public GenomeNEAT InitializeRandomBrain(int numInputNodes, int numOutputNodes) {
        
        sourceGenome = new GenomeNEAT(numInputNodes, numOutputNodes); // create blank genome with no connections
        sourceGenome.CreateMinimumRandomConnections(); // Add links to each output node froma  random input node

        return sourceGenome;
    }
示例#21
0
 public BrainNEAT(GenomeNEAT newGenome)
 {
     sourceGenome   = newGenome;
     visitedNodes   = new List <NeuronNEAT>();
     completedNodes = new List <NeuronNEAT>();
 }
示例#22
0
    public void ApplyTrainingModifierEffects(Trainer trainer)
    {
        int currentGen = trainer.PlayingCurGeneration;

        if (trainer.loadedTrainingSave != null)
        {
            currentGen += trainer.loadedTrainingSave.endGeneration;
        }
        //Debug.Log("ApplyTrainingModifierEffects  currentGen: " + currentGen.ToString());
        CrossoverManager crossoverManager = trainer.PlayerList[0].masterCupid;
        int numModifiers = activeTrainingModifierList.Count;

        crossoverManager.mutationBlastModifier     = 1f;
        crossoverManager.bodyMutationBlastModifier = 1f;
        if (numModifiers > 0)
        {
            for (int i = numModifiers - 1; i >= 0; i--)
            {
                float t = 0f;
                switch (activeTrainingModifierList[i].modifierType)
                {
                case TrainingModifier.TrainingModifierType.LinkExplosion:

                    // go through all agents and pump them up -- THIS WILL NEED IMPROVEMENTS!!!!
                    for (int a = 0; a < trainer.PlayerList[0].masterPopulation.masterAgentArray.Length; a++)
                    {
                        GenomeNEAT genome      = trainer.PlayerList[0].masterPopulation.masterAgentArray[a].brainGenome;
                        int        numNodes    = genome.nodeNEATList.Count;
                        int        numNewLinks = (int)((float)numNodes * activeTrainingModifierList[i].linksPerNode);
                        for (int n = 0; n < numNewLinks; n++)
                        {
                            genome.AddNewRandomLink(currentGen);
                        }
                        int numLinks    = genome.linkNEATList.Count;
                        int numNewNodes = (int)((float)numLinks * activeTrainingModifierList[i].nodesPerLink);
                        for (int b = 0; b < numNewNodes; b++)
                        {
                            genome.AddNewRandomNode(currentGen, crossoverManager.GetNextAddonInnov());
                        }
                    }
                    activeTrainingModifierList.RemoveAt(i);
                    break;

                case TrainingModifier.TrainingModifierType.MutationBlast:
                    t = ((float)currentGen - (float)activeTrainingModifierList[i].startGen) / (float)activeTrainingModifierList[i].duration;
                    crossoverManager = trainer.PlayerList[0].masterCupid;
                    if (t > 1f)
                    {
                        if (activeTrainingModifierList[i].liveForever)
                        {
                            t = 1f;
                        }
                        else
                        {
                            // Duration has expired, and the live forever flag if false, so remove this modifier
                            crossoverManager.mutationBlastModifier = 1f;
                            activeTrainingModifierList.RemoveAt(i);
                            break;
                        }
                    }
                    crossoverManager.mutationBlastModifier = Mathf.Lerp(1f, activeTrainingModifierList[i].minMultiplier, t);
                    break;

                case TrainingModifier.TrainingModifierType.BodyMutationBlast:
                    t = ((float)currentGen - (float)activeTrainingModifierList[i].startGen) / (float)activeTrainingModifierList[i].duration;
                    crossoverManager = trainer.PlayerList[0].masterCupid;
                    if (t > 1f)
                    {
                        if (activeTrainingModifierList[i].liveForever)
                        {
                            t = 1f;
                        }
                        else
                        {
                            // Duration has expired, and the live forever flag if false, so remove this modifier
                            crossoverManager.bodyMutationBlastModifier = 1f;
                            activeTrainingModifierList.RemoveAt(i);
                            break;
                        }
                    }
                    crossoverManager.bodyMutationBlastModifier = Mathf.Lerp(1f, activeTrainingModifierList[i].minMultiplier, t);
                    break;

                case TrainingModifier.TrainingModifierType.PruneBrain:
                    t = ((float)currentGen - (float)activeTrainingModifierList[i].startGen) / (float)activeTrainingModifierList[i].duration;
                    crossoverManager = trainer.PlayerList[0].masterCupid;
                    if (t > 1f)
                    {
                        t = 1f;
                    }
                    t = 1f - t;
                    crossoverManager.largeBrainPenalty        = activeTrainingModifierList[i].largeBrainPenalty * t;
                    crossoverManager.mutationRemoveLinkChance = activeTrainingModifierList[i].removeLinkChance * t;
                    crossoverManager.mutationRemoveNodeChance = activeTrainingModifierList[i].removeNodeChance * t;
                    break;

                case TrainingModifier.TrainingModifierType.TargetCone:

                    break;

                case TrainingModifier.TrainingModifierType.TargetForward:

                    break;

                case TrainingModifier.TrainingModifierType.TargetOmni:

                    break;

                case TrainingModifier.TrainingModifierType.VariableTrialTimes:
                    t = ((float)currentGen - (float)activeTrainingModifierList[i].startGen) / (float)activeTrainingModifierList[i].duration;
                    Trial trial = trainer.PlayerList[0].masterTrialsList[0];
                    if (t > 1f)
                    {
                        if (activeTrainingModifierList[i].liveForever)
                        {
                            t = 1f;
                        }
                        else
                        {
                            // Remove this modifier
                            activeTrainingModifierList.RemoveAt(i);
                        }
                    }
                    int minSteps = (int)Mathf.Lerp(activeTrainingModifierList[i].beginMinTime, activeTrainingModifierList[i].endMinTime, t);
                    int maxSteps = (int)Mathf.Lerp(activeTrainingModifierList[i].beginMaxTime, activeTrainingModifierList[i].endMaxTime, t);
                    trial.maxEvaluationTimeSteps = maxSteps;     // (int)UnityEngine.Random.Range(minSteps, maxSteps);
                    trial.minEvaluationTimeSteps = minSteps;
                    break;

                case TrainingModifier.TrainingModifierType.WideSearch:
                    t = ((float)currentGen - (float)activeTrainingModifierList[i].startGen) / (float)activeTrainingModifierList[i].duration;
                    crossoverManager = trainer.PlayerList[0].masterCupid;
                    if (t > 1f)
                    {
                        t = 1f;
                    }
                    t = 1f - t;
                    //crossoverManager.largeBrainPenalty = activeTrainingModifierList[i].largeBrainPenalty * t;
                    //crossoverManager.mutationRemoveLinkChance = activeTrainingModifierList[i].removeLinkChance * t;
                    //crossoverManager.mutationRemoveNodeChance = activeTrainingModifierList[i].removeNodeChance * t;
                    break;

                default:
                    Debug.Log("Modifier type not found!!! SWITCH DEFAULT CASE");
                    break;
                }
            }
        }
    }
 public SpeciesBreedingPool(GenomeNEAT genome, int id) {
     agentList = new List<Agent>();
     templateGenome = genome;
     speciesID = id;
     nextAgentIndex = 0;
 }
    public static float MeasureGeneticDistance(GenomeNEAT genomeA, GenomeNEAT genomeB, float neuronCoefficient, float linkCoefficient, float weightCoefficient, float normNeuron, float normLink, float normWeight) {
        
        int indexA = 0;
        int indexB = 0;

        float weightDeltaTotal = 0f;
        float maxGenes = Mathf.Max((float)genomeA.linkNEATList.Count, (float)genomeB.linkNEATList.Count);
        float matchingGenes = 0f;
        //float disjointGenes = 0f;
        //float excessGenes = 0f;
        float linkGenes = 0f;
        float neuronGenes = 0f;
        int matchingNeurons = 0;
        //float smallestWeightValue = 0f;
        float largestWeightDelta = 0f;
        

        // matching Links
        // matching nodes/neurons
        // matching activation functions?
        // difference in weights
        // difference in add-on settings?
        // -- NEED TO GO BY BODY GENOME?????

        // LINKS:::
        for(int i = 0; i < genomeA.linkNEATList.Count + genomeB.linkNEATList.Count; i++) {
            if(indexA < genomeA.linkNEATList.Count) {

                if (indexB < genomeB.linkNEATList.Count) { // both good

                    if (genomeA.linkNEATList[indexA].innov < genomeB.linkNEATList[indexB].innov) {
                        // disjoint A
                        linkGenes++;
                        indexA++;
                    }
                    else if (genomeA.linkNEATList[indexA].innov > genomeB.linkNEATList[indexB].innov) {
                        // disjoint B
                        linkGenes++;
                        indexB++;
                    }
                    else if (genomeA.linkNEATList[indexA].innov == genomeB.linkNEATList[indexB].innov) {
                        // match!
                        float weightDelta = Mathf.Abs(genomeA.linkNEATList[indexA].weight - genomeB.linkNEATList[indexB].weight);
                        weightDeltaTotal += weightDelta;
                        matchingGenes++;
                        //Debug.Log("!@$#!$#!@$#!@$# MeasureGeneticDistance! innov MATCHING GENE: weightA: " + genomeA.linkNEATList[indexA].weight.ToString() + ", weightB: " + genomeB.linkNEATList[indexB].weight.ToString());
                        indexA++;
                        indexB++;
                        
                        largestWeightDelta = Mathf.Max(largestWeightDelta, weightDelta);
                    }

                }
                else { // A is good, B is finished
                    linkGenes++;
                    indexA++;
                }
            }
            else { // A done
                if (indexB < genomeB.linkNEATList.Count) {  // A is finished, B is good
                    linkGenes++;
                    indexB++;

                }
                else { // both done
                    break;
                }
            }            
        }        
        for (int i = 0; i < genomeA.nodeNEATList.Count; i++) {
            Int3 nodeID = new Int3(genomeA.nodeNEATList[i].sourceAddonInno, genomeA.nodeNEATList[i].sourceAddonRecursionNum, genomeA.nodeNEATList[i].sourceAddonChannelNum);
            if(genomeB.GetNodeIndexFromInt3(nodeID) != -1) { // if genomeB has the same neuron:
                matchingNeurons++;
            }            
        }
        int totalNeurons = (genomeA.nodeNEATList.Count + genomeB.nodeNEATList.Count);
        int totalLinks = (genomeA.linkNEATList.Count + genomeB.linkNEATList.Count);
        if(totalNeurons > 0) {
            neuronGenes = Mathf.Lerp((float)((totalNeurons) - matchingNeurons * 2), (float)((totalNeurons) - matchingNeurons * 2) / (float)(totalNeurons), normNeuron); // get proportion of neurons that match between the two genomes, should be 0-1
        }      
        if(totalLinks > 0) {
            linkGenes = Mathf.Lerp(((float)(totalLinks) - matchingGenes * 2f), ((float)(totalLinks) - matchingGenes * 2f) / (float)(totalLinks), normLink);
        }
        //float weightSpan = largestWeightValue - smallestWeightValue;       

        float distance = 0f;
        if(true) {  // maxGenes > 0f
            //float excessComponent = excessCoefficient * Mathf.Lerp(excessGenes, excessGenes / maxGenes, normExcess);
            //float disjointComponent = disjointCoefficient * Mathf.Lerp(disjointGenes, disjointGenes / maxGenes, normDisjoint);
            //float weightComponent = weightCoefficient * Mathf.Lerp(weightDeltaTotal, weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f), normWeight);
            float totalPie = neuronCoefficient + linkCoefficient + weightCoefficient;
            float weightComponent = 0f;
            if(largestWeightDelta > 0f)
                weightComponent = Mathf.Lerp(weightDeltaTotal / largestWeightDelta, weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f) / largestWeightDelta, normWeight);
            // OLD //distance = excessComponent + disjointComponent + weightComponent;
            distance = neuronGenes * (neuronCoefficient / totalPie) + linkGenes * (linkCoefficient / totalPie) + weightComponent * (weightCoefficient / totalPie);
            //Debug.Log("MeasureGeneticDistance! neuronGenes: " + (neuronGenes).ToString() + " (" + genomeA.nodeNEATList.Count.ToString() + "," + genomeB.nodeNEATList.Count.ToString() + 
            //    "), linkGenes: " + (linkGenes).ToString() + " (" + genomeA.linkNEATList.Count.ToString() + "," + genomeB.linkNEATList.Count.ToString() +                
            //    "), weight: " + weightComponent.ToString() + ", largestWeightDelta: " + largestWeightDelta.ToString() + ", weightDeltaTotal: " + weightDeltaTotal.ToString() + "weightMatches: " + matchingGenes.ToString() +
            //    ", distance: " + distance.ToString());
            //Debug.Log("MeasureGeneticDistance! disjoint: " + (disjointGenes).ToString() + ", excess: " + (excessGenes).ToString() + ", weight: " + (weightDeltaTotal / Mathf.Max((float)matchingGenes, 1f)).ToString() + ", distance: " + distance.ToString());
        }
        // else stays 0f;
        return distance;
    }
示例#25
0
 public Species(Species baseSpecies)
 {
     id = baseSpecies.id;
     currentMemberCount = 0;
     templateGenome     = baseSpecies.templateGenome;
 }
	public override object Read(ES2Reader reader)
	{
		GenomeNEAT data = new GenomeNEAT();
		Read(reader, data);
		return data;
	}
示例#27
0
 public Species(Species baseSpecies) {
     id = baseSpecies.id;
     currentMemberCount = 0;
     templateGenome = baseSpecies.templateGenome;
 }