protected override IAttemptResult TryVariant() { var migration = GetRandomMigration(); // Move generation can randomly fail if (migration == null) { return(new AttemptResult(null, -1)); } var config = new SimulationConfiguration(configuration, map, Constants.AUTO_EVO_VARIANT_SIMULATION_STEPS); config.SetPatchesToRunBySpeciesPresence(species); config.PatchesToRun.Add(migration.From); config.PatchesToRun.Add(migration.To); config.Migrations.Add(new Tuple <Species, SpeciesMigration>(species, migration)); // TODO: this could be faster to just simulate the source and // destination patches (assuming in the future no global effects of // migrations are added, which would need a full patch map // simulation anyway) PopulationSimulation.Simulate(config); var population = config.Results.GetGlobalPopulation(species); return(new AttemptResult(migration, population)); }
public bool RunStep(RunResults results) { // ReSharper disable RedundantArgumentDefaultValue var config = new SimulationConfiguration(configuration, map, 1) { Results = results, CollectEnergyInformation = collectEnergyInfo, }; // ReSharper restore RedundantArgumentDefaultValue if (extraSpecies != null) { config.ExtraSpecies = extraSpecies; } if (excludedSpecies != null) { config.ExcludedSpecies = excludedSpecies; } // Directly feed the population results to the main results object PopulationSimulation.Simulate(config); return(true); }
private static void RunSimulationStep(SimulationConfiguration parameters, List <Species> species, Random random) { foreach (var entry in parameters.OriginalMap.Patches) { // Simulate the species in each patch taking into account the already computed populations SimulatePatchStep(parameters.Results, entry.Value, species.Where((item) => parameters.Results.GetPopulationInPatch(item, entry.Value) > 0).ToList(), random); } }
protected override IAttemptResult TryCurrentVariant() { var config = new SimulationConfiguration(map, Constants.AUTOEVO_VARIANT_SIMULATION_STEPS); PopulationSimulation.Simulate(config); var population = config.Results.GetGlobalPopulation(species); return(new AttemptResult(null, population)); }
public static void Simulate(SimulationConfiguration parameters) { var random = new Random(); var speciesToSimulate = CopyInitialPopulationsToResults(parameters); while (parameters.StepsLeft > 0) { RunSimulationStep(parameters, speciesToSimulate, random); --parameters.StepsLeft; } }
private static void RunSimulationStep(SimulationConfiguration parameters, List <Species> species, IEnumerable <KeyValuePair <int, Patch> > patchesToSimulate, Random random, SimulationCache cache, AutoEvoConfiguration autoEvoConfiguration) { foreach (var entry in patchesToSimulate) { // Simulate the species in each patch taking into account the already computed populations SimulatePatchStep(parameters, entry.Value, species.Where(item => parameters.Results.GetPopulationInPatch(item, entry.Value) > 0), random, cache, autoEvoConfiguration); } }
protected override IAttemptResult TryVariant() { var mutated = (MicrobeSpecies)species.Clone(); mutations.CreateMutatedSpecies((MicrobeSpecies)species, mutated); var config = new SimulationConfiguration(map, Constants.AUTOEVO_VARIANT_SIMULATION_STEPS); config.ExcludedSpecies.Add(species); config.ExtraSpecies.Add(mutated); PopulationSimulation.Simulate(config); var population = config.Results.GetGlobalPopulation(mutated); return(new AttemptResult(mutated, population)); }
private MicrobeSpecies TryBiodiversitySplit(Species splitFrom, bool inCurrentPatch) { var config = new SimulationConfiguration(configuration, map, Constants.AUTO_EVO_VARIANT_SIMULATION_STEPS); var split = (MicrobeSpecies)splitFrom.Clone(); if (configuration.BiodiversitySplitIsMutated) { mutations.CreateMutatedSpecies((MicrobeSpecies)splitFrom, split); } // Set the starting population in the patch split.Population = configuration.NewBiodiversityIncreasingSpeciesPopulation; config.ExtraSpecies.Add(split); config.PatchesToRun.Add(patch); if (inCurrentPatch) { // TODO: should we apply the population reduction to splitFrom? } PopulationSimulation.Simulate(config); var population = config.Results.GetPopulationInPatch(split, patch); if (population < configuration.NewBiodiversityIncreasingSpeciesPopulation) { return(null); } // TODO: could compare the original species population here to determine if this change is beneficial to // it as well (in which case a non-force species split could be done) // Successfully found a species to create in order to increase biodiversity return(split); }
public static void Simulate(SimulationConfiguration parameters) { var random = new Random(); var cache = new SimulationCache(); var speciesToSimulate = CopyInitialPopulationsToResults(parameters); IEnumerable <KeyValuePair <int, Patch> > patchesToSimulate = parameters.OriginalMap.Patches; // Skip patches not configured to be simulated in order to run faster if (parameters.PatchesToRun.Count > 0) { patchesToSimulate = patchesToSimulate.Where(p => parameters.PatchesToRun.Contains(p.Value)); } var patchesList = patchesToSimulate.ToList(); while (parameters.StepsLeft > 0) { RunSimulationStep(parameters, speciesToSimulate, patchesList, random, cache, parameters.AutoEvoConfiguration); --parameters.StepsLeft; } }
/// <summary> /// Populates the initial population numbers taking config into account /// </summary> private static List <Species> CopyInitialPopulationsToResults(SimulationConfiguration parameters) { var species = new List <Species>(); // Copy non excluded species foreach (var candidateSpecies in parameters.OriginalMap.FindAllSpeciesWithPopulation()) { if (parameters.ExcludedSpecies.Contains(candidateSpecies)) { continue; } species.Add(candidateSpecies); } // Copy extra species species.AddRange(parameters.ExtraSpecies); // Prepare population numbers for each patch for each of the included species foreach (var entry in parameters.OriginalMap.Patches) { var patch = entry.Value; foreach (var currentSpecies in species) { int currentPopulation = patch.GetSpeciesPopulation(currentSpecies); // If this is an extra species, this first takes the // population from extra species that match its index, if that // doesn't exist then the population number is used if (currentPopulation == 0 && parameters.ExtraSpecies.Contains(currentSpecies)) { bool useGlobal = true; for (int i = 0; i < parameters.ExtraSpecies.Count; ++i) { if (parameters.ExtraSpecies[i] == currentSpecies) { if (parameters.ExcludedSpecies.Count > i) { currentPopulation = patch.GetSpeciesPopulation(parameters.ExcludedSpecies[i]); useGlobal = false; } break; } } if (useGlobal) { currentPopulation = currentSpecies.Population; } } // Apply migrations foreach (var migration in parameters.Migrations) { if (migration.Item1 == currentSpecies) { if (migration.Item2.From == patch) { currentPopulation -= migration.Item2.Population; } else if (migration.Item2.To == patch) { currentPopulation += migration.Item2.Population; } } } // All species even ones not in a patch need to have their population numbers added // as the simulation expects to be able to get the populations parameters.Results.AddPopulationResultForSpecies(currentSpecies, patch, currentPopulation); } } return(species); }
/// <summary> /// The heart of the simulation that handles the processed parameters and calculates future populations. /// </summary> private static void SimulatePatchStep(SimulationConfiguration simulationConfiguration, Patch patch, IEnumerable <Species> genericSpecies, Random random, SimulationCache cache, AutoEvoConfiguration autoEvoConfiguration) { _ = random; var populations = simulationConfiguration.Results; bool trackEnergy = simulationConfiguration.CollectEnergyInformation; // This algorithm version is for microbe species var species = genericSpecies.Select(s => (MicrobeSpecies)s).ToList(); // Skip if there aren't any species in this patch if (species.Count < 1) { return; } var energyBySpecies = new Dictionary <MicrobeSpecies, float>(); foreach (var currentSpecies in species) { energyBySpecies[currentSpecies] = 0.0f; } bool strictCompetition = autoEvoConfiguration.StrictNicheCompetition; var niches = new List <FoodSource> { new EnvironmentalFoodSource(patch, Sunlight, Constants.AUTO_EVO_SUNLIGHT_ENERGY_AMOUNT), new CompoundFoodSource(patch, Glucose), new CompoundFoodSource(patch, HydrogenSulfide), new CompoundFoodSource(patch, Iron), new ChunkFoodSource(patch, "marineSnow"), new ChunkFoodSource(patch, "ironSmallChunk"), new ChunkFoodSource(patch, "ironBigChunk"), }; foreach (var currentSpecies in species) { niches.Add(new HeterotrophicFoodSource(patch, currentSpecies)); } foreach (var niche in niches) { // If there isn't a source of energy here, no need for more calculations if (niche.TotalEnergyAvailable() <= MathUtils.EPSILON) { continue; } var fitnessBySpecies = new Dictionary <MicrobeSpecies, float>(); var totalNicheFitness = 0.0f; foreach (var currentSpecies in species) { float thisSpeciesFitness; if (strictCompetition) { // Softly enforces https://en.wikipedia.org/wiki/Competitive_exclusion_principle // by exaggerating fitness differences thisSpeciesFitness = Mathf.Max(Mathf.Pow(niche.FitnessScore(currentSpecies, cache), 2.5f), 0.0f); } else { thisSpeciesFitness = Mathf.Max(niche.FitnessScore(currentSpecies, cache), 0.0f); } fitnessBySpecies[currentSpecies] = thisSpeciesFitness; totalNicheFitness += thisSpeciesFitness; } // If no species can get energy this way, no need for more calculations if (totalNicheFitness <= MathUtils.EPSILON) { continue; } foreach (var currentSpecies in species) { var energy = fitnessBySpecies[currentSpecies] * niche.TotalEnergyAvailable() / totalNicheFitness; // If this species can't gain energy here, don't count it (this also prevents it from appearing // in food sources (if that's not what we want), if the species doesn't use this food source if (energy <= MathUtils.EPSILON) { continue; } energyBySpecies[currentSpecies] += energy; if (trackEnergy) { populations.AddTrackedEnergyForSpecies(currentSpecies, patch, niche, fitnessBySpecies[currentSpecies], energy, totalNicheFitness); } } } foreach (var currentSpecies in species) { var energyBalanceInfo = cache.GetEnergyBalanceForSpecies(currentSpecies, patch); // Modify populations based on energy var newPopulation = (long)(energyBySpecies[currentSpecies] / energyBalanceInfo.FinalBalanceStationary); if (trackEnergy) { populations.AddTrackedEnergyConsumptionForSpecies(currentSpecies, patch, newPopulation, energyBySpecies[currentSpecies], energyBalanceInfo.FinalBalanceStationary); } // TODO: this is a hack for now to make the player experience better, try to get the same rules working // for the player and AI species in the future. if (currentSpecies.PlayerSpecies) { // Severely penalize a species that can't osmoregulate if (energyBalanceInfo.FinalBalanceStationary < 0) { newPopulation /= 10; } } else { // Severely penalize a species that can't move indefinitely if (energyBalanceInfo.FinalBalance < 0) { newPopulation /= 10; } } // Can't survive without enough population if (newPopulation < Constants.AUTO_EVO_MINIMUM_VIABLE_POPULATION) { newPopulation = 0; } populations.AddPopulationResultForSpecies(currentSpecies, patch, newPopulation); } }