Files
4Gewinnt/API/Services/GameManager/GameManager.cs
T
Jonas 0e4fb5d828 Add bot play, replay creation, and move phrases
Create replay games when a match ends (win or draw) and include ReplayGameCode in the GameEnded payload; adjust deletion timing to keep the replay longer and clean up original games sooner. Update game deletion logic to only destroy non-running games and schedule immediate deletion for quick timeouts. Add optional local bot support (botPlayer2 flag) with automated random-delayed moves and a UI switch in the game creation menu; expose botPlayer2 in GameSettings and LocalMode defaults. Introduce RandomPlaceMessage utility to generate varied turn descriptions and integrate it into LocalGame and OnlineGame. Also include a minor whitespace fix in Game.cs.
2026-03-12 23:20:05 +01:00

195 lines
5.8 KiB
C#

using API.Controllers;
using API.Models.DataClasses;
using API.Models.Game;
using API.Repository.GameRepo;
using Microsoft.AspNetCore.SignalR;
using System.Text.RegularExpressions;
namespace API.Services.GameManager;
public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSocket> hubContext) : IGameManager
{
public (string, int) CreateGame(Coordinates gFs, Player player)
{
var game = gameRepository.Create(gFs);
game.AddPlayer(player);
return (game.Id, game.GameCode);
}
public async Task<string?> JoinGame(Player player, int gameCode)
{
var game = gameRepository.GetOne(new SixDigitInt(gameCode));
var success = game != null && game.State == GameState.Lobby && game.AddPlayer(player);
if (!success)
{
await hubContext.Clients.Client(player.ConnectionId).SendAsync("Error", "Spiel Existiert nicht!");
return null;
}
await hubContext.Groups.AddToGroupAsync(player.ConnectionId, game.Id);
if (game.Players.Count == 2)
{
game.StartGame();
var gameInfoDto = new GameInformationDto
{
Players = game?.Players,
CurrentField = ConvertField(game?.Field.CurrentField),
State = game?.State,
CurrentTurn = game?.CurrentTurn ?? 0
};
await hubContext.Clients.Group(game.Id).SendAsync("GameStarted", gameInfoDto);
}
return game.Id;
}
public async Task RequestGameInformation(string gameId, string playerConnectionId)
{
var game = gameRepository.GetOne(gameId);
if (game == null)
return;
var gameInfoDto = new GameInformationDto
{
Players = game?.Players,
CurrentField = ConvertField(game?.Field.CurrentField),
State = game?.State,
CurrentTurn = game?.CurrentTurn ?? 0
};
await hubContext.Clients.Client(playerConnectionId).SendAsync("GameInformation", gameInfoDto);
}
public async Task Drop(string gameCode, int column, string playerConnectionId)
{
var game = gameRepository.GetOne(gameCode);
var player = game?.GetPlayerByConnectionId(playerConnectionId);
if (player == null)
return;
if(game.CurrentTurn != player.PlayerTag || game.State != GameState.Running)
return;
if(game.State != GameState.Running) return;
var result = game.Field.Drop(column, player.PlayerTag);
if (result != DropResult.Placed)
{
await hubContext.Clients.Client(playerConnectionId).SendAsync("Error", "Ungültiger Zug.");
return;
}
game.CurrentTurn = game.CurrentTurn == 1 ? 2 : 1;
var gameInfoDto = new GameInformationDto
{
Players = game?.Players,
CurrentField = ConvertField(game?.Field.CurrentField),
State = game?.State,
CurrentTurn = game?.CurrentTurn ?? 0
};
await hubContext.Clients.Group(game.Id).SendAsync("FieldUpdated", gameInfoDto);
var winResult = game.Field.CheckForWin();
if (winResult != 0)
{
var newGame = gameRepository.Create(game.Field.GFs);
var winPlayer = game.GetPlayerByTag(winResult);
game.EndGame();
await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "Win",
Player = winPlayer,
ReplayGameCode = newGame.GameCode
});
ScheduleGameDeletion(newGame.Id, TimeSpan.FromSeconds(20));
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(10));
}
if (game.Field.IsFull())
{
var newGame = gameRepository.Create(game.Field.GFs);
game.EndGame();
await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "Draw",
ReplayGameCode = newGame.GameCode
});
ScheduleGameDeletion(newGame.Id, TimeSpan.FromSeconds(15));
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(10));
}
}
public Task DisconnectedPlayer(string playerConnectionId)
{
var game = gameRepository.GetOneByConnectionId(playerConnectionId);
if (game == null || game.State == GameState.Ended)
return Task.CompletedTask;
var player = game.GetPlayerByConnectionId(playerConnectionId);
if (player == null)
return Task.CompletedTask;
game.RemovePlayer(playerConnectionId);
game.EndGame();
hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
{
Method = "PlayerDisconnected",
Player = player
});
ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(0));
return Task.CompletedTask;
}
private static int[][] ConvertField(int[,]? field)
{
if (field == null)
return Array.Empty<int[]>();
var rows = field.GetLength(0);
var cols = field.GetLength(1);
var convertedField = new int[rows][];
for (var y = 0; y < rows; y++)
{
convertedField[y] = new int[cols];
for (var x = 0; x < cols; x++)
{
convertedField[y][x] = field[y, x];
}
}
return convertedField;
}
private void ScheduleGameDeletion(string gameId, TimeSpan delay)
{
_ = Task.Run(async () =>
{
await Task.Delay(delay);
var g = gameRepository.GetOne(gameId);
if (g != null && g.State != GameState.Running)
{
gameRepository.Destroy(gameId);
await hubContext.Clients.Group(gameId).SendAsync("GameDestroyed");
}
});
}
}