Files
4Gewinnt/API/Models/Game/GameField.cs
T
Jonas 0eed8020b8 Convert field to jagged arrays; add local SignalR UI
Change game field representation to jagged arrays (int[][]) for JSON/SignalR compatibility and add conversion helpers in GameManager. Make Coordinates JSON-serializable (constructor and JsonPropertyName attributes). Update GameHubSocket: validate field size, fix JoinGame parameter order/Group join, rename Place->Drop and send proper game id. Add client-side local play support: GameConnection SignalR client, LocalGame orchestration for two local players, and UI components (GameCreationMenu, Slider) plus GameSettings interface. Update LocalMode route to use the new creation UI and start local games. These changes enable reliable serialization over SignalR and a local two-player flow with a creation UI.
2026-03-12 23:20:05 +01:00

162 lines
3.9 KiB
C#

using System.Text.Json.Serialization;
namespace API.Models.Game;
public readonly struct Coordinates
{
[JsonConstructor]
public Coordinates(int x, int y)
{
X = x;
Y = y;
}
[JsonPropertyName("x")]
public int X { get; init; }
[JsonPropertyName("y")]
public int Y { get; init; }
}
public enum DropResult
{
OutOfGameField,
NotAllowedPlayer,
ColumnFull,
InvalidFieldValue,
Placed
}
public enum FieldState
{
OutOfGameField,
Empty,
OccupiedRed,
OccupiedYellow,
InvalidFieldValue
}
public class GameField(Coordinates gFs)
{
public Coordinates GFs = gFs;
public int[,] CurrentField { get; } = new int[gFs.Y, gFs.X];
public int[,] BackupField { get; } = new int[gFs.Y, gFs.X];
public DropResult Drop(int column, int player)
{
if (column < 0 || column >= CurrentField.GetLength(1))
return DropResult.OutOfGameField;
if (player != 1 && player != 2)
return DropResult.NotAllowedPlayer;
int rows = CurrentField.GetLength(0);
for (int y = rows - 1; y >= 0; y--)
{
int currentValue = CurrentField[y, column];
if (currentValue == 0)
{
CreateSave();
CurrentField[y, column] = player;
return DropResult.Placed;
}
if (currentValue != 1 && currentValue != 2)
return DropResult.InvalidFieldValue;
}
return DropResult.ColumnFull;
}
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
};
}
public bool IsFull()
{
int rows = CurrentField.GetLength(0);
int cols = CurrentField.GetLength(1);
for (int y = 0; y < rows; y++)
{
for (int x = 0; x < cols; x++)
{
if (CurrentField[y, x] == 0)
return false;
}
}
return true;
}
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);
}
}