using OECS;
namespace Game.TicTacToe;
///
/// After each move, checks whether the game has been won or drawn.
/// Updates the GameState singleton accordingly.
///
public class WinCheckSystem : ISystem
{
public void Run(World world)
{
// Check game status before doing any work. Use ReadSingleton
// to avoid auto-marking GameState as modified when we bail early.
if (world.ReadSingleton().Status != GameStatus.Playing)
return;
// Get singleton ref inside iteration so all mutations are auto-marked
// by EndIteration — no MarkModified calls needed.
using var iter = world.Select| ();
ref var state = ref world.GetSingleton();
// Build a 3×3 grid of marks.
var grid = new Player[3, 3];
while (iter.MoveNext())
{
grid[iter.Item1.Row, iter.Item1.Col] = iter.Item2.Player;
}
// Check rows.
for (int r = 0; r < 3; r++)
{
if (TryGetWinner(grid[r, 0], grid[r, 1], grid[r, 2], out var winner))
{
SetWinner(ref state, winner);
return;
}
}
// Check columns.
for (int c = 0; c < 3; c++)
{
if (TryGetWinner(grid[0, c], grid[1, c], grid[2, c], out var winner))
{
SetWinner(ref state, winner);
return;
}
}
// Check diagonals.
if (TryGetWinner(grid[0, 0], grid[1, 1], grid[2, 2], out var diag1))
{
SetWinner(ref state, diag1);
return;
}
if (TryGetWinner(grid[0, 2], grid[1, 1], grid[2, 0], out var diag2))
{
SetWinner(ref state, diag2);
return;
}
// Check draw.
if (state.MoveCount >= 9)
{
state.Status = GameStatus.Draw;
}
}
private static bool TryGetWinner(Player a, Player b, Player c, out Player winner)
{
if (a != Player.None && a == b && b == c)
{
winner = a;
return true;
}
winner = Player.None;
return false;
}
private static void SetWinner(ref GameState state, Player winner)
{
state.Status = winner == Player.X ? GameStatus.XWon : GameStatus.OWon;
}
} |