using Chessistics.Engine.Commands; using Chessistics.Engine.Events; using Chessistics.Engine.Model; using Chessistics.Tests.Helpers; using Xunit; namespace Chessistics.Tests.Simulation; public class UndoTests { private SimHelper CreateSim() { var level = new BoardBuilder(4, 4) .WithProduction(0, 0, "Scierie", CargoType.Wood, amount: 1) .WithDemand(3, 0, "Depot", CargoType.Wood, 3, 30) .WithStock(PieceKind.Rook, 3) .Build(); return SimHelper.FromLevel(level); } [Fact] public void Undo_EmptyStack_ReturnsNoEvents() { var sim = CreateSim(); Assert.False(sim.Sim.CanUndo); Assert.Empty(sim.Sim.Undo()); } [Fact] public void Undo_AfterPlace_RestoresPreviousState() { var sim = CreateSim(); Assert.False(sim.Sim.CanUndo); sim.Place(PieceKind.Rook, (0, 0), (1, 0)); Assert.Single(sim.Snapshot.Pieces); Assert.True(sim.Sim.CanUndo); var events = sim.Sim.Undo(); Assert.Single(events); Assert.IsType(events[0]); Assert.Empty(sim.Snapshot.Pieces); Assert.Equal(3, sim.Snapshot.RemainingStock[PieceKind.Rook]); Assert.False(sim.Sim.CanUndo); } [Fact] public void Undo_AfterRemove_RestoresPiece() { var sim = CreateSim(); sim.Place(PieceKind.Rook, (0, 0), (1, 0)); var pieceId = sim.Snapshot.Pieces[0].Id; sim.Remove(pieceId); Assert.Empty(sim.Snapshot.Pieces); sim.Sim.Undo(); Assert.Single(sim.Snapshot.Pieces); Assert.Equal(pieceId, sim.Snapshot.Pieces[0].Id); } [Fact] public void Undo_MultipleMutations_UndoneInReverseOrder() { var sim = CreateSim(); sim.Place(PieceKind.Rook, (0, 0), (1, 0)); sim.Place(PieceKind.Rook, (0, 1), (1, 1)); sim.Place(PieceKind.Rook, (0, 2), (1, 2)); Assert.Equal(3, sim.Snapshot.Pieces.Count); sim.Sim.Undo(); Assert.Equal(2, sim.Snapshot.Pieces.Count); sim.Sim.Undo(); Assert.Single(sim.Snapshot.Pieces); sim.Sim.Undo(); Assert.Empty(sim.Snapshot.Pieces); Assert.False(sim.Sim.CanUndo); } [Fact] public void Undo_RejectedPlacement_DoesNotCheckpoint() { var sim = CreateSim(); // Invalid placement (off the board) — should be rejected and not undoable sim.Place(PieceKind.Rook, (99, 99), (100, 100)); Assert.False(sim.Sim.CanUndo); } [Fact] public void Undo_AfterSimulationStep_RewindsTurnsToo() { // Placing then stepping means the sim advanced. Undo of the placement // should restore the pre-placement state — which also means turn 0. var sim = CreateSim(); sim.Place(PieceKind.Rook, (0, 0), (1, 0)); sim.Step(); sim.Step(); Assert.Equal(2, sim.Snapshot.TurnNumber); sim.Sim.Undo(); Assert.Equal(0, sim.Snapshot.TurnNumber); Assert.Empty(sim.Snapshot.Pieces); } [Fact] public void QuickLoad_ClearsUndoStack() { var sim = CreateSim(); sim.Place(PieceKind.Rook, (0, 0), (1, 0)); sim.Sim.QuickSave(); sim.Place(PieceKind.Rook, (0, 1), (1, 1)); // Two undoable mutations so far — load clears the stack sim.Sim.QuickLoad(); Assert.False(sim.Sim.CanUndo); } }