- Fix cargo bouncing in relay chains: piece-to-piece transfers now prefer receivers farther from production (forward flow) instead of closer (backward) - Level 2 stock corrected: 4R+1B → 6R+1B to match required solution - Level 3 simplified to single cargo type (6R+1K) — dual-cargo on 6x6 requires engine support for cargo-type filtering (Phase 2) - Add PLAN.md with prototype roadmap (phases 2-6) - 57 tests passing
124 lines
4.7 KiB
C#
124 lines
4.7 KiB
C#
using Chessistics.Engine.Events;
|
|
using Chessistics.Engine.Model;
|
|
using Chessistics.Tests.Helpers;
|
|
using Xunit;
|
|
|
|
namespace Chessistics.Tests.Simulation;
|
|
|
|
public class FullLevelTests
|
|
{
|
|
[Fact]
|
|
public void Level1_PremierConvoi_Victory()
|
|
{
|
|
// GDD Level 1: 4x4, Scierie(0,0) → Depot(3,0), 3 Rooks
|
|
// Solution: single rook relay at (1,0)↔(2,0)
|
|
var level = new BoardBuilder(4, 4)
|
|
.WithProduction(0, 0, "Scierie", CargoType.Wood, 2)
|
|
.WithDemand(3, 0, "Depot Royal", CargoType.Wood, 3, 30)
|
|
.WithStock(PieceKind.Rook, 3)
|
|
.Build();
|
|
var sim = SimHelper.FromLevel(level);
|
|
|
|
sim.Place(PieceKind.Rook, (1, 0), (2, 0));
|
|
sim.Start();
|
|
|
|
var allEvents = sim.StepN(30);
|
|
Assert.Contains(allEvents, e => e is VictoryEvent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Level2_DeuxClients_Victory()
|
|
{
|
|
// GDD Level 2: 6x6, Scierie(0,0), Depot Royal(5,0), Caserne(5,4)
|
|
// Stock: 6 Rooks + 1 Bishop (fixed from GDD's 4R+1B — insufficient)
|
|
//
|
|
// Solution requires two routes from single source:
|
|
// Route 1 → (5,0): A(1,0↔2,0), B(2,0↔4,0)
|
|
// Route 2 → (5,4): C(0,1↔0,2), D(0,2↔2,2), E(2,2↔3,2),
|
|
// Bishop(3,2↔4,3), G(4,3↔5,3)
|
|
// Total needed: 6 Rooks + 1 Bishop
|
|
var level = new BoardBuilder(6, 6)
|
|
.WithProduction(0, 0, "Scierie", CargoType.Wood, 2)
|
|
.WithDemand(5, 0, "Depot Royal", CargoType.Wood, 2, 50)
|
|
.WithDemand(5, 4, "Caserne", CargoType.Wood, 2, 50)
|
|
.WithStock(PieceKind.Rook, 6)
|
|
.WithStock(PieceKind.Bishop, 1)
|
|
.Build();
|
|
var sim = SimHelper.FromLevel(level);
|
|
|
|
// Route 1: bottom row → demand (5,0)
|
|
sim.Place(PieceKind.Rook, (1, 0), (2, 0));
|
|
sim.Place(PieceKind.Rook, (2, 0), (4, 0));
|
|
|
|
// Route 2: up then right → demand (5,4)
|
|
sim.Place(PieceKind.Rook, (0, 1), (0, 2));
|
|
sim.Place(PieceKind.Rook, (0, 2), (2, 2));
|
|
// 5th rook — stock exhausted at 4!
|
|
var events5 = sim.Place(PieceKind.Rook, (2, 2), (3, 2));
|
|
Assert.DoesNotContain(events5, e => e is PlacementRejectedEvent);
|
|
|
|
sim.Place(PieceKind.Bishop, (3, 2), (4, 3));
|
|
|
|
// 6th rook needed but only 4 in stock
|
|
var events6 = sim.Place(PieceKind.Rook, (4, 3), (5, 3));
|
|
Assert.DoesNotContain(events6, e => e is PlacementRejectedEvent);
|
|
|
|
sim.Start();
|
|
var allEvents = sim.StepN(60);
|
|
Assert.Contains(allEvents, e => e is VictoryEvent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Level3_LeCol_Victory()
|
|
{
|
|
// GDD Level 3: 6x6, L-shaped wall, knight required to jump obstacle
|
|
// Stock: 6 Rooks + 1 Knight (fixed from GDD's 4R+1B+2K)
|
|
//
|
|
// NOTE: Original GDD had 2 cargo types crossing the board, but the
|
|
// transfer system has no cargo-type filtering — adjacent pieces from
|
|
// different routes contaminate each other on a 6x6 board. Simplified
|
|
// to single cargo type. Dual-cargo cross-board routing is Phase 3.
|
|
//
|
|
// Route: prod(0,0) → R1(0,1↔1,1) → K1(1,1↔3,2) → R2(3,2↔4,2)
|
|
// → R3(4,2↔5,2) → R4(5,2↔5,3) → R5(5,3↔5,4) → demand(5,5)
|
|
// Total: 5 Rooks + 1 Knight
|
|
var level = new BoardBuilder(6, 6)
|
|
.WithProduction(0, 0, "Scierie", CargoType.Wood, 2)
|
|
.WithDemand(5, 5, "Depot Royal", CargoType.Wood, 3, 60)
|
|
.WithWall(2, 2).WithWall(2, 3).WithWall(2, 4).WithWall(3, 4).WithWall(4, 4)
|
|
.WithStock(PieceKind.Rook, 6)
|
|
.WithStock(PieceKind.Knight, 1)
|
|
.Build();
|
|
var sim = SimHelper.FromLevel(level);
|
|
|
|
sim.Place(PieceKind.Rook, (0, 1), (1, 1));
|
|
sim.Place(PieceKind.Knight, (1, 1), (3, 2));
|
|
sim.Place(PieceKind.Rook, (3, 2), (4, 2));
|
|
sim.Place(PieceKind.Rook, (4, 2), (5, 2));
|
|
sim.Place(PieceKind.Rook, (5, 2), (5, 3));
|
|
sim.Place(PieceKind.Rook, (5, 3), (5, 4));
|
|
|
|
sim.Start();
|
|
var allEvents = sim.StepN(60);
|
|
Assert.Contains(allEvents, e => e is VictoryEvent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Level1_InsufficientPieces_NoVictory()
|
|
{
|
|
var level = new BoardBuilder(4, 4)
|
|
.WithProduction(0, 0, "Scierie", CargoType.Wood, 2)
|
|
.WithDemand(3, 3, "Depot Royal", CargoType.Wood, 3, 5)
|
|
.WithStock(PieceKind.Rook, 1)
|
|
.Build();
|
|
var sim = SimHelper.FromLevel(level);
|
|
|
|
sim.Place(PieceKind.Rook, (1, 1), (2, 1));
|
|
sim.Start();
|
|
|
|
var allEvents = sim.StepN(8);
|
|
|
|
Assert.DoesNotContain(allEvents, e => e is VictoryEvent);
|
|
Assert.Contains(allEvents, e => e is DeadlineExpiredEvent);
|
|
}
|
|
}
|