using Chessistics.Engine.Events; using Chessistics.Engine.Model; using Chessistics.Engine.Rules; using Chessistics.Engine.Simulation; namespace Chessistics.Engine.Commands; public class PlacePieceCommand : WorldCommand { public PieceKind Kind { get; } public Coords Start { get; } public Coords End { get; } public PlacePieceCommand(PieceKind kind, Coords start, Coords end) { Kind = kind; Start = start; End = end; } public override void AssertApplicationConditions(BoardState state) { if (state.Phase != SimPhase.Edit) throw new CommandRejectedException( new CommandRejectedEvent(nameof(PlacePieceCommand), "Can only place pieces during Edit phase.")); if (!state.RemainingStock.TryGetValue(Kind, out var remaining) || remaining <= 0) throw new CommandRejectedException( new PlacementRejectedEvent(Kind, Start, End, "No pieces of this type remaining in stock.")); if (!state.IsOnBoard(Start) || !state.IsOnBoard(End)) throw new CommandRejectedException( new PlacementRejectedEvent(Kind, Start, End, "Position is off the board.")); if (state.GetCell(Start) == CellType.Wall) throw new CommandRejectedException( new PlacementRejectedEvent(Kind, Start, End, "Cannot place on a wall.")); if (!MoveValidator.IsLegalPlacement(Kind, Start, End, state)) throw new CommandRejectedException( new PlacementRejectedEvent(Kind, Start, End, "Illegal move for this piece type.")); } protected override void DoApply(BoardState state, List changeList) { var piece = new PieceState( state.NextPieceId++, Kind, Start, End, state.Pieces.Count); state.Pieces.Add(piece); state.RemainingStock[Kind] = state.RemainingStock[Kind] - 1; state.OccupiedCells.Add(Start); state.OccupiedCells.Add(End); changeList.Add(new PiecePlacedEvent(piece.Id, Kind, Start, End)); } } public class RemovePieceCommand : WorldCommand { public int PieceId { get; } public RemovePieceCommand(int pieceId) { PieceId = pieceId; } public override void AssertApplicationConditions(BoardState state) { if (state.Phase != SimPhase.Edit) throw new CommandRejectedException( new CommandRejectedEvent(nameof(RemovePieceCommand), "Can only remove pieces during Edit phase.")); if (state.GetPieceById(PieceId) == null) throw new CommandRejectedException( new CommandRejectedEvent(nameof(RemovePieceCommand), $"Piece {PieceId} not found.")); } protected override void DoApply(BoardState state, List changeList) { var piece = state.GetPieceById(PieceId)!; state.Pieces.Remove(piece); state.RemainingStock[piece.Kind] = state.RemainingStock.GetValueOrDefault(piece.Kind) + 1; changeList.Add(new PieceRemovedEvent(PieceId)); } } public class StartSimulationCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Phase != SimPhase.Edit) throw new CommandRejectedException( new CommandRejectedEvent(nameof(StartSimulationCommand), "Can only start from Edit phase.")); if (state.Pieces.Count == 0) throw new CommandRejectedException( new CommandRejectedEvent(nameof(StartSimulationCommand), "Place at least one piece before starting.")); } protected override void DoApply(BoardState state, List changeList) { state.Phase = SimPhase.Running; changeList.Add(new SimulationStartedEvent()); } } public class PauseSimulationCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Phase != SimPhase.Running) throw new CommandRejectedException( new CommandRejectedEvent(nameof(PauseSimulationCommand), "Can only pause while running.")); } protected override void DoApply(BoardState state, List changeList) { state.Phase = SimPhase.Paused; changeList.Add(new SimulationPausedEvent()); } } public class ResumeSimulationCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Phase != SimPhase.Paused) throw new CommandRejectedException( new CommandRejectedEvent(nameof(ResumeSimulationCommand), "Can only resume from Paused phase.")); } protected override void DoApply(BoardState state, List changeList) { state.Phase = SimPhase.Running; changeList.Add(new SimulationResumedEvent()); } } public class StepSimulationCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Phase == SimPhase.Edit && state.Pieces.Count == 0) throw new CommandRejectedException( new CommandRejectedEvent(nameof(StepSimulationCommand), "Place at least one piece before stepping.")); if (state.Phase != SimPhase.Edit && state.Phase != SimPhase.Running && state.Phase != SimPhase.Paused) throw new CommandRejectedException( new CommandRejectedEvent(nameof(StepSimulationCommand), "Cannot step in current phase.")); } protected override void DoApply(BoardState state, List changeList) { if (state.Phase == SimPhase.Edit) state.Phase = SimPhase.Paused; TurnExecutor.ExecuteTurn(state, changeList); // After a step, remain in Paused unless victory/defeat/collision occurred if (state.Phase == SimPhase.Running) state.Phase = SimPhase.Paused; } } public class StopSimulationCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Phase == SimPhase.Edit) throw new CommandRejectedException( new CommandRejectedEvent(nameof(StopSimulationCommand), "Already in Edit phase.")); } protected override void DoApply(BoardState state, List changeList) { foreach (var piece in state.Pieces) { piece.CurrentCell = piece.StartCell; piece.Cargo = null; } foreach (var pos in state.ProductionBuffers.Keys.ToList()) state.ProductionBuffers[pos] = null; foreach (var demand in state.Demands.Values) demand.ReceivedCount = 0; state.TurnNumber = 0; state.Phase = SimPhase.Edit; changeList.Add(new SimulationStoppedEvent()); } } public class ResetLevelCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { // Reset is always valid } protected override void DoApply(BoardState state, List changeList) { state.ResetFromLevel(); changeList.Add(new LevelResetEvent()); } }