2026-04-10 14:58:03 +02:00
|
|
|
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(
|
2026-04-10 21:44:12 +02:00
|
|
|
new Coords(p.Col, p.Row), p.Name, ParseCargo(p.Cargo), p.Amount
|
2026-04-10 14:58:03 +02:00
|
|
|
)).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() ?? [],
|
2026-04-10 21:44:12 +02:00
|
|
|
Stock = dto.Stock.Select(s => new PieceStock(ParseKind(s.Kind), s.Count, s.Level)).ToList()
|
2026-04-10 14:58:03 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
Add Pion piece, surplus stock, and levels 4-6
- Pion (Pawn): orthogonal range 1, social status 1 (lowest), green color
- All existing levels get surplus stock including pawns for player choice
- Level 4 "Le Carrefour": 8x8, dual cargo diagonal routes, center wall block
- Level 5 "Le Labyrinthe": 8x6, wall corridors, knights essential for shortcuts
- Level 6 "Trois Royaumes": 10x8, 3 productions + 3 demands, full network puzzle
- Production interval concept removed (all produce every turn)
- GDD updated with Pion section and 6 level descriptions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:03:36 +02:00
|
|
|
"pawn" => PieceKind.Pawn,
|
2026-04-10 14:58:03 +02:00
|
|
|
"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; } = "";
|
2026-04-10 21:44:12 +02:00
|
|
|
public int Amount { get; set; } = 1;
|
2026-04-10 14:58:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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; }
|
2026-04-10 21:44:12 +02:00
|
|
|
public int Level { get; set; } = 1;
|
2026-04-10 14:58:03 +02:00
|
|
|
}
|
|
|
|
|
}
|