protected override bool TryExecuteTurn(Randomizer randomizer, IGameLayer gameLayer, GameState state) { var buildingsUnderConstruction = state.GetBuildingsUnderConstruction().ToArray(); // Randomly choose building //var building = buildingsUnderConstruction.ElementAt(randomizer.Random.Next(0, buildingsUnderConstruction.Length)); // Choose the one closest to completion var building = buildingsUnderConstruction.OrderByDescending(x => x.BuildProgress).FirstOrDefault(); if (building != null) { var position = building.Position; gameLayer.ExecuteAction(GameActions.Build, position); building = state.GetCompletedBuildings().FirstOrDefault(x => x.Position.ToString() == position.ToString()); if (building is BuiltResidenceBuilding residence) { if (AdjustHeatOnConstructed) { var blueprint = state.AvailableResidenceBuildings.Find(x => x.BuildingName == building.BuildingName); var energy = blueprint.BaseEnergyNeed + (residence.Temperature - state.CurrentTemp) * blueprint.Emissivity / 1 - 0.5 - residence.CurrentPop * 0.04; if (IsBetween(energy, residence.RequestedEnergyIn - AllowedDiffMargin, residence.RequestedEnergyIn + AllowedDiffMargin)) { // current energy setting is sufficient } else { // adjust energy as the first action after completing building (will take one turn) gameLayer.AdjustEnergy(building.Position, energy, state.GameId); } } } else { } return(true); } return(false); }
protected override bool TryExecuteTurn(Randomizer randomizer, IGameLayer gameLayer, GameState state) { double?predictedTrend = null; var outdoorTemp = state.CurrentTemp; if (state.TemperatureHistory?.Count > 2) { // Predict outdoor temperature var prePreviousTemp = state.TemperatureHistory.Reverse().Skip(2).First().Value; var previousTemp = state.TemperatureHistory.Reverse().Skip(1).First().Value; var previousDiff = previousTemp - prePreviousTemp; var currentDiff = state.CurrentTemp - previousTemp; if (previousDiff > 0 && currentDiff > 0) { // Trend is "getting hotter" predictedTrend = (previousDiff + currentDiff) / 2; } else if (previousDiff < 0 && currentDiff < 0) { // Trend is "getting colder" predictedTrend = (previousDiff + currentDiff) / 2; } if (predictedTrend.HasValue) { // Add the trend twice to more quickly react to big temperature changes predictedTrend = predictedTrend.Value * 2; outdoorTemp = state.CurrentTemp + predictedTrend.Value; Logger.LogTrace($"Using prediction for OutdoorTemp: {outdoorTemp:N3}, CurrentTemp: {state.CurrentTemp:N1} (weighted trend: {predictedTrend:N3})"); } } var buildings = state.GetCompletedBuildings() .OfType <BuiltResidenceBuilding>() //.Where(x => x.Temperature < MinTemperature + AllowedTemperatureDiffMargin) .OrderBy(x => x.Temperature) .ToArray(); foreach (var building in buildings) { var blueprint = state.AvailableResidenceBuildings.Find(x => x.BuildingName == building.BuildingName); // Predict next temperature var newTemp = building.Temperature + (building.EffectiveEnergyIn - blueprint.BaseEnergyNeed) * _degreesPerExcessMwh + _degreesPerPop * building.CurrentPop - (building.Temperature - outdoorTemp) * blueprint.Emissivity; if (IsBetween(newTemp, TargetTemperature - AllowedTemperatureDiffMargin, TargetTemperature + AllowedTemperatureDiffMargin)) { // close enough to target temperature already... continue; } if (newTemp < MinTemperature) { // Is below minimum, fake that it is much colder than it is to make a faster recovery outdoorTemp -= Math.Abs(TargetTemperature - building.Temperature); } if (building.Temperature < MinTemperature) { // Is below minimum, fake that it is much colder than it is to make a faster recovery outdoorTemp -= Math.Abs(TargetTemperature - building.Temperature); } if (newTemp > MaxTemperature) { // Is above maximum, fake that it is much hotter than it is to make a faster recovery outdoorTemp += Math.Abs(TargetTemperature - building.Temperature); } if (building.Temperature > MaxTemperature) { // Is above maximum, fake that it is much hotter than it is to make a faster recovery outdoorTemp += Math.Abs(TargetTemperature - building.Temperature); } var energyOld = blueprint.BaseEnergyNeed + (building.Temperature - outdoorTemp) * blueprint.Emissivity / 1 + 0.5 - building.CurrentPop * 0.04; var energyNew = (TargetTemperature - building.Temperature + blueprint.BaseEnergyNeed * _degreesPerExcessMwh - _degreesPerPop * building.CurrentPop + building.Temperature * blueprint.Emissivity - outdoorTemp * blueprint.Emissivity) / _degreesPerExcessMwh; var energy = UseSecondaryAlgorithm ? energyNew : energyOld; if (predictedTrend.GetValueOrDefault() > 0) { // Trend is getting hotter // Then reduce energy to save heating resources and to cooldown appartment... energy *= 0.9; } else if (predictedTrend.GetValueOrDefault() < 0) { // Trend is getting colder // Then apply more energy to make sure has enough heating... energy *= 1.1; } if (energy < blueprint.BaseEnergyNeed) { Logger.LogWarning($"Wanted to set lower energy than BaseEnergyNeed, restoring to base: {blueprint.BaseEnergyNeed:N3} Mwh from {energy:N3} Mwh"); energy = blueprint.BaseEnergyNeed; } // Predict next temperature, if change energy var predictedNewTemp = building.Temperature + (energy - blueprint.BaseEnergyNeed) * _degreesPerExcessMwh + _degreesPerPop * building.CurrentPop - (building.Temperature - outdoorTemp) * blueprint.Emissivity; Logger.LogTrace($"{building.BuildingName} at {building.Position}"); Logger.LogTrace($"Current building temp: \t\t{building.Temperature:N3}"); Logger.LogTrace($"Next building temp: \t\t\t{newTemp:N3}"); Logger.LogTrace($"Predicted New Temp: \t\t\t{predictedNewTemp:N3}"); Logger.LogTrace($"Current building energy: \t{building.EffectiveEnergyIn}/{building.RequestedEnergyIn} Mwh"); Logger.LogTrace($"New requested energy: \t\t{energyOld:N3} Mwh"); Logger.LogTrace($"New requested energy v2: \t{energyNew:N3} Mwh"); if (newTemp < TargetTemperature) { // Colder than target if (energy < building.RequestedEnergyIn) { // Should not lower energy if already too cold continue; } else { } } else if (newTemp > TargetTemperature) { // Hotter than target if (energy > building.RequestedEnergyIn) { // Should not increase energy if already too hot continue; } else { } } if (IsBetween(energy, building.RequestedEnergyIn - AllowedDiffMargin, building.RequestedEnergyIn + AllowedDiffMargin)) { // minimal change, wait to update energy (to reduce calls and save money) continue; } if (state.Funds < _adjustCost) { Logger.LogWarning($"Wanted to apply energy '{energy:N1}' to building at {building.Position}, but has insufficient funds ({state.Funds})"); continue; } Logger.LogInformation($"Adjusting energy for {building.Position} from {building.RequestedEnergyIn} to {energy:N1} (TEMP:: current={building.Temperature:N3}, predicted={predictedNewTemp:N3})"); gameLayer.AdjustEnergy(building.Position, energy, state.GameId); return(true); } return(false); }