ActorInfo ChooseUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) { return(null); } var myUnits = player.World .ActorsHavingTrait <IPositionable>() .Where(a => a.Owner == player) .Select(a => a.Info.Name).ToList(); foreach (var unit in Info.UnitsToBuild.Shuffle(world.LocalRandom)) { if (buildableThings.Any(b => b.Name == unit.Key)) { if (myUnits.Count(a => a == unit.Key) * 100 < unit.Value * myUnits.Count) { if (HasAdequateAirUnitReloadBuildings(world.Map.Rules.Actors[unit.Key])) { return(world.Map.Rules.Actors[unit.Key]); } } } } return(null); }
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) { return(null); } var unit = buildableThings.Random(world.LocalRandom); return(HasAdequateAirUnitReloadBuildings(unit) ? unit : null); }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding(baseBuilder.Info.PowerTypes, buildableThings, a => a.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount)); // First priority is to get out of a low power situation if (playerPower != null && playerPower.ExcessPower < minimumExcessPower) { if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(p => p.Amount) > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return(power); } } // Next is to build up a strong economy if (!baseBuilder.HasAdequateRefineryCount) { var refinery = GetProducibleBuilding(baseBuilder.Info.RefineryTypes, buildableThings); if (refinery != null && HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return(refinery); } if (power != null && refinery != null && !HasSufficientPowerForActor(refinery)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Make sure that we can spend as fast as we are earning if (baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding(baseBuilder.Info.ProductionTypes, buildableThings); if (production != null && HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return(production); } if (power != null && production != null && !HasSufficientPowerForActor(production)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings if (waterState == WaterCheck.EnoughWater && baseBuilder.Info.NewProductionCashThreshold > 0 && playerResources.Resources > baseBuilder.Info.NewProductionCashThreshold && AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes)) { var navalproduction = GetProducibleBuilding(baseBuilder.Info.NavalProductionTypes, buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name); return(navalproduction); } if (power != null && navalproduction != null && !HasSufficientPowerForActor(navalproduction)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Create some head room for resource storage if we really need it if (playerResources.Resources > 0.8 * playerResources.ResourceCapacity) { var silo = GetProducibleBuilding(baseBuilder.Info.SiloTypes, buildableThings); if (silo != null && HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return(silo); } if (power != null && silo != null && !HasSufficientPowerForActor(silo)) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return(power); } } // Build everything else foreach (var frac in baseBuilder.Info.BuildingFractions.Shuffle(world.LocalRandom)) { var name = frac.Key; // Does this building have initial delay, if so have we passed it? if (baseBuilder.Info.BuildingDelays != null && baseBuilder.Info.BuildingDelays.ContainsKey(name) && baseBuilder.Info.BuildingDelays[name] > world.WorldTick) { continue; } // Can we build this structure? if (!buildableThings.Any(b => b.Name == name)) { continue; } // Do we want to build this structure? var count = playerBuildings.Count(a => a.Info.Name == name); if (count * 100 > frac.Value * playerBuildings.Length) { continue; } if (baseBuilder.Info.BuildingLimits.ContainsKey(name) && baseBuilder.Info.BuildingLimits[name] <= count) { continue; } // If we're considering to build a naval structure, check whether there is enough water inside the base perimeter // and any structure providing buildable area close enough to that water. // TODO: Extend this check to cover any naval structure, not just production. if (baseBuilder.Info.NavalProductionTypes.Contains(name) && (waterState == WaterCheck.NotEnoughWater || !AIUtils.IsAreaAvailable <GivesBuildableArea>(world, player, world.Map, baseBuilder.Info.CheckForWaterRadius, baseBuilder.Info.WaterTerrainTypes))) { continue; } // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; if (playerPower != null && (playerPower.ExcessPower < minimumExcessPower || !HasSufficientPowerForActor(actor))) { // Try building a power plant instead if (power != null && power.TraitInfos <PowerInfo>().Where(i => i.EnabledByDefault).Sum(pi => pi.Amount) > 0) { if (playerPower.PowerOutageRemainingTicks > 0) { AIUtils.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); } else { AIUtils.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); } return(power); } } // Lets build this AIUtils.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count); return(actor); } // Too spammy to keep enabled all the time, but very useful when debugging specific issues. // AIUtils.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group); return(null); }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); // This gets used quite a bit, so let's cache it here var power = GetProducibleBuilding("Power", buildableThings, a => a.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); // First priority is to get out of a low power situation if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) { if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return power; } } // Next is to build up a strong economy if (!ai.HasAdequateProc() || !ai.HasMinimumProc()) { var refinery = GetProducibleBuilding("Refinery", buildableThings); if (refinery != null && HasSufficientPowerForActor(refinery)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return refinery; } if (power != null && refinery != null && !HasSufficientPowerForActor(refinery)) { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } // Make sure that we can spend as fast as we are earning if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding("Production", buildableThings); if (production != null && HasSufficientPowerForActor(production)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return production; } if (power != null && production != null && !HasSufficientPowerForActor(production)) { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings if (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold && ai.CloseEnoughToWater()) { var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name); return navalproduction; } if (power != null && navalproduction != null && !HasSufficientPowerForActor(navalproduction)) { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } // Create some head room for resource storage if we really need it if (playerResources.AlertSilo) { var silo = GetProducibleBuilding("Silo", buildableThings); if (silo != null && HasSufficientPowerForActor(silo)) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return silo; } if (power != null && silo != null && !HasSufficientPowerForActor(silo)) { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } // Build everything else foreach (var frac in ai.Info.BuildingFractions.Shuffle(ai.Random)) { var name = frac.Key; // Can we build this structure? if (!buildableThings.Any(b => b.Name == name)) continue; // Do we want to build this structure? var count = playerBuildings.Count(a => a.Info.Name == name); if (count > frac.Value * playerBuildings.Length) continue; if (ai.Info.BuildingLimits.ContainsKey(name) && ai.Info.BuildingLimits[name] <= count) continue; // If we're considering to build a naval structure, check whether there is enough water inside the base perimeter // and any structure providing buildable area close enough to that water. // TODO: Extend this check to cover any naval structure, not just production. if (ai.Info.BuildingCommonNames.ContainsKey("NavalProduction") && ai.Info.BuildingCommonNames["NavalProduction"].Contains(name) && (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater())) continue; // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) { // Try building a power plant instead if (power != null && power.TraitInfos<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) { if (playerPower.PowerOutageRemainingTicks > 0) HackyAI.BotDebug("{0} decided to build {1}: Priority override (is low power)", queue.Actor.Owner, power.Name); else HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } // Lets build this HackyAI.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count); return actor; } // Too spammy to keep enabled all the time, but very useful when debugging specific issues. // HackyAI.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group); return null; }
ActorInfo ChooseUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) return null; var myUnits = Player.World .ActorsWithTrait<IPositionable>() .Where(a => a.Actor.Owner == Player) .Select(a => a.Actor.Info.Name).ToList(); foreach (var unit in Info.UnitsToBuild.Shuffle(Random)) if (buildableThings.Any(b => b.Name == unit.Key)) if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Count) if (HasAdequateAirUnitReloadBuildings(Map.Rules.Actors[unit.Key])) return Map.Rules.Actors[unit.Key]; return null; }
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); if (!buildableThings.Any()) return null; var unit = buildableThings.Random(Random); return HasAdequateAirUnitReloadBuildings(unit) ? unit : null; }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue) { var buildableThings = queue.BuildableItems(); // First priority is to get out of a low power situation if (playerPower.ExcessPower < ai.Info.MinimumExcessPower) { var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.WithInterface<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount)); if (power != null && power.Traits.WithInterface<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(p => p.Amount) > 0) { // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage if (playerPower.PowerOutageRemainingTicks <= 0) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name); return power; } } } // Next is to build up a strong economy if (!ai.HasAdequateProc() || !ai.HasMinimumProc()) { var refinery = GetProducibleBuilding("Refinery", buildableThings); if (refinery != null) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name); return refinery; } } // Make sure that we can can spend as fast as we are earning if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding("Production", buildableThings); if (production != null) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name); return production; } } // Create some head room for resource storage if we really need it if (playerResources.AlertSilo) { var silo = GetProducibleBuilding("Silo", buildableThings); if (silo != null) { HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name); return silo; } } // Build everything else foreach (var frac in ai.Info.BuildingFractions.Shuffle(ai.Random)) { var name = frac.Key; // Can we build this structure? if (!buildableThings.Any(b => b.Name == name)) continue; // Do we want to build this structure? var count = playerBuildings.Count(a => a.Info.Name == name); if (count > frac.Value * playerBuildings.Length) continue; if (ai.Info.BuildingLimits.ContainsKey(name) && ai.Info.BuildingLimits[name] <= count) continue; // Will this put us into low power? var actor = world.Map.Rules.Actors[frac.Key]; var pis = actor.Traits.WithInterface<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1); if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || playerPower.ExcessPower < pis.Sum(pi => pi.Amount)) { // Try building a power plant instead var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.WithInterface<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount)); if (power != null && power.Traits.WithInterface<PowerInfo>().Where(i => i.UpgradeMinEnabledLevel < 1).Sum(pi => pi.Amount) > 0) { // TODO: Handle the case when of when we actually do need a power plant because we don't have enough but are also suffering from a power outage if (playerPower.PowerOutageRemainingTicks > 0) HackyAI.BotDebug("AI: {0} is suffering from a power outage; not going to build {1}", queue.Actor.Owner, power.Name); else { HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); return power; } } } // Lets build this HackyAI.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count); return actor; } // Too spammy to keep enabled all the time, but very useful when debugging specific issues. // HackyAI.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group); return null; }