136 lines
4.8 KiB
C#
136 lines
4.8 KiB
C#
|
|
using Chessistics.Engine.Commands;
|
||
|
|
using Chessistics.Engine.Loading;
|
||
|
|
using Chessistics.Engine.Model;
|
||
|
|
using Chessistics.Engine.Simulation;
|
||
|
|
using Xunit;
|
||
|
|
|
||
|
|
namespace Chessistics.Tests.Loading;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Validates campaign_01.json structural integrity:
|
||
|
|
/// no cell overlaps, unique building names, walls only on new cells.
|
||
|
|
/// </summary>
|
||
|
|
public class CampaignValidationTests
|
||
|
|
{
|
||
|
|
private static CampaignDef LoadCampaign()
|
||
|
|
=> CampaignLoader.LoadFromFile("../../../../Data/campaigns/campaign_01.json");
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void Campaign01_NoBuildingOverlaps()
|
||
|
|
{
|
||
|
|
var campaign = LoadCampaign();
|
||
|
|
var sim = new GameSim(campaign);
|
||
|
|
sim.ProcessCommand(new LoadCampaignCommand());
|
||
|
|
|
||
|
|
// Step through missions by manually applying patches and checking for conflicts
|
||
|
|
var state = BoardState.FromCampaign(campaign);
|
||
|
|
|
||
|
|
// Track all active buildings after each mission
|
||
|
|
for (int i = 0; i < campaign.Missions.Count; i++)
|
||
|
|
{
|
||
|
|
var mission = campaign.Missions[i];
|
||
|
|
state.ApplyTerrainPatch(mission.TerrainPatch, i);
|
||
|
|
|
||
|
|
// After each mission, verify no cell has multiple building types
|
||
|
|
foreach (var pos in state.Productions.Keys)
|
||
|
|
{
|
||
|
|
Assert.False(state.Demands.ContainsKey(pos),
|
||
|
|
$"Mission {i + 1}: Production and Demand overlap at {pos}");
|
||
|
|
Assert.False(state.Transformers.ContainsKey(pos),
|
||
|
|
$"Mission {i + 1}: Production and Transformer overlap at {pos}");
|
||
|
|
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var pos in state.Demands.Keys)
|
||
|
|
{
|
||
|
|
Assert.False(state.Transformers.ContainsKey(pos),
|
||
|
|
$"Mission {i + 1}: Demand and Transformer overlap at {pos}");
|
||
|
|
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var pos in state.Transformers.Keys)
|
||
|
|
{
|
||
|
|
Assert.NotEqual(CellType.Wall, state.GetCell(pos));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void Campaign01_UniqueBuildingNames()
|
||
|
|
{
|
||
|
|
var campaign = LoadCampaign();
|
||
|
|
var state = BoardState.FromCampaign(campaign);
|
||
|
|
|
||
|
|
for (int i = 0; i < campaign.Missions.Count; i++)
|
||
|
|
{
|
||
|
|
state.ApplyTerrainPatch(campaign.Missions[i].TerrainPatch, i);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Collect all building names
|
||
|
|
var names = new List<string>();
|
||
|
|
names.AddRange(state.Productions.Values.Select(p => p.Name));
|
||
|
|
names.AddRange(state.Demands.Values.Select(d => d.Name));
|
||
|
|
names.AddRange(state.Transformers.Values.Select(t => t.Name));
|
||
|
|
|
||
|
|
var duplicates = names.GroupBy(n => n).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
|
||
|
|
Assert.Empty(duplicates);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void Campaign01_WallsOnlyOnNewOrEmptyCells()
|
||
|
|
{
|
||
|
|
var campaign = LoadCampaign();
|
||
|
|
|
||
|
|
// Track which cells have buildings before each mission
|
||
|
|
var buildingCells = new HashSet<Coords>();
|
||
|
|
int prevWidth = campaign.InitialWidth;
|
||
|
|
int prevHeight = campaign.InitialHeight;
|
||
|
|
|
||
|
|
for (int i = 0; i < campaign.Missions.Count; i++)
|
||
|
|
{
|
||
|
|
var mission = campaign.Missions[i];
|
||
|
|
|
||
|
|
// Check walls in this mission's patch
|
||
|
|
foreach (var cell in mission.TerrainPatch.Cells)
|
||
|
|
{
|
||
|
|
if (cell.Type == CellType.Wall)
|
||
|
|
{
|
||
|
|
// Wall should be on a cell that was either:
|
||
|
|
// - Outside the previous board dimensions (new cell)
|
||
|
|
// - Not a building cell
|
||
|
|
bool isNewCell = cell.Col >= prevWidth || cell.Row >= prevHeight;
|
||
|
|
bool isBuilding = buildingCells.Contains(new Coords(cell.Col, cell.Row));
|
||
|
|
|
||
|
|
Assert.True(isNewCell || !isBuilding,
|
||
|
|
$"Mission {i + 1}: Wall at ({cell.Col},{cell.Row}) overwrites an existing building");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update building tracking
|
||
|
|
foreach (var cell in mission.TerrainPatch.Cells)
|
||
|
|
{
|
||
|
|
var coords = new Coords(cell.Col, cell.Row);
|
||
|
|
if (cell.Type is CellType.Production or CellType.Demand or CellType.Transformer)
|
||
|
|
buildingCells.Add(coords);
|
||
|
|
else
|
||
|
|
buildingCells.Remove(coords);
|
||
|
|
}
|
||
|
|
|
||
|
|
prevWidth = mission.TerrainPatch.NewWidth;
|
||
|
|
prevHeight = mission.TerrainPatch.NewHeight;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public void Campaign01_AllMissionsHaveFlavor()
|
||
|
|
{
|
||
|
|
var campaign = LoadCampaign();
|
||
|
|
|
||
|
|
for (int i = 0; i < campaign.Missions.Count; i++)
|
||
|
|
{
|
||
|
|
Assert.False(string.IsNullOrWhiteSpace(campaign.Missions[i].Flavor),
|
||
|
|
$"Mission {i + 1} ({campaign.Missions[i].Name}) has no flavor text");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|