feat(ai): improving agents planning and memory

This commit is contained in:
2026-03-20 02:12:29 -04:00
parent f5bf7d8e3f
commit a2f66b0dca
11 changed files with 2012 additions and 445 deletions

View File

@@ -207,8 +207,11 @@ internal sealed class SimulationProjectionService
faction.ShipsBuilt,
faction.ShipsLost,
faction.DefaultPolicySetId,
faction.GoapState,
faction.GoapPriorities)).ToList());
faction.StrategicAssessment,
faction.StrategicPriorities,
faction.Blackboard,
faction.Objectives,
faction.IssuedTasks)).ToList());
}
public void PrimeDeltaBaseline(SimulationWorld world)
@@ -515,10 +518,18 @@ internal sealed class SimulationProjectionService
private static string BuildFactionSignature(FactionRuntime faction, CommanderRuntime? commander)
{
var goapSig = commander?.LastGoalPriorities is { } prios
var prioritySig = commander?.LastStrategicPriorities is { } prios
? string.Join(",", prios.Select(p => $"{p.Name}:{p.Priority:0.##}"))
: string.Empty;
return $"{faction.Credits:0.###}|{faction.PopulationTotal:0.###}|{faction.OreMined:0.###}|{faction.GoodsProduced:0.###}|{faction.ShipsBuilt}|{faction.ShipsLost}|{faction.DefaultPolicySetId}|{goapSig}";
var objectiveSig = commander?.Objectives is { Count: > 0 } objectives
? string.Join(",", objectives.Select(objective =>
$"{objective.Kind}:{objective.State}:{objective.Priority:0.##}:{objective.BlockingReason}:{objective.InvalidationReason}"))
: string.Empty;
var taskSig = commander?.IssuedTasks is { Count: > 0 } tasks
? string.Join(",", tasks.Select(task =>
$"{task.Kind}:{task.State}:{task.Priority:0.##}:{task.ShipRole}:{task.CommodityId}:{task.TargetFactionId}:{task.TargetSiteId}"))
: string.Empty;
return $"{faction.Credits:0.###}|{faction.PopulationTotal:0.###}|{faction.OreMined:0.###}|{faction.GoodsProduced:0.###}|{faction.ShipsBuilt}|{faction.ShipsLost}|{faction.DefaultPolicySetId}|{prioritySig}|{objectiveSig}|{taskSig}";
}
private static ResourceNodeDelta ToNodeDelta(ResourceNodeRuntime node) => new(
@@ -759,12 +770,15 @@ internal sealed class SimulationProjectionService
private static FactionDelta ToFactionDelta(FactionRuntime faction, CommanderRuntime? commander)
{
FactionGoapStateSnapshot? goapState = null;
IReadOnlyList<FactionGoapPrioritySnapshot>? goapPriorities = null;
FactionPlanningStateSnapshot? strategicAssessment = null;
IReadOnlyList<FactionStrategicPrioritySnapshot>? strategicPriorities = null;
FactionBlackboardSnapshot? blackboard = null;
IReadOnlyList<FactionObjectiveSnapshot>? objectives = null;
IReadOnlyList<FactionIssuedTaskSnapshot>? issuedTasks = null;
if (commander?.LastPlanningState is { } ps)
if (commander?.LastStrategicAssessment is { } ps)
{
goapState = new FactionGoapStateSnapshot(
strategicAssessment = new FactionPlanningStateSnapshot(
ps.MilitaryShipCount,
ps.MinerShipCount,
ps.TransportShipCount,
@@ -773,20 +787,149 @@ internal sealed class SimulationProjectionService
ps.TargetSystemCount,
ps.HasShipFactory,
NormalizeFiniteFloat(ps.OreStockpile),
NormalizeFiniteFloat(ps.RefinedMetalsStockpile),
NormalizeFiniteFloat(ps.RefinedMetalsProductionRate),
NormalizeFiniteFloat(ps.HullpartsStockpile),
NormalizeFiniteFloat(ps.HullpartsProductionRate),
NormalizeFiniteFloat(ps.ClaytronicsStockpile),
NormalizeFiniteFloat(ps.ClaytronicsProductionRate),
NormalizeFiniteFloat(ps.WaterStockpile),
NormalizeFiniteFloat(ps.WaterProductionRate),
NormalizeFiniteFloat(ps.WaterShortageHorizonSeconds));
NormalizeFiniteFloat(ps.RefinedMetalsAvailableStock),
NormalizeFiniteFloat(ps.RefinedMetalsUsageRate),
NormalizeFiniteFloat(ps.RefinedMetalsProjectedProductionRate),
NormalizeFiniteFloat(ps.RefinedMetalsProjectedNetRate),
NormalizeFiniteFloat(ps.RefinedMetalsLevelSeconds),
ps.RefinedMetalsLevel,
NormalizeFiniteFloat(ps.HullpartsAvailableStock),
NormalizeFiniteFloat(ps.HullpartsUsageRate),
NormalizeFiniteFloat(ps.HullpartsProjectedProductionRate),
NormalizeFiniteFloat(ps.HullpartsProjectedNetRate),
NormalizeFiniteFloat(ps.HullpartsLevelSeconds),
ps.HullpartsLevel,
NormalizeFiniteFloat(ps.ClaytronicsAvailableStock),
NormalizeFiniteFloat(ps.ClaytronicsUsageRate),
NormalizeFiniteFloat(ps.ClaytronicsProjectedProductionRate),
NormalizeFiniteFloat(ps.ClaytronicsProjectedNetRate),
NormalizeFiniteFloat(ps.ClaytronicsLevelSeconds),
ps.ClaytronicsLevel,
NormalizeFiniteFloat(ps.WaterAvailableStock),
NormalizeFiniteFloat(ps.WaterUsageRate),
NormalizeFiniteFloat(ps.WaterProjectedProductionRate),
NormalizeFiniteFloat(ps.WaterProjectedNetRate),
NormalizeFiniteFloat(ps.WaterLevelSeconds),
ps.WaterLevel);
}
if (commander?.LastGoalPriorities is { } prios)
if (commander?.LastStrategicPriorities is { } prios)
{
goapPriorities = prios.Select(p => new FactionGoapPrioritySnapshot(p.Name, p.Priority)).ToList();
strategicPriorities = prios.Select(p => new FactionStrategicPrioritySnapshot(p.Name, p.Priority)).ToList();
}
if (commander?.FactionBlackboard is { } bb)
{
blackboard = new FactionBlackboardSnapshot(
bb.PlanCycle,
bb.UpdatedAtUtc,
bb.TargetWarshipCount,
bb.HasWarIndustrySupplyChain,
bb.HasShipyard,
bb.HasActiveExpansionProject,
bb.ActiveExpansionCommodityId,
bb.ActiveExpansionModuleId,
bb.ActiveExpansionSiteId,
bb.ActiveExpansionSystemId,
bb.EnemyFactionCount,
bb.EnemyShipCount,
bb.EnemyStationCount,
bb.MilitaryShipCount,
bb.MinerShipCount,
bb.TransportShipCount,
bb.ConstructorShipCount,
bb.ControlledSystemCount,
bb.CommoditySignals.Select(signal => new FactionCommoditySignalSnapshot(
signal.ItemId,
NormalizeFiniteFloat(signal.AvailableStock),
NormalizeFiniteFloat(signal.OnHand),
NormalizeFiniteFloat(signal.ProductionRatePerSecond),
NormalizeFiniteFloat(signal.CommittedProductionRatePerSecond),
NormalizeFiniteFloat(signal.UsageRatePerSecond),
NormalizeFiniteFloat(signal.NetRatePerSecond),
NormalizeFiniteFloat(signal.ProjectedNetRatePerSecond),
NormalizeFiniteFloat(signal.LevelSeconds),
signal.Level,
NormalizeFiniteFloat(signal.ProjectedProductionRatePerSecond),
NormalizeFiniteFloat(signal.BuyBacklog),
NormalizeFiniteFloat(signal.ReservedForConstruction))).ToList(),
bb.ThreatSignals.Select(signal => new FactionThreatSignalSnapshot(
signal.ScopeId,
signal.ScopeKind,
signal.EnemyShipCount,
signal.EnemyStationCount)).ToList());
}
if (commander?.Objectives is { Count: > 0 } runtimeObjectives)
{
objectives = runtimeObjectives
.OrderByDescending(objective => objective.Priority)
.Select(objective => new FactionObjectiveSnapshot(
objective.Id,
objective.Kind.ToString(),
objective.State.ToString(),
objective.Priority,
objective.ParentObjectiveId,
objective.TargetFactionId,
objective.TargetSystemId,
objective.TargetSiteId,
objective.TargetRegionId,
objective.CommodityId,
objective.ModuleId,
objective.BudgetWeight,
objective.SlotCost,
objective.CreatedAtCycle,
objective.UpdatedAtCycle,
objective.InvalidationReason,
objective.BlockingReason,
objective.PrerequisiteObjectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
objective.AssignedAssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
objective.Steps
.OrderByDescending(step => step.Priority)
.Select(step => new FactionPlanStepSnapshot(
step.Id,
step.Kind.ToString(),
step.Status.ToString(),
step.Priority,
step.CommodityId,
step.ModuleId,
step.TargetFactionId,
step.TargetSiteId,
step.BlockingReason,
step.Notes,
step.LastEvaluatedCycle,
step.DependencyStepIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
step.RequiredFacts.OrderBy(fact => fact, StringComparer.Ordinal).ToList(),
step.ProducedFacts.OrderBy(fact => fact, StringComparer.Ordinal).ToList(),
step.AssignedAssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
step.IssuedTaskIds.OrderBy(id => id, StringComparer.Ordinal).ToList()))
.ToList()))
.ToList();
}
if (commander?.IssuedTasks is { Count: > 0 } runtimeTasks)
{
issuedTasks = runtimeTasks
.OrderByDescending(task => task.Priority)
.Select(task => new FactionIssuedTaskSnapshot(
task.Id,
task.Kind.ToString(),
task.State.ToString(),
task.ObjectiveId,
task.StepId,
task.Priority,
task.ShipRole,
task.CommodityId,
task.ModuleId,
task.TargetFactionId,
task.TargetSystemId,
task.TargetSiteId,
task.CreatedAtCycle,
task.UpdatedAtCycle,
task.BlockingReason,
task.Notes,
task.AssignedAssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList()))
.ToList();
}
return new FactionDelta(
@@ -800,8 +943,11 @@ internal sealed class SimulationProjectionService
faction.ShipsBuilt,
faction.ShipsLost,
faction.DefaultPolicySetId,
goapState,
goapPriorities);
strategicAssessment,
strategicPriorities,
blackboard,
objectives,
issuedTasks);
}
private static ShipSpatialStateSnapshot ToShipSpatialStateSnapshot(ShipSpatialStateRuntime state) => new(