using MessagePack; using OECS; namespace Game.TicTacToe; /// /// Places the current player's mark on the cell at (Row, Col). /// Validates that the cell is empty and the game is still playing. /// [MessagePackObject] public struct PlaceMarkCommand : ICommand { [Key(0)] public int Row; [Key(1)] public int Col; public void Execute(World world) { // Read-only check before iteration — never auto-marks. if (world.ReadSingleton().Status != GameStatus.Playing) return; // Find the cell entity at (Row, Col) that has no Mark. var query = new Query().Without(); Entity? target = null; // Fetch singleton ref inside iteration so mutations are auto-marked. using var iter = world.Select(query); ref var state = ref world.GetSingleton(); while (iter.MoveNext()) { if (iter.Item1.Row == Row && iter.Item1.Col == Col) { target = iter.Entity; break; } } if (target == null) return; // Cell already occupied or invalid position. // Place the mark. world.AddComponent(target.Value, new Mark { Player = state.CurrentPlayer }); // Advance turn. Mutations auto-marked via EndIteration — no MarkModified needed. state.MoveCount++; state.CurrentPlayer = state.CurrentPlayer == Player.X ? Player.O : Player.X; } }