public override float FitnessScore(Species species, SimulationCache simulationCache)
        {
            var microbeSpecies = (MicrobeSpecies)species;

            // No cannibalism
            if (microbeSpecies == prey)
            {
                return(0.0f);
            }

            var behaviourScore = microbeSpecies.Behaviour.Aggression / Constants.MAX_SPECIES_AGGRESSION;

            var microbeSpeciesHexSize = microbeSpecies.BaseHexSize;
            var predatorSpeed         = microbeSpecies.BaseSpeed;

            predatorSpeed += simulationCache.GetEnergyBalanceForSpecies(microbeSpecies, patch).FinalBalance;

            // It's great if you can engulf this prey, but only if you can catch it
            var engulfScore = 0.0f;

            if (microbeSpeciesHexSize / preyHexSize >
                Constants.ENGULF_SIZE_RATIO_REQ && !microbeSpecies.MembraneType.CellWall)
            {
                engulfScore = Constants.AUTO_EVO_ENGULF_PREDATION_SCORE;
            }

            engulfScore *= predatorSpeed > preySpeed ? 1.0f : Constants.AUTO_EVO_ENGULF_LUCKY_CATCH_PROBABILITY;

            var pilusScore   = 0.0f;
            var oxytoxyScore = 0.0f;

            foreach (var organelle in microbeSpecies.Organelles)
            {
                if (organelle.Definition.HasComponentFactory <PilusComponentFactory>())
                {
                    pilusScore += Constants.AUTO_EVO_PILUS_PREDATION_SCORE;
                    continue;
                }

                foreach (var process in organelle.Definition.RunnableProcesses)
                {
                    if (process.Process.Outputs.ContainsKey(oxytoxy))
                    {
                        oxytoxyScore += process.Process.Outputs[oxytoxy] * Constants.AUTO_EVO_TOXIN_PREDATION_SCORE;
                    }
                }
            }

            // Pili are much more useful if the microbe can close to melee
            pilusScore *= predatorSpeed > preySpeed ? 1.0f : Constants.AUTO_EVO_ENGULF_LUCKY_CATCH_PROBABILITY;

            // predators are less likely to use toxin against larger prey, unless they are opportunistic
            if (preyHexSize > microbeSpeciesHexSize)
            {
                oxytoxyScore *= microbeSpecies.Behaviour.Opportunism / Constants.MAX_SPECIES_OPPORTUNISM;
            }

            // Intentionally don't penalize for osmoregulation cost to encourage larger monsters
            return(behaviourScore * (pilusScore + engulfScore + microbeSpeciesHexSize + oxytoxyScore));
        }
Beispiel #2
0
        public override float FitnessScore(Species species, SimulationCache simulationCache)
        {
            var microbeSpecies = (MicrobeSpecies)species;

            var energyBalance = simulationCache.GetEnergyBalanceForSpecies(microbeSpecies, patch);

            // Don't penalize species that can't move at full speed all the time as much here
            var chunkEaterSpeed = Math.Max(microbeSpecies.BaseSpeed + energyBalance.FinalBalance,
                                           microbeSpecies.BaseSpeed / 3);

            // We ponder the score for each compound by its amount, leading to pondering in proportion of total
            // quantity, with a constant factor that will be eliminated when making ratios of scores for this niche.
            var score = energyCompounds.Sum(c => EnergyGenerationScore(microbeSpecies, c.Key) * c.Value);

            score *= chunkEaterSpeed * species.Behaviour.Activity;

            // If the species can't engulf, then they are dependent on only eating the runoff compounds
            if (microbeSpecies.MembraneType.CellWall ||
                microbeSpecies.BaseHexSize < chunkSize * Constants.ENGULF_SIZE_RATIO_REQ)
            {
                score *= Constants.AUTO_EVO_CHUNK_LEAK_MULTIPLIER;
            }

            // Chunk (originally from marine snow) food source penalizes big creatures that try to rely on it
            score /= energyBalance.TotalConsumptionStationary;

            return(score);
        }
        public override float FitnessScore(Species species, SimulationCache simulationCache)
        {
            var microbeSpecies = (MicrobeSpecies)species;

            var energyCreationScore = EnergyGenerationScore(microbeSpecies, compound);

            var energyCost = simulationCache
                             .GetEnergyBalanceForSpecies(microbeSpecies, patch)
                             .TotalConsumptionStationary;

            return(energyCreationScore / energyCost);
        }
Beispiel #4
0
        /// <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);
            }
        }