/// <summary> /// Creates a brain based on the genes of 2 parent brains. /// For each gene in parent1 we check if parent2 has a gene with the same innovation number, if this is the case we randomly choose a parent to get the gene from. /// If a gene only exists in one parent we add it no matter what. /// </summary> /// <param name="parent2Brain">The other parent.</param> /// <returns>Returns a child brain based on the genes of this brain and the passed brain.</returns> public Brain Crossover(Brain parent2Brain) { _childGenes.Clear(); List <ConnectionGene> parent2 = new List <ConnectionGene>(parent2Brain.ConnectionGenes); foreach (ConnectionGene gene in ConnectionGenes) { ConnectionGene geneToRemove = default; ConnectionGene geneToAdd = gene; ConnectionGene gene2 = parent2Brain.ConnectionGenes.SingleOrDefault(gen => gen.InnovationNumber == gene.InnovationNumber); if (gene2 != default) { geneToRemove = gene2; geneToAdd = TrainingRoom.Random.NextDouble() < 0.5 ? gene : gene2; } geneToAdd = geneToAdd.Clone(); if (geneToRemove != default) { parent2.Remove(geneToRemove); } if (!geneToAdd.Enabled && TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.EnableConnectionChance) { geneToAdd.Enabled = true; } _childGenes.Add(geneToAdd); } _childGenes.AddRange(parent2); return(new Brain(Guid.NewGuid(), TrainingRoom, _childGenes)); }
/// <summary> /// Creates an organism based on the genes of 2 parent organisms. /// For each gene in parent1 we check if parent2 has a gene with the same innovation number, if this is the case we randomly choose a parent to get the gene from. /// If a gene only exists in one parent we add it no matter what. /// </summary> /// <param name="parent2Organism">The other parent.</param> /// <param name="trainingRoomSettings">The training room settings.</param> /// <returns>Returns a child organism based on the genes of this organism and the passed organism.</returns> public Organism Crossover(Organism parent2Organism, TrainingRoomSettings trainingRoomSettings) { // Clears the temporary child genes list for re-use. _childGenes.Clear(); // Copies the organisms from parent2. List <ConnectionGene> parent2 = new List <ConnectionGene>(parent2Organism.ConnectionGenes); // Pre-generates a child id for creating connection genes. Guid childId = Guid.NewGuid(); foreach (ConnectionGene gene in ConnectionGenes) { // Prepares a gene to remove and add. ConnectionGene geneToRemove = default; ConnectionGene geneToAdd = gene; // Tries to find a connection gene based on the current connection gene in the parent, and if not found, gives a default. ConnectionGene gene2 = parent2Organism.ConnectionGenes.SingleOrDefault(gen => gen.InnovationNumber == gene.InnovationNumber); // If the gene is not a default connection gene. if (gene2 != default) { // Set it for removal. geneToRemove = gene2; // Depending on a random set the current gene or a random gene to add. geneToAdd = trainingRoomSettings.Random.NextDouble() < 0.5 ? gene : gene2; } // Clone the gene to add with the new id. geneToAdd = geneToAdd.Clone(childId); // If the gene to remove is not default, remove it from its parent. if (geneToRemove != default) { parent2.Remove(geneToRemove); } // If the gene to add is not enabled and a random is lower than the enable connection chance, enable the gene. if (!geneToAdd.Enabled && trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.EnableConnectionChance) { geneToAdd.Enabled = true; } // Add gene to the temporary child genes list. _childGenes.Add(geneToAdd); } // Add all remaining genes in the parent to the child genes. _childGenes.AddRange(parent2); // Return a new organism with all new genes. return(new Organism(childId, trainingRoomSettings, Generation + 1, _childGenes)); }