Skip to content
2 changes: 1 addition & 1 deletion simulation_parameters/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion simulation_parameters/common/auto-evo_parameters.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"MutationsPerSpecies": 3,
"MutationsPerSpecies": 5,
"MoveAttemptsPerSpecies": 8,
"StrictNicheCompetition": true
}
28 changes: 14 additions & 14 deletions src/auto-evo/mutation_strategy/AddOrganelleAnywhere.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static CommonMutationFunctions;

/// <summary>
/// Adds a random, valid organelle to a valid position. Doesn't place multicellular or later organelles.
/// </summary>
public class AddOrganelleAnywhere : IMutationStrategy<MicrobeSpecies>
{
private readonly CommonMutationFunctions.Direction direction;
private readonly Direction direction;
private readonly OrganelleDefinition[] allOrganelles;

public AddOrganelleAnywhere(Func<OrganelleDefinition, bool> criteria, CommonMutationFunctions.Direction direction
= CommonMutationFunctions.Direction.Neutral)
public AddOrganelleAnywhere(Func<OrganelleDefinition, bool> criteria, Direction direction = Direction.Neutral)
{
allOrganelles = SimulationParameters.Instance.GetAllOrganelles().Where(criteria).Where(IsOrganelleValid)
.ToArray();
Expand All @@ -26,31 +26,31 @@ public AddOrganelleAnywhere(Func<OrganelleDefinition, bool> 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);

return ThatUseCompound(compoundResolved, direction);
}

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

public static AddOrganelleAnywhere ThatCreateCompound(Compound compound,
CommonMutationFunctions.Direction direction = CommonMutationFunctions.Direction.Neutral)
Direction direction = Direction.Neutral)
{
var compoundResolved = SimulationParameters.GetCompound(compound);

Expand All @@ -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) &&
Expand All @@ -69,15 +69,15 @@ 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);

return ThatConvertBetweenCompounds(fromCompoundResolved, toCompoundResolved, direction);
}

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
// If a cheaper organelle gets added, this will need to be updated
Expand All @@ -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<Tuple<MicrobeSpecies, double>>();
var mutated = new List<Mutant>();

// TODO: reuse this memory somehow
var workMemory1 = new List<Hex>();
Expand All @@ -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));
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/auto-evo/mutation_strategy/ChangeBehaviorScore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using static CommonMutationFunctions;

public class ChangeBehaviorScore : IMutationStrategy<MicrobeSpecies>
{
Expand All @@ -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<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
// TODO: Make random something passed in
Expand Down Expand Up @@ -61,6 +62,6 @@ public enum BehaviorAttribute
break;
}

return [Tuple.Create(newSpecies, mp)];
return [new Mutant(newSpecies, mp)];
}
}
5 changes: 3 additions & 2 deletions src/auto-evo/mutation_strategy/ChangeMembraneRigidity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using static CommonMutationFunctions;

public class ChangeMembraneRigidity : IMutationStrategy<MicrobeSpecies>
{
Expand All @@ -14,7 +15,7 @@ public ChangeMembraneRigidity(bool lower)

public bool Repeatable => true;

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
const float change = Constants.AUTO_EVO_MUTATION_RIGIDITY_STEP;
Expand Down Expand Up @@ -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)];
}
}
5 changes: 3 additions & 2 deletions src/auto-evo/mutation_strategy/ChangeMembraneType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using static CommonMutationFunctions;

public class ChangeMembraneType : IMutationStrategy<MicrobeSpecies>
{
Expand All @@ -14,7 +15,7 @@ public ChangeMembraneType(string membraneType)

public bool Repeatable => false;

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (baseSpecies.MembraneType == membraneType)
Expand All @@ -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)];
}
}
3 changes: 2 additions & 1 deletion src/auto-evo/mutation_strategy/IMutationStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using static CommonMutationFunctions;

public interface IMutationStrategy<T>
where T : Species
Expand All @@ -27,6 +28,6 @@ public interface IMutationStrategy<T>
/// List of mutated species, null if no possible mutations are found (some strategies may return an empty list
/// instead in this case)
/// </returns>
public List<Tuple<T, double>>? MutationsOf(T baseSpecies, double mp, bool lawk, Random random,
public List<Mutant>? MutationsOf(T baseSpecies, double mp, bool lawk, Random random,
BiomeConditions biomeToConsider);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using static CommonMutationFunctions;

public class ModifyEnvironmentalTolerance : IMutationStrategy<MicrobeSpecies>
{
Expand All @@ -12,7 +13,7 @@ public class ModifyEnvironmentalTolerance : IMutationStrategy<MicrobeSpecies>
/// </summary>
public bool Repeatable => false;

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (mp <= 0)
Expand Down Expand Up @@ -216,6 +217,6 @@ public class ModifyEnvironmentalTolerance : IMutationStrategy<MicrobeSpecies>
newSpecies.Tolerances.SanityCheck();
#endif

return [Tuple.Create(newSpecies, mp)];
return [new Mutant(newSpecies, mp)];
}
}
11 changes: 6 additions & 5 deletions src/auto-evo/mutation_strategy/MoveOrganelleBack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using AutoEvo;
using static CommonMutationFunctions;

internal class MoveOrganelleBack : IMutationStrategy<MicrobeSpecies>
{
Expand All @@ -15,13 +16,13 @@ public MoveOrganelleBack(Func<OrganelleDefinition, bool> criteria)

public bool Repeatable => true;

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (mp < Constants.ORGANELLE_MOVE_COST)
return null;

var mutated = new List<Tuple<MicrobeSpecies, double>>();
var mutated = new List<Mutant>();

// TODO: try to avoid these temporary list allocations
var workMemory1 = new List<Hex>();
Expand All @@ -34,13 +35,13 @@ public MoveOrganelleBack(Func<OrganelleDefinition, bool> 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));
}
}

Expand Down
14 changes: 9 additions & 5 deletions src/auto-evo/mutation_strategy/RemoveOrganelle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static CommonMutationFunctions;

public class RemoveOrganelle : IMutationStrategy<MicrobeSpecies>
{
Expand Down Expand Up @@ -46,16 +47,19 @@ public static RemoveOrganelle ThatCreateCompound(Compound compound)

// ReSharper restore InvokeAsExtensionMethod

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? 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<Tuple<MicrobeSpecies, double>>? mutated = null;
List<Mutant>? mutated = null;

MutationWorkMemory? workMemory = null;

Expand Down Expand Up @@ -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<Tuple<MicrobeSpecies, double>>();
mutated.Add(Tuple.Create(newSpecies, mp - Constants.ORGANELLE_REMOVE_COST));
mutated ??= new List<Mutant>();
mutated.Add(new Mutant(newSpecies, mp - Constants.ORGANELLE_REMOVE_COST));
}

return mutated;
Expand Down
7 changes: 4 additions & 3 deletions src/auto-evo/mutation_strategy/UpgradeOrganelle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using AutoEvo;
using static CommonMutationFunctions;

public class UpgradeOrganelle : IMutationStrategy<MicrobeSpecies>
{
Expand Down Expand Up @@ -32,7 +33,7 @@ public UpgradeOrganelle(Func<OrganelleDefinition, bool> criteria, string upgrade

public bool Repeatable => false;

public List<Tuple<MicrobeSpecies, double>>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
public List<Mutant>? MutationsOf(MicrobeSpecies baseSpecies, double mp, bool lawk,
Random random, BiomeConditions biomeToConsider)
{
if (allOrganelles.Count == 0)
Expand All @@ -58,7 +59,7 @@ public UpgradeOrganelle(Func<OrganelleDefinition, bool> criteria, string upgrade

if (validMutations)
{
var mutated = new List<Tuple<MicrobeSpecies, double>>();
var mutated = new List<Mutant>();

var newSpecies = (MicrobeSpecies)baseSpecies.Clone();

Expand All @@ -84,7 +85,7 @@ public UpgradeOrganelle(Func<OrganelleDefinition, bool> criteria, string upgrade
}
}

mutated.Add(new Tuple<MicrobeSpecies, double>(newSpecies, mp));
mutated.Add(new Mutant(newSpecies, mp));

return mutated;
}
Expand Down
6 changes: 4 additions & 2 deletions src/auto-evo/mutations/CommonMutationFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -444,4 +444,6 @@ private static Hex.HexSide[] SideTraversalOrder(Hex hex, Direction direction, Ra

return TraversalOrder8;
}

public record Mutant(MicrobeSpecies Species, double MP);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To not wreck memory allocations, this has to be public record struct Mutant(MicrobeSpecies Species, double MP); to make this into a value type.

And also that means Mutant? should not be used anywhere as that causes boxing. Instead I think that Species here should be made nullable so that null species can stand-in for invalid Mutant instances.

Copy link
Contributor Author

@GameDungeon GameDungeon Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only place that Mutant? is used is in the sorter, and I don't think it can be replaced with your solution in that context.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that's a bit unfortunate, but it seems it had the same problem with Tuples already. So I guess for now could you add a TODO comment there about this architectural problem that would be nice to solve eventually?

}
Loading