955d1e18c6
Additions and refactors across controllers, models, repo and services to support game lifecycle and win detection. Key changes: - Models: Player now has PlayerTag; Game assigns tags, starts when 2 players join, exposes GetPlayerByConnectionId, StartGame and EndGame helpers. - GameField: Implemented win-detection (CountInDirection, CheckLine, CheckForWin) and minor backing-field init. - Repository/API: GameRepository.Create no longer accepts a Player (repo only creates games); minor variable cleanups; IGameRepository signature updated accordingly. - Services: GameManager now injects IHubContext<GameHubSocket>, moves player-adding into manager (CreateGame), makes JoinGame async and notifies the SignalR group when a game starts, and adds a placeholder async Place method. Controller (GameHubSocket) also exposes a Place method stub to match the service API. Motivation: separate responsibilities so repository only creates games, manager handles player joins and notifications, and add core game logic (player tagging and win checks) needed for gameplay. Place methods are added as placeholders for future move handling.
132 lines
3.4 KiB
C#
132 lines
3.4 KiB
C#
namespace API.Models.Game;
|
|
|
|
public readonly struct Coordinates(int x, int y)
|
|
{
|
|
public readonly int X = x;
|
|
public readonly int Y = y;
|
|
}
|
|
|
|
public enum PlaceResult
|
|
{
|
|
OutOfGameField,
|
|
NotAllowedPlayer,
|
|
OccupiedRed,
|
|
OccupiedYellow,
|
|
InvalidFieldValue,
|
|
Placed
|
|
}
|
|
|
|
public enum FieldState
|
|
{
|
|
OutOfGameField,
|
|
Empty,
|
|
OccupiedRed,
|
|
OccupiedYellow,
|
|
InvalidFieldValue
|
|
}
|
|
|
|
public class GameField(Coordinates gFs)
|
|
{
|
|
private Coordinates _gFs = gFs;
|
|
public int[,] CurrentField { get; } = new int[gFs.Y, gFs.X];
|
|
public int[,] BackupField { get; } = new int[gFs.Y, gFs.X];
|
|
|
|
public PlaceResult Place(Coordinates coordinates, int player)
|
|
{
|
|
if (coordinates.X < 0 || coordinates.X >= CurrentField.GetLength(1) ||
|
|
coordinates.Y < 0 || coordinates.Y >= CurrentField.GetLength(0))
|
|
return PlaceResult.OutOfGameField;
|
|
|
|
if (player != 1 && player != 2)
|
|
return PlaceResult.NotAllowedPlayer;
|
|
|
|
var currentValue = CurrentField[coordinates.Y, coordinates.X];
|
|
|
|
if (currentValue != 0)
|
|
return currentValue switch
|
|
{
|
|
1 => PlaceResult.OccupiedRed,
|
|
2 => PlaceResult.OccupiedYellow,
|
|
_ => PlaceResult.InvalidFieldValue
|
|
};
|
|
|
|
CreateSave();
|
|
CurrentField[coordinates.Y, coordinates.X] = player;
|
|
|
|
return PlaceResult.Placed;
|
|
}
|
|
|
|
public FieldState CheckField(Coordinates coordinates)
|
|
{
|
|
if (coordinates.X < 0 || coordinates.X >= CurrentField.GetLength(1) ||
|
|
coordinates.Y < 0 || coordinates.Y >= CurrentField.GetLength(0))
|
|
return FieldState.OutOfGameField;
|
|
|
|
var currentValue = CurrentField[coordinates.Y, coordinates.X];
|
|
|
|
return currentValue switch
|
|
{
|
|
0 => FieldState.Empty,
|
|
1 => FieldState.OccupiedRed,
|
|
2 => FieldState.OccupiedYellow,
|
|
_ => FieldState.InvalidFieldValue
|
|
};
|
|
}
|
|
|
|
private int CountInDirection(int startX, int startY, int dx, int dy, int player)
|
|
{
|
|
var count = 0;
|
|
var x = startX;
|
|
var y = startY;
|
|
|
|
while (
|
|
x >= 0 && x < CurrentField.GetLength(1) &&
|
|
y >= 0 && y < CurrentField.GetLength(0) &&
|
|
CurrentField[y, x] == player)
|
|
{
|
|
count++;
|
|
x += dx;
|
|
y += dy;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
private bool CheckLine(int startX, int startY, int dx, int dy, int player)
|
|
{
|
|
int count = CountInDirection(startX + dx, startY + dy, dx, dy, player);
|
|
count += CountInDirection(startX - dx, startY - dy, -dx, -dy, player);
|
|
return count + 1 >= 4;
|
|
}
|
|
|
|
public int CheckForWin()
|
|
{
|
|
int rows = CurrentField.GetLength(0);
|
|
int cols = CurrentField.GetLength(1);
|
|
|
|
(int dx, int dy)[] directions = [(1, 0), (0, 1), (1, 1), (-1, 1)];
|
|
|
|
for (int y = 0; y < rows; y++)
|
|
{
|
|
for (int x = 0; x < cols; x++)
|
|
{
|
|
int player = CurrentField[y, x];
|
|
|
|
if (player != 1 && player != 2) continue;
|
|
|
|
foreach (var (dx, dy) in directions)
|
|
{
|
|
if (CheckLine(x, y, dx, dy, player))
|
|
return player;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private void CreateSave()
|
|
{
|
|
Array.Copy(CurrentField, BackupField, CurrentField.Length);
|
|
}
|
|
} |