using Chessistics.Engine.Events; namespace Chessistics.Engine.Model; public class BoardState { public int Width { get; } public int Height { get; } public CellType[,] Grid { get; } public Dictionary Productions { get; } public Dictionary Demands { get; } public List Pieces { get; } public Dictionary ProductionBuffers { get; } public SimPhase Phase { get; set; } public int TurnNumber { get; set; } public int NextPieceId { get; set; } public Dictionary RemainingStock { get; } public int MaxDeadline { get; } // Tracks all cells ever occupied by a piece (for metrics) public HashSet OccupiedCells { get; } private readonly LevelDef _levelDef; private bool _isApplyingCommand; private BoardState(LevelDef level) { _levelDef = level; Width = level.Width; Height = level.Height; MaxDeadline = level.MaxDeadline; Grid = new CellType[Width, Height]; Productions = new Dictionary(); Demands = new Dictionary(); Pieces = new List(); ProductionBuffers = new Dictionary(); RemainingStock = new Dictionary(); OccupiedCells = new HashSet(); Phase = SimPhase.Edit; TurnNumber = 0; NextPieceId = 1; // Initialize grid as empty for (int c = 0; c < Width; c++) for (int r = 0; r < Height; r++) Grid[c, r] = CellType.Empty; // Place walls foreach (var wall in level.Walls) Grid[wall.Col, wall.Row] = CellType.Wall; // Place productions foreach (var prod in level.Productions) { Grid[prod.Position.Col, prod.Position.Row] = CellType.Production; Productions[prod.Position] = prod; ProductionBuffers[prod.Position] = null; } // Place demands foreach (var demand in level.Demands) { Grid[demand.Position.Col, demand.Position.Row] = CellType.Demand; Demands[demand.Position] = new DemandState(demand); } // Initialize stock foreach (var stock in level.Stock) RemainingStock[stock.Kind] = stock.Count; } public static BoardState FromLevel(LevelDef level) => new(level); public CellType GetCell(Coords coords) => Grid[coords.Col, coords.Row]; public bool IsOnBoard(Coords coords) => coords.IsOnBoard(Width, Height); /// /// Returns all cells currently occupied by any piece (both start and end during Edit, CurrentCell during sim). /// public HashSet GetOccupiedCells() { var occupied = new HashSet(); foreach (var piece in Pieces) { if (Phase == SimPhase.Edit) { occupied.Add(piece.StartCell); occupied.Add(piece.EndCell); } else { occupied.Add(piece.CurrentCell); } } return occupied; } public PieceState? GetPieceById(int pieceId) => Pieces.Find(p => p.Id == pieceId); public void ApplyCommand(Action> apply, List changeList) { if (_isApplyingCommand) throw new InvalidOperationException("Nested command not allowed."); _isApplyingCommand = true; try { apply(this, changeList); } finally { _isApplyingCommand = false; } } public void ResetFromLevel() { Pieces.Clear(); Productions.Clear(); Demands.Clear(); ProductionBuffers.Clear(); RemainingStock.Clear(); OccupiedCells.Clear(); Phase = SimPhase.Edit; TurnNumber = 0; NextPieceId = 1; for (int c = 0; c < Width; c++) for (int r = 0; r < Height; r++) Grid[c, r] = CellType.Empty; foreach (var wall in _levelDef.Walls) Grid[wall.Col, wall.Row] = CellType.Wall; foreach (var prod in _levelDef.Productions) { Grid[prod.Position.Col, prod.Position.Row] = CellType.Production; Productions[prod.Position] = prod; ProductionBuffers[prod.Position] = null; } foreach (var demand in _levelDef.Demands) { Grid[demand.Position.Col, demand.Position.Row] = CellType.Demand; Demands[demand.Position] = new DemandState(demand); } foreach (var stock in _levelDef.Stock) RemainingStock[stock.Kind] = stock.Count; } }