355 lines
12 KiB
C#
355 lines
12 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace WebAssembly.Throwaway;
|
|
|
|
public sealed class ResourceAmounts
|
|
{
|
|
public int Draft { get; set; }
|
|
public int Food { get; set; }
|
|
public int Knowledge { get; set; }
|
|
public int Industry { get; set; }
|
|
public int Magic { get; set; }
|
|
public int Gold { get; set; }
|
|
public int Imperial { get; set; }
|
|
public int Stability { get; set; }
|
|
|
|
[JsonIgnore]
|
|
public bool IsZero => Draft == 0 && Food == 0 && Knowledge == 0 && Industry == 0 && Magic == 0 && Gold == 0 &&
|
|
Imperial == 0 && Stability == 0;
|
|
|
|
public void Add(ResourceAmounts other)
|
|
{
|
|
Draft += other.Draft;
|
|
Food += other.Food;
|
|
Knowledge += other.Knowledge;
|
|
Industry += other.Industry;
|
|
Magic += other.Magic;
|
|
Gold += other.Gold;
|
|
Imperial += other.Imperial;
|
|
Stability += other.Stability;
|
|
}
|
|
|
|
public void Subtract(ResourceAmounts other)
|
|
{
|
|
Draft -= other.Draft;
|
|
Food -= other.Food;
|
|
Knowledge -= other.Knowledge;
|
|
Industry -= other.Industry;
|
|
Magic -= other.Magic;
|
|
Gold -= other.Gold;
|
|
Imperial -= other.Imperial;
|
|
Stability -= other.Stability;
|
|
}
|
|
|
|
public static ResourceAmounts operator +(ResourceAmounts a, ResourceAmounts b)
|
|
{
|
|
return new ResourceAmounts
|
|
{
|
|
Draft = a.Draft + b.Draft,
|
|
Food = a.Food + b.Food,
|
|
Knowledge = a.Knowledge + b.Knowledge,
|
|
Industry = a.Industry + b.Industry,
|
|
Magic = a.Magic + b.Magic,
|
|
Gold = a.Gold + b.Gold,
|
|
Imperial = a.Imperial + b.Imperial,
|
|
Stability = a.Stability + b.Stability
|
|
};
|
|
}
|
|
|
|
public static ResourceAmounts operator -(ResourceAmounts a, ResourceAmounts b)
|
|
{
|
|
return new ResourceAmounts
|
|
{
|
|
Draft = a.Draft - b.Draft,
|
|
Food = a.Food - b.Food,
|
|
Knowledge = a.Knowledge - b.Knowledge,
|
|
Industry = a.Industry - b.Industry,
|
|
Magic = a.Magic - b.Magic,
|
|
Gold = a.Gold - b.Gold,
|
|
Imperial = a.Imperial - b.Imperial,
|
|
Stability = a.Stability - b.Stability
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed class BuildingDefinition
|
|
{
|
|
public string Id { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public string? Image { get; set; }
|
|
public string SourceId { get; set; } = "General";
|
|
public List<string> RequirementIds { get; set; } = new();
|
|
public ResourceAmounts Income { get; set; } = new();
|
|
public ResourceAmounts Upkeep { get; set; } = new();
|
|
public ResourceAmounts Cost { get; set; } = new();
|
|
public int BoostPopulation { get; set; }
|
|
public int BoostForester { get; set; }
|
|
public int BoostFarm { get; set; }
|
|
public int BoostQuarry { get; set; }
|
|
public int BoostGoldMine { get; set; }
|
|
public int BoostConduit { get; set; }
|
|
}
|
|
|
|
public sealed class BuildOrderEntry
|
|
{
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
public string ItemId { get; set; } = string.Empty;
|
|
public string Name { get; set; } = string.Empty;
|
|
public int RequestedTurn { get; set; }
|
|
public int BuildStartTurn { get; set; }
|
|
public int BuiltFinishTurn { get; set; }
|
|
public int IndustryCostRemaining { get; set; }
|
|
public BuildingDefinition Definition { get; set; } = new();
|
|
}
|
|
|
|
public sealed class ResourceSnapshot
|
|
{
|
|
public int Turn { get; set; }
|
|
public ResourceAmounts Stored { get; set; } = new();
|
|
public ResourceAmounts TotalIncome { get; set; } = new();
|
|
public ResourceAmounts TotalUpkeep { get; set; } = new();
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class BuildPlanResult
|
|
{
|
|
public List<BuildOrderEntry> BuildOrder { get; set; } = new();
|
|
public List<ResourceSnapshot> ResourceHistory { get; set; } = new();
|
|
public ResourceAmounts StartingPool { get; set; } = new();
|
|
}
|
|
|
|
public sealed class BuildOrderRequest
|
|
{
|
|
public string ItemId { get; init; } = string.Empty;
|
|
public BuildingDefinition Definition { get; init; } = new();
|
|
public int RequestedTurn { get; init; }
|
|
}
|
|
|
|
public static class BuildingPlanCalculator
|
|
{
|
|
public static BuildPlanResult CalculateBuildPlan(
|
|
int totalTurns,
|
|
IEnumerable<BuildOrderRequest> buildRequests,
|
|
IEnumerable<BuildingDefinition>? startingBuildings = null,
|
|
ResourceAmounts? startingResources = null)
|
|
{
|
|
var activeBuildings = new List<BuildOrderEntry>();
|
|
var result = new BuildPlanResult
|
|
{
|
|
StartingPool = startingResources ?? new ResourceAmounts()
|
|
};
|
|
|
|
var stored = new ResourceAmounts();
|
|
if (startingResources is not null)
|
|
stored = new ResourceAmounts
|
|
{
|
|
Draft = startingResources.Draft,
|
|
Food = startingResources.Food,
|
|
Knowledge = startingResources.Knowledge,
|
|
Industry = startingResources.Industry,
|
|
Magic = startingResources.Magic,
|
|
Gold = startingResources.Gold,
|
|
Imperial = startingResources.Imperial,
|
|
Stability = startingResources.Stability
|
|
};
|
|
|
|
if (startingBuildings is not null)
|
|
foreach (var startingBuilding in startingBuildings)
|
|
{
|
|
var entry = new BuildOrderEntry
|
|
{
|
|
ItemId = startingBuilding.Id,
|
|
Name = startingBuilding.Name,
|
|
RequestedTurn = 1,
|
|
BuildStartTurn = 1,
|
|
BuiltFinishTurn = 0,
|
|
IndustryCostRemaining = 0,
|
|
Definition = startingBuilding
|
|
};
|
|
activeBuildings.Add(entry);
|
|
result.BuildOrder.Add(entry);
|
|
}
|
|
|
|
var requestsByTurn = buildRequests
|
|
.OrderBy(r => r.RequestedTurn)
|
|
.GroupBy(r => r.RequestedTurn)
|
|
.ToDictionary(g => g.Key, g => g.ToList());
|
|
|
|
var projects = new List<BuildOrderEntry>();
|
|
|
|
for (var turn = 1; turn <= totalTurns; turn++)
|
|
{
|
|
var incomeThisTurn = new ResourceAmounts();
|
|
var upkeepThisTurn = new ResourceAmounts();
|
|
|
|
foreach (var building in activeBuildings)
|
|
if (building.BuiltFinishTurn == 0 || turn > building.BuiltFinishTurn)
|
|
{
|
|
incomeThisTurn.Add(building.Definition.Income);
|
|
upkeepThisTurn.Add(building.Definition.Upkeep);
|
|
}
|
|
|
|
stored.Add(incomeThisTurn);
|
|
stored.Subtract(upkeepThisTurn);
|
|
|
|
if (requestsByTurn.TryGetValue(turn, out var requests))
|
|
foreach (var request in requests)
|
|
{
|
|
var nextProject = new BuildOrderEntry
|
|
{
|
|
ItemId = request.ItemId,
|
|
Name = request.Definition.Name,
|
|
RequestedTurn = turn,
|
|
BuildStartTurn = turn,
|
|
BuiltFinishTurn = 0,
|
|
IndustryCostRemaining = request.Definition.Cost.Industry,
|
|
Definition = request.Definition
|
|
};
|
|
|
|
stored.Subtract(new ResourceAmounts
|
|
{
|
|
Draft = request.Definition.Cost.Draft,
|
|
Food = request.Definition.Cost.Food,
|
|
Knowledge = request.Definition.Cost.Knowledge,
|
|
Magic = request.Definition.Cost.Magic,
|
|
Gold = request.Definition.Cost.Gold,
|
|
Imperial = request.Definition.Cost.Imperial,
|
|
Stability = 0
|
|
});
|
|
|
|
projects.Add(nextProject);
|
|
result.BuildOrder.Add(nextProject);
|
|
}
|
|
|
|
var availableIndustry = incomeThisTurn.Industry;
|
|
foreach (var project in projects.Where(p => p.BuiltFinishTurn == 0).OrderBy(p => p.RequestedTurn))
|
|
{
|
|
if (availableIndustry <= 0 || project.IndustryCostRemaining <= 0) continue;
|
|
|
|
var applied = Math.Min(availableIndustry, project.IndustryCostRemaining);
|
|
project.IndustryCostRemaining -= applied;
|
|
availableIndustry -= applied;
|
|
|
|
if (project.IndustryCostRemaining <= 0)
|
|
{
|
|
project.BuiltFinishTurn = turn;
|
|
activeBuildings.Add(project);
|
|
}
|
|
}
|
|
|
|
result.ResourceHistory.Add(new ResourceSnapshot
|
|
{
|
|
Turn = turn,
|
|
Stored = new ResourceAmounts
|
|
{
|
|
Draft = stored.Draft,
|
|
Food = stored.Food,
|
|
Knowledge = stored.Knowledge,
|
|
Industry = stored.Industry,
|
|
Magic = stored.Magic,
|
|
Gold = stored.Gold,
|
|
Imperial = stored.Imperial,
|
|
Stability = stored.Stability
|
|
},
|
|
TotalIncome = incomeThisTurn,
|
|
TotalUpkeep = upkeepThisTurn,
|
|
Notes = null
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static IReadOnlyList<BuildingDefinition> GetDefaultStartingBuildings()
|
|
{
|
|
return new List<BuildingDefinition>
|
|
{
|
|
new()
|
|
{
|
|
Id = "town-hall-1",
|
|
Name = "Town Hall I",
|
|
Description = "Starting civic center that produces basic resources and morale.",
|
|
SourceId = "General",
|
|
Income = new ResourceAmounts
|
|
{
|
|
Draft = 20,
|
|
Food = 30,
|
|
Industry = 20,
|
|
Gold = 0,
|
|
Stability = 10
|
|
},
|
|
Upkeep = new ResourceAmounts(),
|
|
Cost = new ResourceAmounts()
|
|
},
|
|
new()
|
|
{
|
|
Id = "throne",
|
|
Name = "Throne",
|
|
Description = "Royal treasury building that provides steady gold income.",
|
|
SourceId = "General",
|
|
Income = new ResourceAmounts
|
|
{
|
|
Gold = 120
|
|
},
|
|
Upkeep = new ResourceAmounts(),
|
|
Cost = new ResourceAmounts()
|
|
}
|
|
};
|
|
}
|
|
|
|
public static BuildPlanResult CreateSampleBuildPlan(int totalTurns = 60)
|
|
{
|
|
var sampleRequests = new List<BuildOrderRequest>
|
|
{
|
|
new()
|
|
{
|
|
ItemId = "farm-1",
|
|
Definition = new BuildingDefinition
|
|
{
|
|
Id = "farm-1",
|
|
Name = "Farm",
|
|
Description = "Provides food income and supports future growth.",
|
|
SourceId = "General",
|
|
Income = new ResourceAmounts { Food = 15 },
|
|
Upkeep = new ResourceAmounts { Gold = 2 },
|
|
Cost = new ResourceAmounts { Gold = 80, Industry = 40 }
|
|
},
|
|
RequestedTurn = 2
|
|
},
|
|
new()
|
|
{
|
|
ItemId = "workshop-1",
|
|
Definition = new BuildingDefinition
|
|
{
|
|
Id = "workshop-1",
|
|
Name = "Workshop",
|
|
Description = "Improves production and generates industry.",
|
|
SourceId = "General",
|
|
Income = new ResourceAmounts { Industry = 20 },
|
|
Upkeep = new ResourceAmounts { Gold = 4 },
|
|
Cost = new ResourceAmounts { Gold = 110, Industry = 80 }
|
|
},
|
|
RequestedTurn = 5
|
|
},
|
|
new()
|
|
{
|
|
ItemId = "market-1",
|
|
Definition = new BuildingDefinition
|
|
{
|
|
Id = "market-1",
|
|
Name = "Market",
|
|
Description = "Provides ongoing gold income and supports trade.",
|
|
SourceId = "General",
|
|
Income = new ResourceAmounts { Gold = 40 },
|
|
Upkeep = new ResourceAmounts { Gold = 6 },
|
|
Cost = new ResourceAmounts { Gold = 180, Industry = 100 }
|
|
},
|
|
RequestedTurn = 12
|
|
}
|
|
};
|
|
|
|
return CalculateBuildPlan(totalTurns, sampleRequests, GetDefaultStartingBuildings(),
|
|
new ResourceAmounts { Gold = 0 });
|
|
}
|
|
} |