Black box sim engine (commands in, events out) with 3 piece types (Rook, Bishop, Knight), cargo transfer system with social status priority, collision detection, and victory/defeat conditions. 57 tests covering rules, simulation, loading, and solvability. Godot 4 presentation layer scaffolding.
119 lines
3.9 KiB
C#
119 lines
3.9 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using Chessistics.Engine.Model;
|
|
|
|
namespace Chessistics.Engine.Loading;
|
|
|
|
public static class LevelLoader
|
|
{
|
|
private static readonly JsonSerializerOptions Options = new()
|
|
{
|
|
PropertyNameCaseInsensitive = true,
|
|
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
|
};
|
|
|
|
public static LevelDef Load(string json)
|
|
{
|
|
var dto = JsonSerializer.Deserialize<LevelDto>(json, Options)
|
|
?? throw new JsonException("Failed to deserialize level JSON.");
|
|
|
|
Validate(dto);
|
|
|
|
return new LevelDef
|
|
{
|
|
Id = dto.Id,
|
|
Name = dto.Name,
|
|
Description = dto.Description ?? "",
|
|
Width = dto.Width,
|
|
Height = dto.Height,
|
|
Productions = dto.Productions.Select(p => new ProductionDef(
|
|
new Coords(p.Col, p.Row), p.Name, ParseCargo(p.Cargo), p.Interval
|
|
)).ToList(),
|
|
Demands = dto.Demands.Select(d => new DemandDef(
|
|
new Coords(d.Col, d.Row), d.Name, ParseCargo(d.Cargo), d.Amount, d.Deadline
|
|
)).ToList(),
|
|
Walls = dto.Walls?.Select(w => new Coords(w.Col, w.Row)).ToList() ?? [],
|
|
Stock = dto.Stock.Select(s => new PieceStock(ParseKind(s.Kind), s.Count)).ToList()
|
|
};
|
|
}
|
|
|
|
public static LevelDef LoadFromFile(string path)
|
|
{
|
|
var json = File.ReadAllText(path);
|
|
return Load(json);
|
|
}
|
|
|
|
private static CargoType ParseCargo(string cargo) => cargo.ToLowerInvariant() switch
|
|
{
|
|
"wood" => CargoType.Wood,
|
|
"stone" => CargoType.Stone,
|
|
_ => throw new JsonException($"Unknown cargo type: '{cargo}'")
|
|
};
|
|
|
|
private static PieceKind ParseKind(string kind) => kind.ToLowerInvariant() switch
|
|
{
|
|
"rook" => PieceKind.Rook,
|
|
"bishop" => PieceKind.Bishop,
|
|
"knight" => PieceKind.Knight,
|
|
_ => throw new JsonException($"Unknown piece kind: '{kind}'")
|
|
};
|
|
|
|
private static void Validate(LevelDto dto)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(dto.Name))
|
|
throw new JsonException("Level name is required.");
|
|
if (dto.Width <= 0 || dto.Height <= 0)
|
|
throw new JsonException("Level dimensions must be positive.");
|
|
if (dto.Productions.Count == 0)
|
|
throw new JsonException("Level must have at least one production.");
|
|
if (dto.Demands.Count == 0)
|
|
throw new JsonException("Level must have at least one demand.");
|
|
if (dto.Stock.Count == 0)
|
|
throw new JsonException("Level must have at least one piece in stock.");
|
|
}
|
|
|
|
// DTOs for JSON deserialization
|
|
private class LevelDto
|
|
{
|
|
public int Id { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string? Description { get; set; }
|
|
public int Width { get; set; }
|
|
public int Height { get; set; }
|
|
public List<ProductionDto> Productions { get; set; } = [];
|
|
public List<DemandDto> Demands { get; set; } = [];
|
|
public List<CoordsDto>? Walls { get; set; }
|
|
public List<StockDto> Stock { get; set; } = [];
|
|
}
|
|
|
|
private class ProductionDto
|
|
{
|
|
public int Col { get; set; }
|
|
public int Row { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Cargo { get; set; } = "";
|
|
public int Interval { get; set; }
|
|
}
|
|
|
|
private class DemandDto
|
|
{
|
|
public int Col { get; set; }
|
|
public int Row { get; set; }
|
|
public string Name { get; set; } = "";
|
|
public string Cargo { get; set; } = "";
|
|
public int Amount { get; set; }
|
|
public int Deadline { get; set; }
|
|
}
|
|
|
|
private class CoordsDto
|
|
{
|
|
public int Col { get; set; }
|
|
public int Row { get; set; }
|
|
}
|
|
|
|
private class StockDto
|
|
{
|
|
public string Kind { get; set; } = "";
|
|
public int Count { get; set; }
|
|
}
|
|
}
|