diff --git a/simulation_parameters/Constants.cs b/simulation_parameters/Constants.cs index b7acf1f8bb2..aae4d053bcf 100644 --- a/simulation_parameters/Constants.cs +++ b/simulation_parameters/Constants.cs @@ -1364,7 +1364,7 @@ public static class Constants public const float AUTO_EVO_MINIMUM_MOVE_POPULATION_FRACTION = 0.1f; public const float AUTO_EVO_MAXIMUM_MOVE_POPULATION_FRACTION = 0.4f; - public const float AUTO_EVO_ENGULF_PREDATION_SCORE = 100; + public const float AUTO_EVO_ENGULF_PREDATION_SCORE = 150; public const float AUTO_EVO_PILUS_PREDATION_SCORE = 5000; public const float AUTO_EVO_PILUS_DEFENSE_SCORE = 2000; public const float AUTO_EVO_TOXIN_PREDATION_SCORE = 90000; @@ -1389,6 +1389,17 @@ public static class Constants public const float AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION = 0.1f; public const float AUTO_EVO_REPRODUCTION_COMPOUND_PRODUCTION_SCORE = 3000.0f; public const float AUTO_EVO_REPRODUCTION_COMPOUND_COST_WEAKENING_MODIFIER = 0.2f; + public const float AUTO_EVO_SPRINTING_CONSUMPTION = 0.1f; + + public const float AUTO_EVO_MAX_AGGRESSION_ENERGY_PENALTY = 0.8f; + public const float AUTO_EVO_MAX_AGGRESSION_GATHERING_PENALTY = 0.1f; + public const float AUTO_EVO_MAX_FEAR_ENERGY_PENALTY = 0.005f; + public const float AUTO_EVO_MAX_FEAR_GATHERING_PENALTY = 0.000f; + public const float AUTO_EVO_MAX_OPPORTUNISM_BONUS = 0.4f; + public const float AUTO_EVO_MAX_OPPORTUNISM_PENALTY = 0.05f; + public const float AUTO_EVO_MAX_FOCUS_CHUNK_BONUS = 0.9f; + public const float AUTO_EVO_MAX_FOCUS_CLOUD_BONUS = 0.5f; + public const float AUTO_EVO_MAX_FOCUS_PENALTY = 0.1f; public const float AUTO_EVO_PREDATION_DEFENSE_SCORE_MODIFIER = 0.5f; diff --git a/src/auto-evo/AutoEvoGlobalCache.cs b/src/auto-evo/AutoEvoGlobalCache.cs index e3cac49e634..9e63ce960b5 100644 --- a/src/auto-evo/AutoEvoGlobalCache.cs +++ b/src/auto-evo/AutoEvoGlobalCache.cs @@ -50,7 +50,7 @@ public AutoEvoGlobalCache(WorldGenerationSettings worldSettings) RootPressure = new RootPressure(); MetabolicStabilityPressure = new MetabolicStabilityPressure(10.0f); GeneralAvoidPredationSelectionPressure = new GeneralAvoidPredationSelectionPressure(1.0f); - EnergyConsumptionPressure = new EnergyConsumptionPressure(0.3f); + EnergyConsumptionPressure = new EnergyConsumptionPressure(0.4f); EnvironmentalTolerancesPressure = new EnvironmentalTolerancePressure(3); PhosphatePressure = new ReproductionCompoundPressure( @@ -63,25 +63,29 @@ public AutoEvoGlobalCache(WorldGenerationSettings worldSettings) GlucoseConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Glucose, Compound.ATP, true, 1.5f); - GlucoseCloudPressure = new CompoundCloudPressure(Compound.Glucose, worldSettings.DayNightCycleEnabled, 1.0f); + GlucoseCloudPressure = new CompoundCloudPressure(Compound.Glucose, Compound.ATP, + worldSettings.DayNightCycleEnabled, 1.0f); IronConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Iron, Compound.ATP, true, 1.5f); SmallIronChunkPressure = new ChunkCompoundPressure("ironSmallChunk", new LocalizedString("SMALL_IRON_CHUNK"), - Compound.Iron, Compound.ATP, 2.0f); + Compound.Iron, Compound.ATP, worldSettings.DayNightCycleEnabled, 2.0f); BigIronChunkPressure = new ChunkCompoundPressure("ironBigChunk", new LocalizedString("BIG_IRON_CHUNK"), - Compound.Iron, Compound.ATP, 2.0f); + Compound.Iron, Compound.ATP, worldSettings.DayNightCycleEnabled, 2.0f); HydrogenSulfideConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Hydrogensulfide, Compound.Glucose, true, 1.5f); - HydrogenSulfideCloudPressure = new CompoundCloudPressure(Compound.Hydrogensulfide, + HydrogenSulfideCloudPressure = new CompoundCloudPressure(Compound.Hydrogensulfide, Compound.Glucose, worldSettings.DayNightCycleEnabled, 1.0f); SmallSulfurChunkPressure = new ChunkCompoundPressure("sulfurSmallChunk", - new LocalizedString("SMALL_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, 2.0f); + new LocalizedString("SMALL_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, + worldSettings.DayNightCycleEnabled, 2.0f); MediumSulfurChunkPressure = new ChunkCompoundPressure("sulfurMediumChunk", - new LocalizedString("MEDIUM_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, 2.0f); + new LocalizedString("MEDIUM_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, + worldSettings.DayNightCycleEnabled, 2.0f); LargeSulfurChunkPressure = new ChunkCompoundPressure("sulfurLargeChunk", - new LocalizedString("LARGE_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, 2.0f); + new LocalizedString("LARGE_SULFUR_CHUNK"), Compound.Hydrogensulfide, Compound.Glucose, + worldSettings.DayNightCycleEnabled, 2.0f); SunlightConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Sunlight, Compound.Glucose, true, 1.5f); @@ -90,7 +94,8 @@ public AutoEvoGlobalCache(WorldGenerationSettings worldSettings) RadiationConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Radiation, Compound.ATP, true, 1.0f); RadioactiveChunkPressure = new ChunkCompoundPressure("radioactiveChunk", - new LocalizedString("RADIOACTIVE_CHUNK"), Compound.Radiation, Compound.ATP, 1.0f); + new LocalizedString("RADIOACTIVE_CHUNK"), Compound.Radiation, Compound.ATP, + worldSettings.DayNightCycleEnabled, 1.0f); TemperatureConversionEfficiencyPressure = new CompoundConversionEfficiencyPressure(Compound.Temperature, Compound.Glucose, true, 1.5f); diff --git a/src/auto-evo/selection_pressure/ChunkCompoundPressure.cs b/src/auto-evo/selection_pressure/ChunkCompoundPressure.cs index 6406c24534a..13932b422c3 100644 --- a/src/auto-evo/selection_pressure/ChunkCompoundPressure.cs +++ b/src/auto-evo/selection_pressure/ChunkCompoundPressure.cs @@ -5,7 +5,7 @@ public class ChunkCompoundPressure : SelectionPressure { - public const ushort SERIALIZATION_VERSION = 1; + public const ushort SERIALIZATION_VERSION = 2; // Needed for translation extraction // ReSharper disable ArrangeObjectCreationWhenTypeEvident @@ -22,9 +22,11 @@ public class ChunkCompoundPressure : SelectionPressure private readonly CompoundDefinition compound; private readonly CompoundDefinition compoundOut; + private readonly bool isDayNightCycleEnabled; + public ChunkCompoundPressure(string chunkType, LocalizedString readableName, Compound compound, - Compound compoundOut, float weight) : base(weight, [ - RemoveOrganelle.ThatCreateCompound(compoundOut), + Compound compoundOut, bool isDayNightCycleEnabled, float weight) : base(weight, [ + new RemoveOrganelle(_ => true), new AddOrganelleAnywhere(organelle => organelle.HasChemoreceptorComponent), new AddOrganelleAnywhere(organelle => organelle.InternalName == "vacuole"), AddOrganelleAnywhere.ThatConvertBetweenCompounds(compound, compoundOut), @@ -33,6 +35,15 @@ public ChunkCompoundPressure(string chunkType, LocalizedString readableName, Com new ChemoreceptorUpgrades(compound, null, Constants.CHEMORECEPTOR_RANGE_DEFAULT, Constants.CHEMORECEPTOR_AMOUNT_DEFAULT, SimulationParameters.GetCompound(compound).Colour)), new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, -150.0f), new ChangeMembraneType("single"), new ChangeMembraneType("double"), ]) @@ -41,6 +52,7 @@ public ChunkCompoundPressure(string chunkType, LocalizedString readableName, Com this.compoundOut = SimulationParameters.GetCompound(compoundOut); this.chunkType = chunkType; this.readableName = readableName; + this.isDayNightCycleEnabled = isDayNightCycleEnabled; } public override LocalizedString Name => NameString; @@ -50,15 +62,29 @@ public ChunkCompoundPressure(string chunkType, LocalizedString readableName, Com public override ArchiveObjectType ArchiveObjectType => (ArchiveObjectType)ThriveArchiveObjectType.ChunkCompoundPressure; - public static ChunkCompoundPressure ReadFromArchive(ISArchiveReader reader, ushort version, - int referenceId) + public static ChunkCompoundPressure ReadFromArchive(ISArchiveReader reader, ushort version, int referenceId) { if (version is > SERIALIZATION_VERSION or <= 0) throw new InvalidArchiveVersionException(version, SERIALIZATION_VERSION); - var instance = new ChunkCompoundPressure(reader.ReadString() ?? throw new NullArchiveObjectException(), - reader.ReadObject(), (Compound)reader.ReadInt32(), (Compound)reader.ReadInt32(), - reader.ReadFloat()); + var chunkType = reader.ReadString(); + var readableName = reader.ReadObject(); + var compound = (Compound)reader.ReadInt32(); + var compoundOut = (Compound)reader.ReadInt32(); + bool isDayNightCycleEnabled; + + if (version >= 2) + { + isDayNightCycleEnabled = reader.ReadBool(); + } + else + { + isDayNightCycleEnabled = true; + } + + var instance = new ChunkCompoundPressure(chunkType ?? throw new NullArchiveObjectException(), + readableName, compound, compoundOut, + isDayNightCycleEnabled, reader.ReadFloat()); instance.ReadBasePropertiesFromArchive(reader, 1); return instance; @@ -70,6 +96,7 @@ public override void WriteToArchive(ISArchiveWriter writer) writer.WriteObject(readableName); writer.Write((int)compound.ID); writer.Write((int)compoundOut.ID); + writer.Write(isDayNightCycleEnabled); base.WriteToArchive(writer); } @@ -86,23 +113,54 @@ public override float Score(Species species, Patch patch, SimulationCache cache) // Speed is not too important to chunk microbes, but all else being the same faster is better than slower score += MathF.Pow(cache.GetSpeedForSpecies(microbeSpecies), 0.4f); + // Diminishing returns on storage + score += (MathF.Pow(microbeSpecies.StorageCapacities.Nominal + 1, 0.8f) - 1) / 0.8f; + // Additional bonus from chemoreceptor var chemoreceptorScore = cache.GetChemoreceptorChunkScore(microbeSpecies, chunk, compound); - // modify score by activity - var activityFraction = microbeSpecies.Behaviour.Activity / Constants.MAX_SPECIES_ACTIVITY; + var activity = microbeSpecies.Behaviour.Activity; - score = (score + chemoreceptorScore) * activityFraction - + score * (1 - activityFraction) * Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; + // Species that are less active during the night get a penalty to their activity + if (isDayNightCycleEnabled && cache.GetUsesVaryingCompoundsForSpecies(microbeSpecies, patch.Biome)) + { + var multiplier = activity / Constants.AI_ACTIVITY_TO_BE_FULLY_ACTIVE_DURING_NIGHT; - // Diminishing returns on storage - score += (MathF.Pow(microbeSpecies.StorageCapacities.Nominal + 1, 0.8f) - 1) / 0.8f; + multiplier = Math.Max(multiplier, Constants.AUTO_EVO_MAX_NIGHT_SESSILITY_COLLECTING_PENALTY); + + if (multiplier <= 1) + activity *= multiplier; + } + + // modify score by activity and focus + var activityScore = MathF.Pow(activity / Constants.MAX_SPECIES_ACTIVITY, 0.4f); + var focusScore = 1 + MathF.Pow(microbeSpecies.Behaviour.Focus / Constants.MAX_SPECIES_ACTIVITY, 0.4f) + * Constants.AUTO_EVO_MAX_FOCUS_CHUNK_BONUS; + + score = (score + chemoreceptorScore) * activityScore * focusScore + + score * (1 - activityScore * focusScore) * Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; + + // compound collection is reduced if you are running away from predators instead + var fearFraction = microbeSpecies.Behaviour.Fear / Constants.MAX_SPECIES_FEAR; + + score *= 1 - fearFraction * Constants.AUTO_EVO_MAX_FEAR_GATHERING_PENALTY; // If the species can't engulf, then they are dependent on only eating the runoff compounds if (!microbeSpecies.CanEngulf || cache.GetBaseHexSizeForSpecies(microbeSpecies) < chunk.Size * Constants.ENGULF_SIZE_RATIO_REQ) { score *= Constants.AUTO_EVO_CHUNK_LEAK_MULTIPLIER; + + // cloud compound collection is reduced if you are chasing prey instead + var aggressionFraction = microbeSpecies.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION; + + score *= 1 - aggressionFraction * Constants.AUTO_EVO_MAX_AGGRESSION_GATHERING_PENALTY; + } + else + { + var opportunismFraction = MathF.Pow( + microbeSpecies.Behaviour.Opportunism / Constants.MAX_SPECIES_ACTIVITY, 0.5f); + score *= 1 + opportunismFraction * Constants.AUTO_EVO_MAX_OPPORTUNISM_BONUS; } float compoundATP; diff --git a/src/auto-evo/selection_pressure/CompoundCloudPressure.cs b/src/auto-evo/selection_pressure/CompoundCloudPressure.cs index dc6bc3186bb..5557e724200 100644 --- a/src/auto-evo/selection_pressure/CompoundCloudPressure.cs +++ b/src/auto-evo/selection_pressure/CompoundCloudPressure.cs @@ -5,7 +5,7 @@ public class CompoundCloudPressure : SelectionPressure { - public const ushort SERIALIZATION_VERSION = 1; + public const ushort SERIALIZATION_VERSION = 2; // Needed for translation extraction // ReSharper disable ArrangeObjectCreationWhenTypeEvident @@ -13,21 +13,33 @@ public class CompoundCloudPressure : SelectionPressure // ReSharper restore ArrangeObjectCreationWhenTypeEvident + private readonly CompoundDefinition atp = SimulationParameters.GetCompound(Compound.ATP); + private readonly Compound compound; + private readonly CompoundDefinition compoundOut; private readonly CompoundDefinition compoundDefinition; private readonly bool isDayNightCycleEnabled; - public CompoundCloudPressure(Compound compound, bool isDayNightCycleEnabled, float weight) : + public CompoundCloudPressure(Compound compound, Compound compoundOut, bool isDayNightCycleEnabled, float weight) : base(weight, [ - RemoveOrganelle.ThatCreateCompound(Compound.Glucose), + new RemoveOrganelle(_ => true), AddOrganelleAnywhere.ThatUseCompound(compound), new AddOrganelleAnywhere(organelle => organelle.HasChemoreceptorComponent), new UpgradeOrganelle(organelle => organelle.HasChemoreceptorComponent, new ChemoreceptorUpgrades(compound, null, Constants.CHEMORECEPTOR_RANGE_DEFAULT, Constants.CHEMORECEPTOR_AMOUNT_DEFAULT, SimulationParameters.GetCompound(compound).Colour)), new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, -150.0f), ]) { compoundDefinition = SimulationParameters.GetCompound(compound); @@ -36,6 +48,7 @@ public CompoundCloudPressure(Compound compound, bool isDayNightCycleEnabled, flo throw new ArgumentException("Given compound to cloud pressure is not of cloud type"); this.compound = compound; + this.compoundOut = SimulationParameters.GetCompound(compoundOut); this.isDayNightCycleEnabled = isDayNightCycleEnabled; } @@ -46,13 +59,32 @@ public CompoundCloudPressure(Compound compound, bool isDayNightCycleEnabled, flo public override ArchiveObjectType ArchiveObjectType => (ArchiveObjectType)ThriveArchiveObjectType.CompoundCloudPressure; - public static CompoundCloudPressure ReadFromArchive(ISArchiveReader reader, ushort version, - int referenceId) + public static CompoundCloudPressure ReadFromArchive(ISArchiveReader reader, ushort version, int referenceId) { if (version is > SERIALIZATION_VERSION or <= 0) throw new InvalidArchiveVersionException(version, SERIALIZATION_VERSION); - var instance = new CompoundCloudPressure((Compound)reader.ReadInt32(), reader.ReadBool(), reader.ReadFloat()); + var compound = (Compound)reader.ReadInt32(); + Compound compoundOut; + + if (version >= 2) + { + compoundOut = (Compound)reader.ReadInt32(); + } + else + { + if (compound == Compound.Hydrogensulfide) + { + compoundOut = Compound.Glucose; + } + else + { + compoundOut = Compound.ATP; + } + } + + var instance = new CompoundCloudPressure(compound, compoundOut, reader.ReadBool(), + reader.ReadFloat()); instance.ReadBasePropertiesFromArchive(reader, 1); return instance; @@ -61,6 +93,7 @@ public static CompoundCloudPressure ReadFromArchive(ISArchiveReader reader, usho public override void WriteToArchive(ISArchiveWriter writer) { writer.Write((int)compound); + writer.Write((int)compoundOut.ID); writer.Write(isDayNightCycleEnabled); base.WriteToArchive(writer); } @@ -72,29 +105,59 @@ public override float Score(Species species, Patch patch, SimulationCache cache) var score = MathF.Pow(cache.GetSpeedForSpecies(microbeSpecies), 0.6f); + // Diminishing returns on storage + score += (MathF.Pow(microbeSpecies.StorageCapacities.Nominal + 1, 0.8f) - 1) / 0.8f; + + var chemoreceptorScore = cache.GetChemoreceptorCloudScore(microbeSpecies, compoundDefinition, patch.Biome); + var activity = microbeSpecies.Behaviour.Activity; - // Species that are less active during the night get a small penalty here based on their activity + // Species that are less active during the night get a penalty to their activity if (isDayNightCycleEnabled && cache.GetUsesVaryingCompoundsForSpecies(microbeSpecies, patch.Biome)) { var multiplier = activity / Constants.AI_ACTIVITY_TO_BE_FULLY_ACTIVE_DURING_NIGHT; - // Make the multiplier less extreme - multiplier *= Constants.AUTO_EVO_NIGHT_SESSILITY_COLLECTING_PENALTY_MULTIPLIER; - multiplier = Math.Max(multiplier, Constants.AUTO_EVO_MAX_NIGHT_SESSILITY_COLLECTING_PENALTY); if (multiplier <= 1) - score *= multiplier; + activity *= multiplier; } - var chemoreceptorScore = cache.GetChemoreceptorCloudScore(microbeSpecies, compoundDefinition, patch.Biome); + // modify score by activity and focus + var activityScore = MathF.Pow(activity / Constants.MAX_SPECIES_ACTIVITY, 0.4f); + var focusScore = 1 + MathF.Pow(microbeSpecies.Behaviour.Focus / Constants.MAX_SPECIES_ACTIVITY, 0.4f) * + Constants.AUTO_EVO_MAX_FOCUS_CLOUD_BONUS; + + score = (score + chemoreceptorScore) * activityScore * focusScore + + score * (1 - activityScore * focusScore) * Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; + + // cloud compound collection is reduced if you are chasing prey or running away from predators instead + // the same goes for chasing chunks + var aggressionFraction = microbeSpecies.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION; + var fearFraction = microbeSpecies.Behaviour.Fear / Constants.MAX_SPECIES_FEAR; + var opportunismFraction = microbeSpecies.Behaviour.Opportunism / Constants.MAX_SPECIES_OPPORTUNISM; + + score *= (1 - aggressionFraction * Constants.AUTO_EVO_MAX_AGGRESSION_GATHERING_PENALTY) + * (1 - fearFraction * Constants.AUTO_EVO_MAX_FEAR_GATHERING_PENALTY) + * (1 - opportunismFraction * Constants.AUTO_EVO_MAX_OPPORTUNISM_PENALTY); + + float compoundATP; + if (compoundOut != atp) + { + var compoundOutGenerated = + cache.GetCompoundGeneratedFrom(compoundDefinition, compoundOut, microbeSpecies, patch.Biome); + compoundATP = cache.GetCompoundConversionScoreForSpecies(compoundOut, atp, microbeSpecies) * + compoundOutGenerated; + } + else + { + compoundATP = cache.GetCompoundGeneratedFrom(compoundDefinition, atp, microbeSpecies, patch.Biome); + } - // modify score by activity - var activityFraction = activity / Constants.MAX_SPECIES_ACTIVITY; + var energyBalance = cache.GetEnergyBalanceForSpecies(microbeSpecies, patch.Biome); - score = (score + chemoreceptorScore) * activityFraction - + score * (1 - activityFraction) * Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; + // Penalize species that don't produce enough ATP to survive from just the compound in this cloud + score *= MathF.Min(compoundATP / energyBalance.TotalConsumption, 1); return score; } diff --git a/src/auto-evo/selection_pressure/EnergyConsumptionPressure.cs b/src/auto-evo/selection_pressure/EnergyConsumptionPressure.cs index af3259ada3c..68766cdbb56 100644 --- a/src/auto-evo/selection_pressure/EnergyConsumptionPressure.cs +++ b/src/auto-evo/selection_pressure/EnergyConsumptionPressure.cs @@ -1,5 +1,6 @@ namespace AutoEvo; +using System; using SharedBase.Archive; public class EnergyConsumptionPressure : SelectionPressure @@ -16,7 +17,7 @@ public EnergyConsumptionPressure(float weight) : base(weight, [ new AddOrganelleAnywhere(organelle => organelle.HasBindingFeature), new AddOrganelleAnywhere(organelle => organelle.HasSignalingFeature), - new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -100.0f), ]) { } @@ -46,11 +47,20 @@ public override float Score(Species species, Patch patch, SimulationCache cache) return 0; var energyBalance = cache.GetEnergyBalanceForSpecies(microbeSpecies, patch.Biome); - var activityScore = species.Behaviour.Activity / Constants.MAX_SPECIES_ACTIVITY; + var inactivityScore = 1 - MathF.Pow(species.Behaviour.Activity / Constants.MAX_SPECIES_ACTIVITY, 1.2f); + var focusScore = MathF.Pow(species.Behaviour.Focus / Constants.MAX_SPECIES_FOCUS, 1.2f); + var fearScore = MathF.Pow(microbeSpecies.Behaviour.Fear / Constants.MAX_SPECIES_FEAR, 1.5f); + var agressionScore = MathF.Pow(microbeSpecies.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION, 1.5f); + inactivityScore *= 1 - focusScore; + + // even inactive species still spend energy when chasing prey or running away from predators + inactivityScore *= 1 - agressionScore * Constants.AUTO_EVO_MAX_AGGRESSION_ENERGY_PENALTY; + inactivityScore *= 1 - fearScore * Constants.AUTO_EVO_MAX_FEAR_ENERGY_PENALTY; // Calculate how much energy is typically being consumed - var energyConsumption = (1 - activityScore) * energyBalance.TotalConsumptionStationary; - energyConsumption += activityScore * energyBalance.TotalConsumption; + var energyConsumption = inactivityScore * energyBalance.TotalConsumptionStationary; + energyConsumption += (1 - inactivityScore) * (energyBalance.TotalConsumption + + energyBalance.BaseMovement * Constants.AUTO_EVO_SPRINTING_CONSUMPTION * focusScore * agressionScore); // Modifier to fit the current mechanics of the Binding Agent. This should probably be removed or adjusted if // being in a colony no longer reduces osmoregulation cost. diff --git a/src/auto-evo/selection_pressure/EnvironmentalCompoundPressure.cs b/src/auto-evo/selection_pressure/EnvironmentalCompoundPressure.cs index 124b2058ee6..0ca31f31b2d 100644 --- a/src/auto-evo/selection_pressure/EnvironmentalCompoundPressure.cs +++ b/src/auto-evo/selection_pressure/EnvironmentalCompoundPressure.cs @@ -23,7 +23,18 @@ public class EnvironmentalCompoundPressure : SelectionPressure public EnvironmentalCompoundPressure(Compound compound, Compound createdCompound, float energyMultiplier, float weight) : base(weight, [ + new RemoveOrganelle(_ => true), AddOrganelleAnywhere.ThatUseCompound(compound), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, -150.0f), ]) { this.compound = SimulationParameters.GetCompound(compound); diff --git a/src/auto-evo/selection_pressure/GeneralAvoidPredationSelectionPressure.cs b/src/auto-evo/selection_pressure/GeneralAvoidPredationSelectionPressure.cs index 00f5124ff80..62ad9ff1cbe 100644 --- a/src/auto-evo/selection_pressure/GeneralAvoidPredationSelectionPressure.cs +++ b/src/auto-evo/selection_pressure/GeneralAvoidPredationSelectionPressure.cs @@ -31,8 +31,6 @@ public GeneralAvoidPredationSelectionPressure(float weight) : base(weight, [ true), new UpgradeOrganelle(organelle => organelle.HasMovementComponent, new FlagellumUpgrades(0.5f)), new UpgradeOrganelle(organelle => organelle.HasMovementComponent, new FlagellumUpgrades(1.0f)), - new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, -150.0f), - new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 150.0f), new ChangeMembraneType("double"), new ChangeMembraneType("cellulose"), new ChangeMembraneType("chitin"), @@ -40,6 +38,8 @@ public GeneralAvoidPredationSelectionPressure(float weight) : base(weight, [ new ChangeMembraneType("silica"), new ChangeMembraneRigidity(true), new ChangeMembraneRigidity(false), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 50.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 50.0f), ]) { } diff --git a/src/auto-evo/selection_pressure/PredationEffectivenessPressure.cs b/src/auto-evo/selection_pressure/PredationEffectivenessPressure.cs index c2666c76ae6..9bd6ff3cc07 100644 --- a/src/auto-evo/selection_pressure/PredationEffectivenessPressure.cs +++ b/src/auto-evo/selection_pressure/PredationEffectivenessPressure.cs @@ -16,6 +16,7 @@ public class PredationEffectivenessPressure : SelectionPressure public PredationEffectivenessPressure(Species prey, float weight) : base(weight, [ + new RemoveOrganelle(_ => true), new AddOrganelleAnywhere(organelle => organelle.MPCost < 30), new AddOrganelleAnywhere(organelle => organelle.InternalName == CommonMutationFunctions.Nucleus.InternalName), @@ -44,9 +45,15 @@ public PredationEffectivenessPressure(Species prey, float weight) : new UpgradeOrganelle(organelle => organelle.HasMovementComponent, new FlagellumUpgrades(0.5f)), new UpgradeOrganelle(organelle => organelle.HasMovementComponent, new FlagellumUpgrades(1.0f)), new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Activity, -150.0f), new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 150.0f), - new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, 150.0f), new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Fear, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Focus, -150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, 150.0f), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Opportunism, -150.0f), new ChangeMembraneType("single"), new ChangeMembraneType("double"), new AddOrganelleAnywhere(organelle => organelle.HasChemoreceptorComponent), diff --git a/src/auto-evo/selection_pressure/PredatorRoot.cs b/src/auto-evo/selection_pressure/PredatorRoot.cs index 981aa1a85f5..cfdf8124967 100644 --- a/src/auto-evo/selection_pressure/PredatorRoot.cs +++ b/src/auto-evo/selection_pressure/PredatorRoot.cs @@ -19,6 +19,7 @@ public PredatorRoot(float weight) : base(weight, [ RemoveOrganelle.ThatCreateCompound(Compound.Glucose), RemoveOrganelle.ThatCreateCompound(Compound.ATP), AddOrganelleAnywhere.ThatConvertBetweenCompounds(Compound.Glucose, Compound.ATP), + new ChangeBehaviorScore(ChangeBehaviorScore.BehaviorAttribute.Aggression, 10.0f), ]) { } @@ -50,6 +51,12 @@ public override float Score(Species species, Patch patch, SimulationCache cache) var atpFromGlucose = cache.GetCompoundGeneratedFrom(glucose, atp, microbeSpecies, patch.Biome); var energyBalance = cache.GetEnergyBalanceForSpecies(microbeSpecies, patch.Biome); + // ensure that the predator is at least slightly willing to hunt + if (microbeSpecies.Behaviour.Aggression == 0) + { + return 0; + } + // Ensure that a predator can actually survive off of only glucose if (atpFromGlucose >= energyBalance.TotalConsumption) { diff --git a/src/auto-evo/selection_pressure/ReproductionCompoundPressure.cs b/src/auto-evo/selection_pressure/ReproductionCompoundPressure.cs index 196fbc026eb..eaa05c46780 100644 --- a/src/auto-evo/selection_pressure/ReproductionCompoundPressure.cs +++ b/src/auto-evo/selection_pressure/ReproductionCompoundPressure.cs @@ -70,7 +70,6 @@ public override float Score(Species species, Patch patch, SimulationCache cache) return 0; var activeProcessList = cache.GetActiveProcessList(microbeSpecies); - var activity = microbeSpecies.Behaviour.Activity; // Let the miche function even at a compound level of 0 var compoundAmount = 1.0f; @@ -84,27 +83,31 @@ public override float Score(Species species, Patch patch, SimulationCache cache) var score = MathF.Pow(cache.GetSpeedForSpecies(microbeSpecies), 0.6f); - // Species that are less active during the night get a small penalty here based on their activity - if (isDayNightCycleEnabled && cache.GetUsesVaryingCompoundsForSpecies(microbeSpecies, patch.Biome)) - { - var multiplier = activity / Constants.AI_ACTIVITY_TO_BE_FULLY_ACTIVE_DURING_NIGHT; + var chemoreceptorScore = cache.GetChemoreceptorCloudScore(microbeSpecies, compoundDefinition, patch.Biome); - // Make the multiplier less extreme - multiplier *= Constants.AUTO_EVO_NIGHT_SESSILITY_COLLECTING_PENALTY_MULTIPLIER; + // Diminishing returns on storage + var capacitiesScore = (MathF.Pow(microbeSpecies.StorageCapacities.Nominal + 1, 0.8f) - 1) * 1.25f; + score += capacitiesScore; - multiplier = Math.Max(multiplier, Constants.AUTO_EVO_MAX_NIGHT_SESSILITY_COLLECTING_PENALTY); + // cloud compound collection is reduced if you are chasing prey or running away from predators instead + var aggressionPenaltyMultiplier = 1 - + microbeSpecies.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION * + Constants.AUTO_EVO_MAX_AGGRESSION_GATHERING_PENALTY; + var fearPenaltyMultiplier = 1 - microbeSpecies.Behaviour.Fear / Constants.MAX_SPECIES_FEAR * + Constants.AUTO_EVO_MAX_FEAR_GATHERING_PENALTY; - if (multiplier <= 1) - score *= multiplier; - } + score *= aggressionPenaltyMultiplier * fearPenaltyMultiplier; + chemoreceptorScore *= aggressionPenaltyMultiplier * fearPenaltyMultiplier; - var chemoreceptorScore = cache.GetChemoreceptorCloudScore(microbeSpecies, compoundDefinition, patch.Biome); - score += chemoreceptorScore; + // modify score by how much compound is available for collection + score *= compoundAmount; + chemoreceptorScore *= compoundAmount; // Precompute some scores to only resolve once. - var capacitiesScore = (MathF.Pow(microbeSpecies.StorageCapacities.Nominal + 1, 0.8f) - 1) * 1.25f; var speedScore = MathF.Pow(cache.GetSpeedForSpecies(microbeSpecies), 0.4f); var baseMicrobeHexSize = cache.GetBaseHexSizeForSpecies(microbeSpecies); + var opportunismFraction = MathF.Pow( + microbeSpecies.Behaviour.Opportunism / Constants.MAX_SPECIES_ACTIVITY, 0.5f); // Combine with compound amounts and scores from all chunks foreach (var chunk in patch.Biome.Chunks.Values) @@ -122,46 +125,68 @@ public override float Score(Species species, Patch patch, SimulationCache cache) // Diminishing returns on storage chunkScore += capacitiesScore; + // compound collection is reduced if you are running away from predators instead + chunkScore *= fearPenaltyMultiplier; + // If the species can't engulf, then they are dependent on only eating the runoff compounds if (!microbeSpecies.CanEngulf || baseMicrobeHexSize < chunk.Size * Constants.ENGULF_SIZE_RATIO_REQ) { chunkScore *= Constants.AUTO_EVO_CHUNK_LEAK_MULTIPLIER; - } + chunkChemoreceptorScore *= Constants.AUTO_EVO_CHUNK_LEAK_MULTIPLIER; - chemoreceptorScore += chunkChemoreceptorScore; - score += chunkScore; + // cloud compound collection is reduced if you are chasing prey instead + chunkScore *= aggressionPenaltyMultiplier; + chunkChemoreceptorScore *= aggressionPenaltyMultiplier; + } + else + { + score *= 1 + opportunismFraction * Constants.AUTO_EVO_MAX_OPPORTUNISM_BONUS; + } if (!chunk.Compounds.TryGetValue(compoundDefinition.ID, out var chunkCompoundAmount)) throw new ArgumentException("Chunk does not contain compound"); var ventedCompound = MathF.Pow(chunkCompoundAmount.Amount, Constants.AUTO_EVO_CHUNK_AMOUNT_NERF); - compoundAmount += ventedCompound; + // modify score by how much compound is available for collection + chemoreceptorScore += chunkChemoreceptorScore * ventedCompound; + score += chunkScore * ventedCompound; } } - // modify score by how much compound is available for collection - score *= compoundAmount; - chemoreceptorScore *= compoundAmount; + var finalScore = 0.1f; + + var activity = microbeSpecies.Behaviour.Activity; + + // Species that are less active during the night get a penalty to their activity + if (isDayNightCycleEnabled && cache.GetUsesVaryingCompoundsForSpecies(microbeSpecies, patch.Biome)) + { + var multiplier = activity / Constants.AI_ACTIVITY_TO_BE_FULLY_ACTIVE_DURING_NIGHT; + + multiplier = Math.Max(multiplier, Constants.AUTO_EVO_MAX_NIGHT_SESSILITY_COLLECTING_PENALTY); + + if (multiplier <= 1) + activity *= multiplier; + } + + // modify score by activity and focus + var activityScore = MathF.Pow(activity / Constants.MAX_SPECIES_ACTIVITY, 0.4f); + var focusScore = MathF.Pow(microbeSpecies.Behaviour.Focus / Constants.MAX_SPECIES_ACTIVITY, 0.4f); + + finalScore += (score + chemoreceptorScore) * activityScore * focusScore; + finalScore += score * (1 - activityScore * focusScore) * + Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; // Score from organelles that produce this compound foreach (var process in activeProcessList) { if (process.Process.Outputs.TryGetValue(compoundDefinition, out var producedCompoundAmount)) { - score += producedCompoundAmount * Constants.AUTO_EVO_REPRODUCTION_COMPOUND_PRODUCTION_SCORE; + finalScore += producedCompoundAmount * Constants.AUTO_EVO_REPRODUCTION_COMPOUND_PRODUCTION_SCORE; } } - var finalScore = 0.1f; - - // modify score by activity - var activityFraction = activity / Constants.MAX_SPECIES_ACTIVITY; - - finalScore += (score + chemoreceptorScore) * activityFraction; - finalScore += score * (1 - activityFraction) * Constants.AUTO_EVO_PASSIVE_COMPOUND_COLLECTION_FRACTION; - // Take into account how much compound the species needs to collect finalScore /= species.TotalReproductionCost[compound] * mildingModifier; diff --git a/src/auto-evo/simulation/SimulationCache.cs b/src/auto-evo/simulation/SimulationCache.cs index 51a71805446..93a520e9375 100644 --- a/src/auto-evo/simulation/SimulationCache.cs +++ b/src/auto-evo/simulation/SimulationCache.cs @@ -478,14 +478,17 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio } var aggressionScore = predator.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION; - var activityScore = predator.Behaviour.Activity / Constants.MAX_SPECIES_ACTIVITY; + var activityScore = MathF.Pow(predator.Behaviour.Activity / Constants.MAX_SPECIES_ACTIVITY, 0.5f); + var opportunismScore = predator.Behaviour.Opportunism / Constants.MAX_SPECIES_OPPORTUNISM; + var focusScore = predator.Behaviour.Focus / Constants.MAX_SPECIES_FOCUS; var preyFearScore = prey.Behaviour.Fear / Constants.MAX_SPECIES_FEAR; var preyAggressionScore = prey.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION; var preyOpportunismScore = prey.Behaviour.Opportunism / Constants.MAX_SPECIES_OPPORTUNISM; + var preyFocusScore = prey.Behaviour.Focus / Constants.MAX_SPECIES_FOCUS; // prey's effectiveness at running away depends on how quickly they choose to run away - preySpeed *= preyFearScore; + preySpeed *= preyFearScore * (1 - preyAggressionScore); // Sprinting calculations var predatorSprintSpeed = predatorSpeed * sprintMultiplier; @@ -665,11 +668,20 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio // Active hunting is more effective for active species catchScore *= activityScore; + catchScore *= 1 + focusScore; // ... but you may also catch them by luck (e.g. when they run into you), // Prey that can't turn away fast enough are more likely to get caught. accidentalCatchScore = Constants.AUTO_EVO_ENGULF_LUCKY_CATCH_PROBABILITY * strongPullingCiliaModifier * preyRotationModifier; + + // Less cautious and more focused prey are slightly more likely to get into a dangerous situation + var opportunismPenalty = MathF.Pow(preyOpportunismScore, 1.5f) + * Constants.AUTO_EVO_MAX_OPPORTUNISM_PENALTY; + var focusPenalty = MathF.Pow(preyFocusScore, 1.5f) + * Constants.AUTO_EVO_MAX_FOCUS_PENALTY; + catchScore *= 1 + opportunismPenalty * (1 + focusPenalty); + accidentalCatchScore *= 1 + opportunismPenalty * (1 + focusPenalty); } // targets that resist physical damage are of course less vulnerable to it @@ -688,7 +700,7 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio defensivePilusScore += defensiveInjectisomeScore; // defensive pili need to be turned directly away from the predator to work - defensivePilusScore *= preyRotationModifier * preyFearScore; + defensivePilusScore *= preyRotationModifier * preyFearScore * (1 - preyAggressionScore); // Calling for allies helps with combat. if (hasSignallingAgent) @@ -697,12 +709,10 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio preyPilusScore *= signallingBonus; // Use catch score for Pili - pilusScore -= defensivePilusScore; - if (pilusScore < 0) - pilusScore = 0; + pilusScore /= Math.Max(1, defensivePilusScore); pilusScore *= catchScore + accidentalCatchScore; - // Prey can use offensive pili for defense in these encounters, but only if they have the right behaviour + // Prey can use offensive pili for defense in these encounters, but only if they have the right behavior preyPilusScore *= (catchScore + accidentalCatchScore) * preyRotationModifier * defenseScoreModifier * preyAggressionScore * (1 - preyFearScore); @@ -729,10 +739,7 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio // Engulfing prey by luck is especially easy if you are huge. // This is also used to incentivize size in microbe species. engulfmentScore = (catchScore + accidentalCatchScore * predatorHexSize) * - (Constants.AUTO_EVO_ENGULF_PREDATION_SCORE - defensivePilusScore - totalPreyToxinContent); - if (engulfmentScore < 0) - engulfmentScore = 0; - + (Constants.AUTO_EVO_ENGULF_PREDATION_SCORE / Math.Max(1, defensivePilusScore + totalPreyToxinContent)); engulfmentScore *= enzymesScore; } @@ -755,6 +762,10 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio if (predatorInhibitedPreyEnergyProduction < predatorOsmoregulationCost) damagingToxinScore += channelInhibitorScore; + // MicrobeAISystem makes prey not fire toxins against predators under this condition + if (preyFearScore >= preyAggressionScore) + preyDamagingToxinScore = 0; + if (damagingToxinScore > 0) { // Applying projectile hit chance to damaging toxins @@ -798,6 +809,7 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio // Active hunting is more effective for active species damagingToxinScore *= activityScore; + damagingToxinScore *= 1 + focusScore; } if (preyDamagingToxinScore > 0) @@ -856,9 +868,10 @@ public float GetPredationScore(Species predatorSpecies, Species preySpecies, Bio if (predatorSlimeJetScore > 0) preySlimeJetScore = 0; - cached = scoreMultiplier * aggressionScore * - (pilusScore + engulfmentScore + damagingToxinScore) - (preySlimeJetScore + preyMucocystsScore + - preyPilusScore + preyDamagingToxinScore); + cached = scoreMultiplier * MathF.Pow(aggressionScore, 0.5f) * + (1 + MathF.Pow(opportunismScore, 0.5f * Constants.AUTO_EVO_MAX_OPPORTUNISM_BONUS)) * + ((pilusScore + engulfmentScore + damagingToxinScore) / + Math.Max(1, preySlimeJetScore + preyMucocystsScore + preyPilusScore + preyDamagingToxinScore)); if (cached < 0) cached = 0;