diff --git a/simulation_parameters/Constants.cs b/simulation_parameters/Constants.cs
index d92cf2f9445..997596b25ac 100644
--- a/simulation_parameters/Constants.cs
+++ b/simulation_parameters/Constants.cs
@@ -1186,7 +1186,7 @@ public static class Constants
// Mutation Variables
public const int MAX_VARIANTS_PER_MUTATION = 50;
- public const int MAX_VARIANTS_IN_MUTATIONS = 100;
+ public const int MAX_VARIANTS_IN_MUTATIONS = 250;
public const float MUTATION_BACTERIA_TO_EUKARYOTE = 0.01f;
public const float MUTATION_CREATION_RATE = 0.25f;
public const float MUTATION_NEW_ORGANELLE_CHANCE = 0.25f;
diff --git a/simulation_parameters/common/auto-evo_parameters.json b/simulation_parameters/common/auto-evo_parameters.json
index 22888e2274d..f7243ec49fb 100644
--- a/simulation_parameters/common/auto-evo_parameters.json
+++ b/simulation_parameters/common/auto-evo_parameters.json
@@ -1,5 +1,5 @@
{
- "MutationsPerSpecies": 3,
+ "MutationsPerSpecies": 5,
"MoveAttemptsPerSpecies": 8,
"StrictNicheCompetition": true
}
diff --git a/src/auto-evo/mutation_strategy/AddOrganelleAnywhere.cs b/src/auto-evo/mutation_strategy/AddOrganelleAnywhere.cs
index d11cdea73de..07c670a2e54 100644
--- a/src/auto-evo/mutation_strategy/AddOrganelleAnywhere.cs
+++ b/src/auto-evo/mutation_strategy/AddOrganelleAnywhere.cs
@@ -3,17 +3,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using static CommonMutationFunctions;
///
/// Adds a random, valid organelle to a valid position. Doesn't place multicellular or later organelles.
///
public class AddOrganelleAnywhere : IMutationStrategy
{
- private readonly CommonMutationFunctions.Direction direction;
+ private readonly Direction direction;
private readonly OrganelleDefinition[] allOrganelles;
- public AddOrganelleAnywhere(Func criteria, CommonMutationFunctions.Direction direction
- = CommonMutationFunctions.Direction.Neutral)
+ public AddOrganelleAnywhere(Func criteria, Direction direction = Direction.Neutral)
{
allOrganelles = SimulationParameters.Instance.GetAllOrganelles().Where(criteria).Where(IsOrganelleValid)
.ToArray();
@@ -26,15 +26,15 @@ public AddOrganelleAnywhere(Func criteria, CommonMuta
// Formatter and inspect code disagree here
// ReSharper disable InvokeAsExtensionMethod
public static AddOrganelleAnywhere ThatUseCompound(CompoundDefinition compound,
- CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
+ Direction direction = Direction.Neutral)
{
return new AddOrganelleAnywhere(
organelle => Enumerable.Any(organelle.RunnableProcesses, proc => proc.Process.Inputs.ContainsKey(compound)),
direction);
}
- public static AddOrganelleAnywhere ThatUseCompound(Compound compound, CommonMutationFunctions.Direction direction
- = CommonMutationFunctions.Direction.Neutral)
+ public static AddOrganelleAnywhere ThatUseCompound(Compound compound, Direction direction
+ = Direction.Neutral)
{
var compoundResolved = SimulationParameters.GetCompound(compound);
@@ -42,7 +42,7 @@ public static AddOrganelleAnywhere ThatUseCompound(Compound compound, CommonMuta
}
public static AddOrganelleAnywhere ThatCreateCompound(CompoundDefinition compound,
- CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
+ Direction direction = Direction.Neutral)
{
return new AddOrganelleAnywhere(organelle =>
Enumerable.Any(organelle.RunnableProcesses, proc => proc.Process.Outputs.ContainsKey(compound)),
@@ -50,7 +50,7 @@ public static AddOrganelleAnywhere ThatCreateCompound(CompoundDefinition compoun
}
public static AddOrganelleAnywhere ThatCreateCompound(Compound compound,
- CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
+ Direction direction = Direction.Neutral)
{
var compoundResolved = SimulationParameters.GetCompound(compound);
@@ -59,7 +59,7 @@ public static AddOrganelleAnywhere ThatCreateCompound(Compound compound,
public static AddOrganelleAnywhere ThatConvertBetweenCompounds(CompoundDefinition fromCompound,
CompoundDefinition toCompound,
- CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
+ Direction direction = Direction.Neutral)
{
return new AddOrganelleAnywhere(organelle => Enumerable.Any(organelle.RunnableProcesses, proc =>
proc.Process.Inputs.ContainsKey(fromCompound) &&
@@ -69,7 +69,7 @@ public static AddOrganelleAnywhere ThatConvertBetweenCompounds(CompoundDefinitio
// ReSharper restore InvokeAsExtensionMethod
public static AddOrganelleAnywhere ThatConvertBetweenCompounds(Compound fromCompound, Compound toCompound,
- CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
+ Direction direction = Direction.Neutral)
{
var fromCompoundResolved = SimulationParameters.GetCompound(fromCompound);
var toCompoundResolved = SimulationParameters.GetCompound(toCompound);
@@ -77,7 +77,7 @@ public static AddOrganelleAnywhere ThatConvertBetweenCompounds(Compound fromComp
return ThatConvertBetweenCompounds(fromCompoundResolved, toCompoundResolved, direction);
}
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
// If a cheaper organelle gets added, this will need to be updated
@@ -93,7 +93,7 @@ public static AddOrganelleAnywhere ThatConvertBetweenCompounds(Compound fromComp
var organelles = allOrganelles.OrderBy(_ => random.Next())
.Take(Constants.AUTO_EVO_ORGANELLE_ADD_ATTEMPTS);
- var mutated = new List>();
+ var mutated = new List();
// TODO: reuse this memory somehow
var workMemory1 = new List();
@@ -119,10 +119,10 @@ public static AddOrganelleAnywhere ThatConvertBetweenCompounds(Compound fromComp
// In the rare case that adding the organelle fails, this can skip adding it to be tested as the species
// is not any different
- if (CommonMutationFunctions.AddOrganelle(organelle, direction, newSpecies, workMemory1, workMemory2,
+ if (AddOrganelle(organelle, direction, newSpecies, workMemory1, workMemory2,
workMemory3, random))
{
- mutated.Add(Tuple.Create(newSpecies, mp - organelle.MPCost));
+ mutated.Add(new Mutant(newSpecies, mp - organelle.MPCost));
}
}
diff --git a/src/auto-evo/mutation_strategy/ChangeBehaviorScore.cs b/src/auto-evo/mutation_strategy/ChangeBehaviorScore.cs
index 3c1f13b497e..b6f4df7fe28 100644
--- a/src/auto-evo/mutation_strategy/ChangeBehaviorScore.cs
+++ b/src/auto-evo/mutation_strategy/ChangeBehaviorScore.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using static CommonMutationFunctions;
public class ChangeBehaviorScore : IMutationStrategy
{
@@ -26,7 +27,7 @@ public enum BehaviorAttribute
// As it cost no MP the mutation code could just repeat this forever
public bool Repeatable => false;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
// TODO: Make random something passed in
@@ -61,6 +62,6 @@ public enum BehaviorAttribute
break;
}
- return [Tuple.Create(newSpecies, mp)];
+ return [new Mutant(newSpecies, mp)];
}
}
diff --git a/src/auto-evo/mutation_strategy/ChangeMembraneRigidity.cs b/src/auto-evo/mutation_strategy/ChangeMembraneRigidity.cs
index 044206a6905..2345e7d4037 100644
--- a/src/auto-evo/mutation_strategy/ChangeMembraneRigidity.cs
+++ b/src/auto-evo/mutation_strategy/ChangeMembraneRigidity.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using static CommonMutationFunctions;
public class ChangeMembraneRigidity : IMutationStrategy
{
@@ -14,7 +15,7 @@ public ChangeMembraneRigidity(bool lower)
public bool Repeatable => true;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
const float change = Constants.AUTO_EVO_MUTATION_RIGIDITY_STEP;
@@ -42,6 +43,6 @@ public ChangeMembraneRigidity(bool lower)
newSpecies.MembraneRigidity = Math.Clamp(newSpecies.MembraneRigidity, -1, 1);
- return [Tuple.Create(newSpecies, mp - mpCost)];
+ return [new Mutant(newSpecies, mp - mpCost)];
}
}
diff --git a/src/auto-evo/mutation_strategy/ChangeMembraneType.cs b/src/auto-evo/mutation_strategy/ChangeMembraneType.cs
index e4e403c7afa..818d6bc8b90 100644
--- a/src/auto-evo/mutation_strategy/ChangeMembraneType.cs
+++ b/src/auto-evo/mutation_strategy/ChangeMembraneType.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using static CommonMutationFunctions;
public class ChangeMembraneType : IMutationStrategy
{
@@ -14,7 +15,7 @@ public ChangeMembraneType(string membraneType)
public bool Repeatable => false;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (baseSpecies.MembraneType == membraneType)
@@ -27,6 +28,6 @@ public ChangeMembraneType(string membraneType)
newSpecies.MembraneType = membraneType;
- return [Tuple.Create(newSpecies, mp - membraneType.EditorCost)];
+ return [new Mutant(newSpecies, mp - membraneType.EditorCost)];
}
}
diff --git a/src/auto-evo/mutation_strategy/IMutationStrategy.cs b/src/auto-evo/mutation_strategy/IMutationStrategy.cs
index 4d5746594f9..8d6144cd20a 100644
--- a/src/auto-evo/mutation_strategy/IMutationStrategy.cs
+++ b/src/auto-evo/mutation_strategy/IMutationStrategy.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
+using static CommonMutationFunctions;
public interface IMutationStrategy
where T : Species
@@ -27,6 +28,6 @@ public interface IMutationStrategy
/// List of mutated species, null if no possible mutations are found (some strategies may return an empty list
/// instead in this case)
///
- public List>? MutationsOf(T baseSpecies, double mp, bool lawk, Random random,
+ public List? MutationsOf(T baseSpecies, double mp, bool lawk, Random random,
BiomeConditions biomeToConsider);
}
diff --git a/src/auto-evo/mutation_strategy/ModifyEnvironmentalTolerance.cs b/src/auto-evo/mutation_strategy/ModifyEnvironmentalTolerance.cs
index 715424c6034..3a900332cf0 100644
--- a/src/auto-evo/mutation_strategy/ModifyEnvironmentalTolerance.cs
+++ b/src/auto-evo/mutation_strategy/ModifyEnvironmentalTolerance.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using static CommonMutationFunctions;
public class ModifyEnvironmentalTolerance : IMutationStrategy
{
@@ -12,7 +13,7 @@ public class ModifyEnvironmentalTolerance : IMutationStrategy
///
public bool Repeatable => false;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (mp <= 0)
@@ -216,6 +217,6 @@ public class ModifyEnvironmentalTolerance : IMutationStrategy
newSpecies.Tolerances.SanityCheck();
#endif
- return [Tuple.Create(newSpecies, mp)];
+ return [new Mutant(newSpecies, mp)];
}
}
diff --git a/src/auto-evo/mutation_strategy/MoveOrganelleBack.cs b/src/auto-evo/mutation_strategy/MoveOrganelleBack.cs
index 40cd4342563..e23ffdf5e9d 100644
--- a/src/auto-evo/mutation_strategy/MoveOrganelleBack.cs
+++ b/src/auto-evo/mutation_strategy/MoveOrganelleBack.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using AutoEvo;
+using static CommonMutationFunctions;
internal class MoveOrganelleBack : IMutationStrategy
{
@@ -15,13 +16,13 @@ public MoveOrganelleBack(Func criteria)
public bool Repeatable => true;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (mp < Constants.ORGANELLE_MOVE_COST)
return null;
- var mutated = new List>();
+ var mutated = new List();
// TODO: try to avoid these temporary list allocations
var workMemory1 = new List();
@@ -34,13 +35,13 @@ public MoveOrganelleBack(Func criteria)
newSpecies.Organelles.Remove(organelle);
- if (CommonMutationFunctions.AddOrganelle(organelle.Definition, CommonMutationFunctions.Direction.Rear,
- newSpecies, workMemory1, workMemory2, workMemory3, random))
+ if (AddOrganelle(organelle.Definition, Direction.Rear, newSpecies, workMemory1, workMemory2, workMemory3,
+ random))
{
// Add mutation attempt only if was able to place the organelle
// TODO: maybe this should add the attempt anyway as this may act as a separate remove organelle step
// for things that cannot be moved?
- mutated.Add(Tuple.Create(newSpecies, mp - Constants.ORGANELLE_MOVE_COST));
+ mutated.Add(new Mutant(newSpecies, mp - Constants.ORGANELLE_MOVE_COST));
}
}
diff --git a/src/auto-evo/mutation_strategy/RemoveOrganelle.cs b/src/auto-evo/mutation_strategy/RemoveOrganelle.cs
index 9be5277590c..61a6ea028dd 100644
--- a/src/auto-evo/mutation_strategy/RemoveOrganelle.cs
+++ b/src/auto-evo/mutation_strategy/RemoveOrganelle.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using static CommonMutationFunctions;
public class RemoveOrganelle : IMutationStrategy
{
@@ -46,16 +47,19 @@ public static RemoveOrganelle ThatCreateCompound(Compound compound)
// ReSharper restore InvokeAsExtensionMethod
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (mp < Constants.ORGANELLE_REMOVE_COST)
return null;
+ if (baseSpecies.Organelles.Count <= 1)
+ return null;
+
var organelles = baseSpecies.Organelles.Where(x => Criteria(x.Definition))
.OrderBy(_ => random.Next()).Take(Constants.AUTO_EVO_ORGANELLE_REMOVE_ATTEMPTS);
- List>? mutated = null;
+ List? mutated = null;
MutationWorkMemory? workMemory = null;
@@ -88,10 +92,10 @@ public static RemoveOrganelle ThatCreateCompound(Compound compound)
newSpecies.Organelles.AddIfPossible(newOrganelle, workMemory.WorkingMemory1, workMemory.WorkingMemory2);
}
- CommonMutationFunctions.AttachIslandHexes(newSpecies.Organelles, workMemory);
+ AttachIslandHexes(newSpecies.Organelles, workMemory);
- mutated ??= new List>();
- mutated.Add(Tuple.Create(newSpecies, mp - Constants.ORGANELLE_REMOVE_COST));
+ mutated ??= new List();
+ mutated.Add(new Mutant(newSpecies, mp - Constants.ORGANELLE_REMOVE_COST));
}
return mutated;
diff --git a/src/auto-evo/mutation_strategy/UpgradeOrganelle.cs b/src/auto-evo/mutation_strategy/UpgradeOrganelle.cs
index d9298892b0e..3a06e9227cd 100644
--- a/src/auto-evo/mutation_strategy/UpgradeOrganelle.cs
+++ b/src/auto-evo/mutation_strategy/UpgradeOrganelle.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using AutoEvo;
+using static CommonMutationFunctions;
public class UpgradeOrganelle : IMutationStrategy
{
@@ -32,7 +33,7 @@ public UpgradeOrganelle(Func criteria, string upgrade
public bool Repeatable => false;
- public List>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
+ public List? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (allOrganelles.Count == 0)
@@ -58,7 +59,7 @@ public UpgradeOrganelle(Func criteria, string upgrade
if (validMutations)
{
- var mutated = new List>();
+ var mutated = new List();
var newSpecies = (MicrobeSpecies)baseSpecies.Clone();
@@ -84,7 +85,7 @@ public UpgradeOrganelle(Func criteria, string upgrade
}
}
- mutated.Add(new Tuple(newSpecies, mp));
+ mutated.Add(new Mutant(newSpecies, mp));
return mutated;
}
diff --git a/src/auto-evo/mutations/CommonMutationFunctions.cs b/src/auto-evo/mutations/CommonMutationFunctions.cs
index d68dcfb53b5..a1446117304 100644
--- a/src/auto-evo/mutations/CommonMutationFunctions.cs
+++ b/src/auto-evo/mutations/CommonMutationFunctions.cs
@@ -85,8 +85,8 @@ public static MicrobeSpecies GenerateRandomSpecies(MicrobeSpecies mutated, Patch
if (mutation == null)
break;
- mutated = mutation.Item1;
- mp -= mutation.Item2;
+ mutated = mutation.Species;
+ mp -= mutation.MP;
var oldColour = mutated.Colour;
@@ -444,4 +444,6 @@ private static Hex.HexSide[] SideTraversalOrder(Hex hex, Direction direction, Ra
return TraversalOrder8;
}
+
+ public record Mutant(MicrobeSpecies Species, double MP);
}
diff --git a/src/auto-evo/selection_pressure/CompoundConversionEfficiencyPressure.cs b/src/auto-evo/selection_pressure/CompoundConversionEfficiencyPressure.cs
index 71c09618a4a..b608353ab56 100644
--- a/src/auto-evo/selection_pressure/CompoundConversionEfficiencyPressure.cs
+++ b/src/auto-evo/selection_pressure/CompoundConversionEfficiencyPressure.cs
@@ -1,5 +1,6 @@
namespace AutoEvo;
+using System;
using Newtonsoft.Json;
[JSONDynamicTypeAllowed]
@@ -30,8 +31,8 @@ public class CompoundConversionEfficiencyPressure : SelectionPressure
public CompoundConversionEfficiencyPressure(Compound compound, Compound outCompound, float weight,
bool usedForSurvival) :
base(weight, [
- AddOrganelleAnywhere.ThatConvertBetweenCompounds(compound, outCompound),
RemoveOrganelle.ThatCreateCompound(outCompound),
+ AddOrganelleAnywhere.ThatConvertBetweenCompounds(compound, outCompound),
])
{
this.compound = compound;
@@ -54,7 +55,10 @@ public override float Score(Species species, Patch patch, SimulationCache cache)
// we need to factor in both conversion from source to output, and energy expenditure time
if (usedForSurvival)
- score /= cache.GetEnergyBalanceForSpecies(microbeSpecies, patch.Biome).TotalConsumptionStationary;
+ {
+ score /=
+ MathF.Sqrt(cache.GetEnergyBalanceForSpecies(microbeSpecies, patch.Biome).TotalConsumptionStationary);
+ }
return score;
}
diff --git a/src/auto-evo/steps/GenerateMiche.cs b/src/auto-evo/steps/GenerateMiche.cs
index 623a8b4ee20..872acfa66e8 100644
--- a/src/auto-evo/steps/GenerateMiche.cs
+++ b/src/auto-evo/steps/GenerateMiche.cs
@@ -148,19 +148,20 @@ public Miche GenerateMicheTree(AutoEvoGlobalCache globalCache)
generatedMiche.AddChild(tempMiche);
}
- var predationRoot = new Miche(globalCache.PredatorRoot);
- var predationGlucose = new Miche(globalCache.MinorGlucoseConversionEfficiencyPressure);
-
- // Heterotrophic Miches
- foreach (var possiblePrey in patch.SpeciesInPatch)
+ if (patch.SpeciesInPatch.Count > 1)
{
- predationGlucose.AddChild(new Miche(new PredationEffectivenessPressure(possiblePrey.Key, 1.0f)));
- }
+ var predationRoot = new Miche(globalCache.PredatorRoot);
+ var predationGlucose = new Miche(globalCache.MinorGlucoseConversionEfficiencyPressure);
- if (patch.SpeciesInPatch.Count > 1)
- predationRoot.AddChild(predationGlucose);
+ // Heterotrophic Miches
+ foreach (var possiblePrey in patch.SpeciesInPatch)
+ {
+ predationGlucose.AddChild(new Miche(new PredationEffectivenessPressure(possiblePrey.Key, 1.0f)));
+ }
- generatedMiche.AddChild(predationRoot);
+ predationRoot.AddChild(predationGlucose);
+ generatedMiche.AddChild(predationRoot);
+ }
return rootMiche;
}
diff --git a/src/auto-evo/steps/ModifyExistingSpecies.cs b/src/auto-evo/steps/ModifyExistingSpecies.cs
index 0788513611d..7b1084d71bb 100644
--- a/src/auto-evo/steps/ModifyExistingSpecies.cs
+++ b/src/auto-evo/steps/ModifyExistingSpecies.cs
@@ -5,6 +5,7 @@
using System.Linq;
using Godot;
using Xoshiro.PRNG64;
+using static CommonMutationFunctions;
///
/// Uses miches to create a mutation for an existing species. Also creates new species from existing ones as
@@ -34,21 +35,15 @@ public class ModifyExistingSpecies : IRunStep
private readonly List predatorCalculationMemory2 = new();
private readonly List predatorPressuresTemporary = new();
- private readonly List> tempMutationStrategies = new();
-
- // TODO: switch to named tuple elements
- private readonly List> temporaryMutations1 = new();
- private readonly List> temporaryMutations2 = new();
+ private readonly List temporaryMutations1 = new();
+ private readonly List temporaryMutations2 = new();
private readonly List lastGeneratedMutations = new();
- private readonly List currentTraversal = new();
-
- private readonly List temporaryPressures = new();
-
- private readonly List> temporaryResultForTopMutations = new();
+ private readonly Stack pressureStack = new();
private readonly MutationSorter mutationSorter;
+ private readonly GenerateMutationsWorkingMemory generateMutationsWorkingMemory = new();
private readonly Random random;
private readonly List mutationsToTry = new();
@@ -169,13 +164,21 @@ public bool RunStep(RunResults results)
// Then shuffle and take only as many mutations as we want to try
mutationsToTry.Shuffle(random);
- while (mutationsToTry.Count > TotalMutationsToTry)
+ // Rename and colour selected mutations
+ foreach (var mutation in mutationsToTry.Take(TotalMutationsToTry))
{
- mutationsToTry.RemoveAt(mutationsToTry.Count - 1);
- }
+ MutationLogicFunctions.NameNewMicrobeSpecies(mutation.MutatedSpecies, mutation.ParentSpecies);
+
+ var oldColour = mutation.MutatedSpecies.Colour;
+
+ var redShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
+ var greenShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
+ var blueShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
+
+ mutation.MutatedSpecies.Colour = new Color(Math.Clamp((float)(oldColour.R + redShift), 0, 1),
+ Math.Clamp((float)(oldColour.G + greenShift), 0, 1),
+ Math.Clamp((float)(oldColour.B + blueShift), 0, 1));
- foreach (var mutation in mutationsToTry)
- {
mutation.MutatedSpecies.OnEdited();
}
@@ -238,16 +241,17 @@ public bool RunStep(RunResults results)
return false;
}
- private static void PruneMutations(List> addResultsTo, MicrobeSpecies baseSpecies,
- List> mutated, Patch patch, SimulationCache cache,
- List selectionPressures)
+ private static void PruneMutations(List addResultsTo, MicrobeSpecies baseSpecies,
+ List mutated, Patch patch, SimulationCache cache,
+ Stack selectionPressures)
{
foreach (var potentialVariant in mutated)
{
var combinedScores = 0.0;
foreach (var pastPressure in selectionPressures)
{
- var newScore = cache.GetPressureScore(pastPressure, patch, potentialVariant.Item1);
+ // Caching a score for a species very likely to be pruned wastes memory
+ var newScore = pastPressure.Score(potentialVariant.Species, patch, cache);
var oldScore = cache.GetPressureScore(pastPressure, patch, baseSpecies);
// Break if the mutation fails a new pressure check
@@ -260,19 +264,21 @@ private static void PruneMutations(List> addResult
combinedScores += pastPressure.WeightedComparedScores(newScore, oldScore);
}
- if (combinedScores > 0)
+ // Not pruning species that don't affect the score can inject more
+ // variety into the species generated
+ if (combinedScores >= 0)
{
addResultsTo.Add(potentialVariant);
}
}
}
- private static void GetTopMutations(List> result,
- List> mutated, int amount, MutationSorter sorter)
+ private static void GetTopMutations(List result,
+ List mutated, int amount, MutationSorter sorter)
{
result.Clear();
- mutated.Sort(sorter);
+ mutated.Sort((a, b) => sorter.Compare(b, a));
foreach (var tuple in mutated)
{
@@ -284,62 +290,20 @@ private static void GetTopMutations(List> result,
private void GetMutationsForSpecies(MicrobeSpecies microbeSpecies)
{
- // The traversal end up being re-calculated quite many times, but this way we avoid quite a lot of memory
- // allocations
-
- foreach (var nonEmptyLeaf in nonEmptyLeafNodes)
- {
- if (nonEmptyLeaf.Occupant == microbeSpecies)
- continue;
-
- currentTraversal.Clear();
- nonEmptyLeaf.BackTraversal(currentTraversal);
-
- temporaryPressures.Clear();
- foreach (var traversalMiche in currentTraversal)
- {
- temporaryPressures.Add(traversalMiche.Pressure);
- }
-
- SpeciesDependentPressures(temporaryPressures, miche!, microbeSpecies);
-
- var variants = GenerateMutations(microbeSpecies,
- worldSettings.AutoEvoConfiguration.MutationsPerSpecies, temporaryPressures);
-
- foreach (var variant in variants)
- {
- mutationsToTry.Add(new Mutation(microbeSpecies, variant,
- RunResults.NewSpeciesType.SplitDueToMutation));
- }
- }
-
- // This section of the code tries to mutate species into unfilled miches
- // Not exactly realistic, but more diversity is more fun for the player
- // TODO: Make this an auto-evo option
- foreach (var emptyLeafNode in emptyLeafNodes)
- {
- currentTraversal.Clear();
- emptyLeafNode.BackTraversal(currentTraversal);
+ double totalMP = 100 * worldSettings.AIMutationMultiplier;
- temporaryPressures.Clear();
- foreach (var traversalMiche in currentTraversal)
- {
- temporaryPressures.Add(traversalMiche.Pressure);
- }
+ generateMutationsWorkingMemory.Clear();
+ pressureStack.Clear();
- SpeciesDependentPressures(temporaryPressures, miche!, microbeSpecies);
+ SpeciesDependentPressures(pressureStack, miche!, microbeSpecies);
- var variants = GenerateMutations(microbeSpecies,
- worldSettings.AutoEvoConfiguration.MutationsPerSpecies, temporaryPressures);
+ var inputSpecies = generateMutationsWorkingMemory.GetMutationsAtDepth(0);
+ inputSpecies.Add(new Mutant(microbeSpecies, totalMP));
- foreach (var variant in variants)
- {
- mutationsToTry.Add(new Mutation(microbeSpecies, variant, RunResults.NewSpeciesType.FillNiche));
- }
- }
+ GenerateMutations(microbeSpecies, miche!, 1);
}
- private void SpeciesDependentPressures(List dataReceiver, Miche targetMiche, Species species)
+ private void SpeciesDependentPressures(Stack dataReceiver, Miche targetMiche, Species species)
{
predatorPressuresTemporary.Clear();
@@ -348,7 +312,7 @@ private void SpeciesDependentPressures(List dataReceiver, Mic
foreach (var predator in predatorPressuresTemporary)
{
// TODO: Make that weight a constant
- dataReceiver.Add(new AvoidPredationSelectionPressure(predator, 5.0f));
+ dataReceiver.Push(new AvoidPredationSelectionPressure(predator, 5.0f));
}
}
@@ -382,89 +346,61 @@ private void PredatorsOf(List result, Miche micheTree, Species species)
}
///
- /// Returns a new list of all possible species that might emerge in response to the provided pressures,
- /// as well as a copy of the original species.
+ /// Adds a new list of all possible species that might emerge in response to the provided pressures,
+ /// as well as a copy of the original species to .
///
- /// List of viable variants and the provided species
- private List GenerateMutations(MicrobeSpecies baseSpecies, int amount,
- List selectionPressures)
+ private void GenerateMutations(MicrobeSpecies baseSpecies, Miche currentMiche, int depth)
{
- double totalMP = 100 * worldSettings.AIMutationMultiplier;
+ var inputSpecies = generateMutationsWorkingMemory.GetMutationsAtDepth(depth - 1);
- temporaryMutations1.Clear();
- temporaryMutations1.Add(Tuple.Create(baseSpecies, totalMP));
- var viableVariants = temporaryMutations1;
+ var outputSpecies = generateMutationsWorkingMemory.GetMutationsAtDepth(depth);
+ outputSpecies.Clear();
+ outputSpecies.AddRange(inputSpecies);
- // Auto-evo assumes that the order mutations are applied doesn't matter when it actually can affect
- // results quite a bit. Maybe they should have weights?
- tempMutationStrategies.Clear();
-
- // Collect and shuffle unique strategies
- foreach (var selectionPressure in selectionPressures)
- {
- foreach (var mutation in selectionPressure.Mutations)
- {
- if (!tempMutationStrategies.Contains(mutation))
- tempMutationStrategies.Add(mutation);
- }
- }
+ pressureStack.Push(currentMiche.Pressure);
+ mutationSorter.Setup(baseSpecies, pressureStack);
+ var mutations = currentMiche.Pressure.Mutations;
bool lawk = worldSettings.LAWK;
- mutationSorter.Setup(baseSpecies, selectionPressures);
-
- tempMutationStrategies.Shuffle(random);
-
- foreach (var mutationStrategy in tempMutationStrategies)
+ foreach (var mutationStrategy in mutations)
{
- var inputSpecies = viableVariants;
+ temporaryMutations1.Clear();
+ temporaryMutations1.AddRange(outputSpecies);
for (int i = 0; i < Constants.AUTO_EVO_MAX_MUTATION_RECURSIONS; ++i)
{
temporaryMutations2.Clear();
- foreach (var speciesTuple in inputSpecies)
+ foreach (var speciesTuple in temporaryMutations1)
{
// TODO: this seems like the longest part, so splitting this into multiple steps (maybe bundling
// up mutation strategies) would be good to have the auto-evo steps flow more smoothly
- var mutated = mutationStrategy.MutationsOf(speciesTuple.Item1, speciesTuple.Item2, lawk, random,
+ var mutated = mutationStrategy.MutationsOf(speciesTuple.Species, speciesTuple.MP, lawk, random,
patch.Biome);
if (mutated != null)
{
- PruneMutations(temporaryMutations2, speciesTuple.Item1, mutated, patch, cache,
- selectionPressures);
+ PruneMutations(temporaryMutations2, speciesTuple.Species, mutated, patch, cache,
+ pressureStack);
}
}
- // TODO: Make these a performance setting?
- if (temporaryMutations2.Count > Constants.MAX_VARIANTS_PER_MUTATION)
- {
- GetTopMutations(temporaryResultForTopMutations, temporaryMutations2,
- Constants.MAX_VARIANTS_PER_MUTATION / 2, mutationSorter);
-
- // TODO: switch to a set of rotating buffers to avoid memory allocations here
- inputSpecies = temporaryResultForTopMutations.ToList();
- }
- else
- {
- // TODO: switch to a set of rotating buffers to avoid memory allocations here
- inputSpecies = new List>();
- PruneMutations(inputSpecies, baseSpecies, temporaryMutations2, patch, cache, selectionPressures);
- }
+ temporaryMutations1.Clear();
+ PruneMutations(temporaryMutations1, baseSpecies, temporaryMutations2, patch, cache, pressureStack);
- viableVariants.AddRange(inputSpecies);
+ outputSpecies.AddRange(temporaryMutations1);
- if (viableVariants.Count > Constants.MAX_VARIANTS_IN_MUTATIONS)
+ if (outputSpecies.Count > Constants.MAX_VARIANTS_IN_MUTATIONS)
{
- GetTopMutations(temporaryResultForTopMutations, viableVariants,
+ GetTopMutations(temporaryMutations2, outputSpecies,
Constants.MAX_VARIANTS_IN_MUTATIONS / 2, mutationSorter);
- // TODO: switch to a set of rotating buffers to avoid memory allocations here
- viableVariants = temporaryResultForTopMutations.ToList();
+ outputSpecies.Clear();
+ outputSpecies.AddRange(temporaryMutations2);
}
- if (temporaryMutations2.Count == 0)
+ if (temporaryMutations1.Count == 0)
break;
if (!mutationStrategy.Repeatable)
@@ -472,54 +408,89 @@ private List GenerateMutations(MicrobeSpecies baseSpecies, int a
}
}
- lastGeneratedMutations.Clear();
-
- GetTopMutations(temporaryResultForTopMutations, viableVariants, amount, mutationSorter);
- foreach (var topMutation in temporaryResultForTopMutations)
+ if (currentMiche.IsLeafNode())
{
- lastGeneratedMutations.Add(topMutation.Item1);
- }
+ lastGeneratedMutations.Clear();
- // TODO: could maybe optimize things by only giving name and colour changes for mutations that are selected
- // in the end
- foreach (var variant in lastGeneratedMutations)
- {
- if (variant == baseSpecies)
- continue;
+ GetTopMutations(temporaryMutations1, outputSpecies, worldSettings.AutoEvoConfiguration.MutationsPerSpecies,
+ mutationSorter);
+ foreach (var topMutation in temporaryMutations1)
+ {
+ lastGeneratedMutations.Add(topMutation.Species);
+ }
- MutationLogicFunctions.NameNewMicrobeSpecies(variant, baseSpecies);
+ var resultType = (currentMiche.Occupant == baseSpecies) ?
+ RunResults.NewSpeciesType.SplitDueToMutation :
+ RunResults.NewSpeciesType.FillNiche;
- var oldColour = variant.Colour;
+ foreach (var species in lastGeneratedMutations)
+ {
+ mutationsToTry.Add(new Mutation(baseSpecies, species, resultType));
+ }
+ }
+ else
+ {
+ // Prune out any results that don't improve this branch
+ temporaryMutations1.Clear();
+ PruneMutations(temporaryMutations1, baseSpecies, outputSpecies, patch, cache, pressureStack);
+ outputSpecies.Clear();
+ outputSpecies.AddRange(temporaryMutations1);
- var redShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
- var greenShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
- var blueShift = (random.NextDouble() - 0.5f) * Constants.AUTO_EVO_COLOR_CHANGE_MAX_STEP;
+ if (outputSpecies.Count != 0)
+ {
+ foreach (var child in currentMiche.Children)
+ {
+ GenerateMutations(baseSpecies, child, depth + 1);
+ }
+ }
- variant.Colour = new Color(Math.Clamp((float)(oldColour.R + redShift), 0, 1),
- Math.Clamp((float)(oldColour.G + greenShift), 0, 1),
- Math.Clamp((float)(oldColour.B + blueShift), 0, 1));
+ pressureStack.Pop();
}
-
- return lastGeneratedMutations;
}
private record struct Mutation(MicrobeSpecies ParentSpecies, MicrobeSpecies MutatedSpecies,
RunResults.NewSpeciesType AddType);
- private class MutationSorter(Patch patch, SimulationCache cache) : IComparer>
+ ///
+ /// Working memory used to reduce memory allocations in .
+ ///
+ private class GenerateMutationsWorkingMemory
+ {
+ private readonly List> currentSpecies = new();
+
+ public List GetMutationsAtDepth(int depth)
+ {
+ while (currentSpecies.Count <= depth)
+ currentSpecies.Add(new List());
+
+ var result = currentSpecies[depth];
+
+ return result;
+ }
+
+ public void Clear()
+ {
+ foreach (var speciesList in currentSpecies)
+ {
+ speciesList.Clear();
+ }
+ }
+ }
+
+ private class MutationSorter(Patch patch, SimulationCache cache) : IComparer
{
// This isn't the cleanest but this class is just optimized for performance so if someone forgets to set up
// this then bad things will happen
- private List pressures = null!;
+ private IEnumerable pressures = null!;
private MicrobeSpecies baseSpecies = null!;
- public void Setup(MicrobeSpecies species, List selectionPressures)
+ public void Setup(MicrobeSpecies species, IEnumerable selectionPressures)
{
pressures = selectionPressures;
baseSpecies = species;
}
- public int Compare(Tuple? x, Tuple? y)
+ public int Compare(Mutant? x, Mutant? y)
{
if (ReferenceEquals(x, y))
return 0;
@@ -533,10 +504,10 @@ public int Compare(Tuple? x, Tuple? x, Tuple strengthX)
return -1;
- // Second float in tuple is apparently compared in ascending order
- // TODO: switch to named tuples to figure out what is going on here
- if (x.Item2 > y.Item2)
- return -1;
-
- if (x.Item2 < y.Item2)
+ if (x.MP > y.MP)
return 1;
+ if (x.MP < y.MP)
+ return -1;
+
return 0;
}
}