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)); }
public void WeightedStrategyTest() { var data = CreateMarketData(); var investorProvider = CreateInvestorProvider(); var marketDataCache = CreateMarketDataCache(data); var simulationCache = new SimulationCache(); var simulationFactory = new SimulatorFactory(marketDataCache, simulationCache); var ratingService = new RatingService(marketDataCache, simulationFactory, investorProvider); var strategyFactory = CreateStrategyFactory(marketDataCache, simulationCache, investorProvider, ratingService); var deltaStrategy = strategyFactory.Create(new DeltaParameters()); var volumeStrategy = strategyFactory.Create(new VolumeParameters()); var _ = simulationFactory.Create <BacktestingSimulator>() .Evaluate(deltaStrategy, investorProvider.Current).ToArray(); var __ = simulationFactory.Create <BacktestingSimulator>() .Evaluate(volumeStrategy, investorProvider.Current).ToArray(); var parameters = new WeightedParameters { Weights = new Dictionary <IStrategy, double> { { deltaStrategy, 0d }, { volumeStrategy, 0d } } }; var strategy = strategyFactory.Create(parameters); var target = simulationFactory.Create <BacktestingSimulator>() .Evaluate(strategy, investorProvider.Current); var actual = ToApprovedString(target); Approvals.Verify(actual); }
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); }
protected static IEnumerable <SimulationState> SimulateStrategy( IEnumerable <MarketData> data, Func <StrategyFactory, IStrategy> createStrategyFunc, bool initialiseRatingService = false) { var investorProvider = CreateInvestorProvider(); var marketDataCache = CreateMarketDataCache(data); var simulationCache = new SimulationCache(); var simulatorFactor = new SimulatorFactory(marketDataCache, simulationCache); var simulator = simulatorFactor.Create <BacktestingSimulator>(); var ratingService = new RatingService( marketDataCache, simulatorFactor, investorProvider); var strategyFactory = CreateStrategyFactory( marketDataCache, simulationCache, investorProvider, ratingService); var strategy = createStrategyFunc(strategyFactory); if (initialiseRatingService) { ratingService.RateMarketData(); } return(simulator.Evaluate(strategy, investorProvider.Current)); }
protected static Dictionary <string, SimulationState> SimulateStrategy( IEnumerable <MarketData> data, IParameters[] parameters) { var investorProvider = CreateInvestorProvider(); var marketDataCache = CreateMarketDataCache(data); var simulationCache = new SimulationCache(); var simulatorFactor = new SimulatorFactory(marketDataCache, simulationCache); var simulator = simulatorFactor.Create <BacktestingSimulator>(); var ratingService = new RatingService(marketDataCache, simulatorFactor, investorProvider); var strategyFactory = CreateStrategyFactory(marketDataCache, simulationCache, investorProvider, ratingService); var results = new Dictionary <string, SimulationState>(); foreach (var p in parameters) { var strategy = strategyFactory.Create(p); var state = simulator.Evaluate(strategy, investorProvider.Current); results[strategy.StrategyType.GetDescription()] = state.Last(); } return(results); }
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); }
public void Setup() { ConfigurationManager.AppSettings["BacktestingDate"] = "2010-07-01"; ConfigurationManager.AppSettings["CacheSize"] = "2000"; ConfigurationManager.AppSettings["DataPath"] = "MarketData.csv"; ConfigurationManager.AppSettings["RelativePath"] = @"../../../"; var marketData = CreateMarketData(); var marketDataCache = CreateMarketDataCache(marketData); var investor = new Investor { DailyFunds = 10, OrderDelayDays = 3 }; var investorProvider = CreateInvestorProvider(); var simulationCache = new SimulationCache(); var simulationFactory = new SimulatorFactory(marketDataCache, simulationCache); var ratingService = new RatingService( marketDataCache, simulationFactory, investorProvider); var strategyFactory = CreateStrategyFactory( marketDataCache, simulationCache, investorProvider, ratingService); var marketDataRepository = new Mock <IRepository <MarketData> >(); var simulationResultsRepository = new Mock <IRepository <SimulationResult> >(); _target = new ResultsProvider( marketDataCache, marketDataRepository.Object, ratingService, simulationResultsRepository.Object); _target.Initialise(); var strategy = strategyFactory.Create(new HolidayEffectParameters()); var stateJson = File.ReadAllText(@"HolidayEffectSimulationState.json"); var simulationState = JsonConvert.DeserializeObject <SimulationState[]>(stateJson); var resultsToAdd = new ConcurrentDictionary <IStrategy, SimulationState[]>(); resultsToAdd.TryAdd(strategy, simulationState); _target.AddResults(investor, resultsToAdd); }
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> /// Provides a fitness metric to determine population adjustments for species in a patch. /// </summary> /// <param name="microbe">The species to be evaluated.</param> /// <param name="simulationCache"> /// Cache that should be used to reduce amount of times expensive computations are run /// </param> /// <returns> /// A float to represent score. Scores are only compared against other scores from the same FoodSource, /// so different implementations do not need to worry about scale. /// </returns> public abstract float FitnessScore(Species microbe, SimulationCache simulationCache);
/// <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); } }
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); } }