diff --git a/apps-rps/rps-game-agent/api_client.py b/apps-rps/rps-game-agent/api_client.py index 384813a..6690221 100644 --- a/apps-rps/rps-game-agent/api_client.py +++ b/apps-rps/rps-game-agent/api_client.py @@ -11,16 +11,17 @@ class RPSGameClient: """Client for communicating with the RPS Game Server API""" - def __init__(self, base_url: str = None): + def __init__(self, base_url: str = None, room_id: int = 1): if base_url is None: base_url = os.getenv("RPS_SERVER_URL", "http://localhost:5289") self.base_url = base_url.rstrip('/') + self.room_id = room_id self.session = requests.Session() def register_player(self, name: str) -> Dict[str, Any]: """Register a new player with the server""" url = f"{self.base_url}/api/player/register" - data = {"Name": name} + data = {"Name": name, "RoomId": self.room_id} try: response = self.session.post(url, json=data) @@ -31,7 +32,7 @@ def register_player(self, name: str) -> Dict[str, Any]: def get_player_status(self, player_id: int) -> Dict[str, Any]: """Get current tournament status for a player""" - url = f"{self.base_url}/api/player/{player_id}/status" + url = f"{self.base_url}/api/player/{player_id}/status?roomId={self.room_id}" logger.info(f"GET {url}") try: @@ -43,7 +44,7 @@ def get_player_status(self, player_id: int) -> Dict[str, Any]: def submit_answer(self, player_id: int, round_number: int, answer: str, move: int) -> Dict[str, Any]: """Submit answer and RPS move for current round""" - url = f"{self.base_url}/api/player/submit-answer" + url = f"{self.base_url}/api/player/submit-answer?roomId={self.room_id}" data = { "PlayerId": player_id, "RoundNumber": round_number, @@ -61,7 +62,7 @@ def submit_answer(self, player_id: int, round_number: int, answer: str, move: in def get_player_results(self, player_id: int) -> Dict[str, Any]: """Get all results for a specific player""" - url = f"{self.base_url}/api/player/{player_id}/results" + url = f"{self.base_url}/api/player/{player_id}/results?roomId={self.room_id}" try: response = self.session.get(url) diff --git a/apps-rps/rps-game-agent/app.py b/apps-rps/rps-game-agent/app.py index a569e59..50bcd68 100644 --- a/apps-rps/rps-game-agent/app.py +++ b/apps-rps/rps-game-agent/app.py @@ -33,11 +33,12 @@ def index(): show_form=False, player_name=game_agent.player_name, player_id=game_agent.player_id, + room_id=game_agent.room_id, tournament_status=game_agent.tournament_status, round_status=game_agent.round_status, current_round=game_agent.current_round, is_running=game_agent.is_running, - status_log=filtered_log[::-1], # Show filtered messages, latest first + status_log=filtered_log[::-1], results=game_agent.results) @app.route('/start', methods=['POST']) @@ -46,20 +47,17 @@ def start_game(): global game_agent player_name = request.form.get('player_name', '').strip() + ' *' + room_id = int(request.form.get('room_id', '1')) if not player_name: return redirect(url_for('index')) - # Create new game agent - game_agent = GameProcessor(player_name) + game_agent = GameProcessor(player_name, room_id) - # Register player if game_agent.register_player(): - # Start autonomous play game_agent.start_autonomous_play() return redirect(url_for('index')) else: - # Registration failed, reset game_agent = None return redirect(url_for('index')) @@ -69,6 +67,7 @@ def reconnect_game(): global game_agent player_id = request.form.get('player_id', '').strip() + room_id = int(request.form.get('room_id', '1')) if not player_id: return redirect(url_for('index')) @@ -78,13 +77,11 @@ def reconnect_game(): except ValueError: return redirect(url_for('index')) - # Create game agent with existing player ID - game_agent = GameProcessor(f"Player {player_id}") + game_agent = GameProcessor(f"Player {player_id}", room_id) game_agent.player_id = player_id - # Verify the player exists by getting status from api_client import RPSGameClient - client = RPSGameClient() + client = RPSGameClient(room_id=room_id) status_response = client.get_player_status(player_id) if "error" in status_response: @@ -94,10 +91,8 @@ def reconnect_game(): game_agent.log_status(f"Successfully reconnected as Player ID: {player_id}") - # Get current results game_agent.get_current_results() - # Start autonomous play game_agent.start_autonomous_play() return redirect(url_for('index')) diff --git a/apps-rps/rps-game-agent/game_processor.py b/apps-rps/rps-game-agent/game_processor.py index bdef475..6509b2c 100644 --- a/apps-rps/rps-game-agent/game_processor.py +++ b/apps-rps/rps-game-agent/game_processor.py @@ -8,9 +8,10 @@ class GameProcessor: """Autonomous game agent for RPS Tournament""" - def __init__(self, player_name: str): + def __init__(self, player_name: str, room_id: int = 1): self.player_name = player_name - self.client = RPSGameClient() + self.room_id = room_id + self.client = RPSGameClient(room_id=room_id) self.agent = GameAgent() self.player_id: Optional[int] = None self.current_round = 1 diff --git a/apps-rps/rps-game-agent/templates/index.html b/apps-rps/rps-game-agent/templates/index.html index 74bc973..9f2b925 100644 --- a/apps-rps/rps-game-agent/templates/index.html +++ b/apps-rps/rps-game-agent/templates/index.html @@ -762,6 +762,13 @@

✨ RPS Game Copilot - Your Smart Assistant

+
+ + +
@@ -772,6 +779,13 @@

✨ RPS Game Copilot - Your Smart Assistant

+
+ + +
@@ -810,7 +824,7 @@

Track Progress

-

Player: {{ player_name }} (ID: {{ player_id }})

+

Player: {{ player_name }} (ID: {{ player_id }}) - Room {{ room_id }}

Reset Game
diff --git a/apps-rps/rps-game-server/Controllers/AdminController.cs b/apps-rps/rps-game-server/Controllers/AdminController.cs index 47f0b93..f654811 100644 --- a/apps-rps/rps-game-server/Controllers/AdminController.cs +++ b/apps-rps/rps-game-server/Controllers/AdminController.cs @@ -43,26 +43,27 @@ public IActionResult Login(string passcode) return View(); } - public IActionResult Index() + public IActionResult Index(int roomId = 1) { if (!IsAuthenticated()) { return RedirectToAction("Login"); } - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); return View(tournament); } [HttpPost] - public IActionResult UnregisterPlayer(int playerId) + public IActionResult UnregisterPlayer(int playerId, int roomId = 1) { if (!IsAuthenticated()) { return RedirectToAction("Login"); } - var success = _tournamentService.UnregisterPlayer(playerId); + var success = _tournamentService.UnregisterPlayer(playerId, roomId); if (success) { TempData["Success"] = "Player unregistered successfully."; @@ -72,18 +73,18 @@ public IActionResult UnregisterPlayer(int playerId) TempData["Error"] = "Failed to unregister player."; } - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult UnregisterAllPlayers() + public IActionResult UnregisterAllPlayers(int roomId = 1) { if (!IsAuthenticated()) { return RedirectToAction("Login"); } - var success = _tournamentService.UnregisterAllPlayers(); + var success = _tournamentService.UnregisterAllPlayers(roomId); if (success) { TempData["Success"] = "All players unregistered successfully."; @@ -93,18 +94,18 @@ public IActionResult UnregisterAllPlayers() TempData["Error"] = "No players to unregister or operation failed."; } - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult ResetCurrentRound() + public IActionResult ResetCurrentRound(int roomId = 1) { if (!IsAuthenticated()) { return RedirectToAction("Login"); } - var success = _tournamentService.ResetCurrentRound(); + var success = _tournamentService.ResetCurrentRound(roomId); if (success) { TempData["Success"] = "Current round reset successfully."; @@ -114,18 +115,18 @@ public IActionResult ResetCurrentRound() TempData["Error"] = "Failed to reset current round."; } - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult ResetTournament() + public IActionResult ResetTournament(int roomId = 1) { if (!IsAuthenticated()) { return RedirectToAction("Login"); } - var success = _tournamentService.ResetTournament(); + var success = _tournamentService.ResetTournament(roomId); if (success) { TempData["Success"] = "Tournament reset successfully. All players remain registered."; @@ -135,7 +136,7 @@ public IActionResult ResetTournament() TempData["Error"] = "Failed to reset tournament."; } - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } [HttpPost] diff --git a/apps-rps/rps-game-server/Controllers/HomeController.cs b/apps-rps/rps-game-server/Controllers/HomeController.cs index 5614a74..9d09104 100644 --- a/apps-rps/rps-game-server/Controllers/HomeController.cs +++ b/apps-rps/rps-game-server/Controllers/HomeController.cs @@ -19,122 +19,128 @@ private bool IsAuthenticated() return HttpContext.Session.GetString(AdminSessionKey) == "true"; } - public IActionResult Index() + public IActionResult Index(int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); return View(tournament); } [HttpPost] - public IActionResult StartTournament() + public IActionResult StartTournament(int roomId = 1) { if (!IsAuthenticated()) { TempData["Error"] = "Admin access required to start tournament"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - _tournamentService.StartTournament(); - return RedirectToAction("Index"); + _tournamentService.StartTournament(roomId); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult EndTournament() + public IActionResult EndTournament(int roomId = 1) { if (!IsAuthenticated()) { TempData["Error"] = "Admin access required to end tournament"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - _tournamentService.EndTournament(); - return RedirectToAction("GrandFinish"); + _tournamentService.EndTournament(roomId); + return RedirectToAction("GrandFinish", new { roomId }); } [HttpPost] - public IActionResult ResetTournament() + public IActionResult ResetTournament(int roomId = 1) { if (!IsAuthenticated()) { TempData["Error"] = "Admin access required to reset tournament"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - _tournamentService.ResetTournament(); - return RedirectToAction("Index"); + _tournamentService.ResetTournament(roomId); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult StartRound(int roundNumber) + public IActionResult StartRound(int roundNumber, int roomId = 1) { if (!IsAuthenticated()) { TempData["Error"] = "Admin access required to start round"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - var success = _tournamentService.StartRound(roundNumber); + var success = _tournamentService.StartRound(roomId, roundNumber); if (!success) { TempData["Error"] = "Failed to start round"; } - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } [HttpPost] - public IActionResult EndRound(int roundNumber) + public IActionResult EndRound(int roundNumber, int roomId = 1) { if (!IsAuthenticated()) { TempData["Error"] = "Admin access required to end round"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - var success = _tournamentService.EndRound(roundNumber); + var success = _tournamentService.EndRound(roomId, roundNumber); if (!success) { TempData["Error"] = "Failed to end round"; - return RedirectToAction("Index"); + return RedirectToAction("Index", new { roomId }); } - return RedirectToAction("RoundComplete", new { roundNumber }); + return RedirectToAction("RoundComplete", new { roundNumber, roomId }); } - public IActionResult RoundComplete(int roundNumber) + public IActionResult RoundComplete(int roundNumber, int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); var viewModel = new RoundCompleteViewModel { Tournament = tournament, CompletedRoundNumber = roundNumber, - RoundResults = _tournamentService.GetRoundResults(roundNumber), + RoundResults = _tournamentService.GetRoundResults(roomId, roundNumber), IsLastRound = roundNumber >= Tournament.MaxRounds }; return View(viewModel); } - public IActionResult TournamentWaiting() + public IActionResult TournamentWaiting(int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); return View(tournament); } - public IActionResult GrandFinish() + public IActionResult GrandFinish(int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); return View(tournament); } - public IActionResult Results() + public IActionResult Results(int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); return View(tournament); } - public IActionResult Grid() + public IActionResult Grid(int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + ViewBag.RoomId = roomId; + var tournament = _tournamentService.GetTournament(roomId); var viewModel = new GridViewModel { Players = tournament.Players, @@ -149,7 +155,7 @@ public IActionResult Register() } [HttpPost] - public IActionResult Register(string playerName) + public IActionResult Register(string playerName, int roomId = 1) { if (string.IsNullOrWhiteSpace(playerName)) { @@ -157,12 +163,13 @@ public IActionResult Register(string playerName) return View(); } - var response = _tournamentService.RegisterPlayer(playerName); + var response = _tournamentService.RegisterPlayer(playerName, roomId); if (response.PlayerId > 0) { ViewBag.Success = true; ViewBag.PlayerId = response.PlayerId; ViewBag.PlayerName = playerName; + ViewBag.RoomId = roomId; ViewBag.Message = response.Message; } else @@ -173,9 +180,10 @@ public IActionResult Register(string playerName) return View(); } - public IActionResult Play() + public IActionResult Play(int roomId = 1) { - var currentRound = _tournamentService.GetCurrentRound(); + ViewBag.RoomId = roomId; + var currentRound = _tournamentService.GetCurrentRound(roomId); if (currentRound != null && currentRound.Status == RoundStatus.InProgress) { ViewBag.CurrentQuestion = currentRound.Question; @@ -185,8 +193,10 @@ public IActionResult Play() } [HttpPost] - public IActionResult Play(int playerId, string answer, string move) + public IActionResult Play(int playerId, string answer, string move, int roomId = 1) { + ViewBag.RoomId = roomId; + if (playerId <= 0) { TempData["Error"] = "Invalid player ID"; @@ -199,8 +209,8 @@ public IActionResult Play(int playerId, string answer, string move) return View(); } - var tournament = _tournamentService.GetTournament(); - var currentRound = _tournamentService.GetCurrentRound(); + var tournament = _tournamentService.GetTournament(roomId); + var currentRound = _tournamentService.GetCurrentRound(roomId); if (currentRound == null || currentRound.Status != RoundStatus.InProgress) { @@ -216,7 +226,7 @@ public IActionResult Play(int playerId, string answer, string move) Move = parsedMove }; - var response = _tournamentService.SubmitAnswer(request); + var response = _tournamentService.SubmitAnswer(request, roomId); if (response.Success) { ViewBag.Success = true; diff --git a/apps-rps/rps-game-server/Controllers/PlayerController.cs b/apps-rps/rps-game-server/Controllers/PlayerController.cs index 8f3a771..23c4ce3 100644 --- a/apps-rps/rps-game-server/Controllers/PlayerController.cs +++ b/apps-rps/rps-game-server/Controllers/PlayerController.cs @@ -27,7 +27,16 @@ public ActionResult RegisterPlayer([FromBody] RegisterPl }); } - var response = _tournamentService.RegisterPlayer(request.Name); + if (request.RoomId != 1 && request.RoomId != 2) + { + return BadRequest(new RegisterPlayerResponse + { + PlayerId = 0, + Message = "Room ID must be 1 or 2" + }); + } + + var response = _tournamentService.RegisterPlayer(request.Name, request.RoomId); if (response.PlayerId == 0) { @@ -38,14 +47,14 @@ public ActionResult RegisterPlayer([FromBody] RegisterPl } [HttpGet("{playerId}/status")] - public ActionResult GetStatus(int playerId) + public ActionResult GetStatus(int playerId, [FromQuery] int roomId = 1) { - var status = _tournamentService.GetTournamentStatus(playerId); + var status = _tournamentService.GetTournamentStatus(playerId, roomId); return Ok(status); } [HttpPost("submit-answer")] - public ActionResult SubmitAnswer([FromBody] SubmitAnswerRequest request) + public ActionResult SubmitAnswer([FromBody] SubmitAnswerRequest request, [FromQuery] int roomId = 1) { if (request.PlayerId <= 0) { @@ -65,7 +74,7 @@ public ActionResult SubmitAnswer([FromBody] SubmitAnswerRe }); } - var response = _tournamentService.SubmitAnswer(request); + var response = _tournamentService.SubmitAnswer(request, roomId); if (!response.Success) { @@ -76,9 +85,9 @@ public ActionResult SubmitAnswer([FromBody] SubmitAnswerRe } [HttpGet("{playerId}/results")] - public ActionResult> GetPlayerResults(int playerId) + public ActionResult> GetPlayerResults(int playerId, [FromQuery] int roomId = 1) { - var results = _tournamentService.GetPlayerResults(playerId); + var results = _tournamentService.GetPlayerResults(playerId, roomId); return Ok(results); } } \ No newline at end of file diff --git a/apps-rps/rps-game-server/Controllers/TournamentController.cs b/apps-rps/rps-game-server/Controllers/TournamentController.cs index fba101b..1171de2 100644 --- a/apps-rps/rps-game-server/Controllers/TournamentController.cs +++ b/apps-rps/rps-game-server/Controllers/TournamentController.cs @@ -16,23 +16,23 @@ public TournamentController(ITournamentService tournamentService) } [HttpGet("status")] - public ActionResult GetTournamentStatus() + public ActionResult GetTournamentStatus([FromQuery] int roomId = 1) { - var tournament = _tournamentService.GetTournament(); + var tournament = _tournamentService.GetTournament(roomId); return Ok(tournament); } [HttpGet("leaderboard")] - public ActionResult> GetLeaderboard() + public ActionResult> GetLeaderboard([FromQuery] int roomId = 1) { - var leaderboard = _tournamentService.GetLeaderboard(hideForLastTwoRounds: true); + var leaderboard = _tournamentService.GetLeaderboard(roomId, hideForLastTwoRounds: true); return Ok(leaderboard); } [HttpGet("round/{roundNumber}/results")] - public ActionResult> GetRoundResults(int roundNumber) + public ActionResult> GetRoundResults(int roundNumber, [FromQuery] int roomId = 1) { - var results = _tournamentService.GetRoundResults(roundNumber); + var results = _tournamentService.GetRoundResults(roomId, roundNumber); return Ok(results); } } \ No newline at end of file diff --git a/apps-rps/rps-game-server/Models/GameModels.cs b/apps-rps/rps-game-server/Models/GameModels.cs index b7ce2da..01b0b8a 100644 --- a/apps-rps/rps-game-server/Models/GameModels.cs +++ b/apps-rps/rps-game-server/Models/GameModels.cs @@ -59,6 +59,7 @@ public class Player public string Name { get; set; } = string.Empty; public int TotalScore { get; set; } public DateTime RegisteredAt { get; set; } + public int RoomId { get; set; } = 1; } public class PlayerRoundResult @@ -96,6 +97,7 @@ public class Tournament public int CurrentRound { get; set; } = 1; public DateTime? StartedAt { get; set; } public DateTime? EndedAt { get; set; } + public int RoomId { get; set; } = 1; public const int MaxRounds = 5; } @@ -103,6 +105,7 @@ public class Tournament public class RegisterPlayerRequest { public string Name { get; set; } = string.Empty; + public int RoomId { get; set; } = 1; } public class RegisterPlayerResponse @@ -132,6 +135,7 @@ public class TournamentStatusResponse public RoundStatus? CurrentRoundStatus { get; set; } public string? CurrentQuestion { get; set; } public bool CanSubmit { get; set; } + public int RoomId { get; set; } } public class LeaderboardEntry @@ -165,6 +169,7 @@ public class TournamentHistory public int TotalRounds { get; set; } public string? WinnerName { get; set; } public int? WinnerScore { get; set; } + public int RoomId { get; set; } = 1; public List Players { get; set; } = new(); public List Rounds { get; set; } = new(); } diff --git a/apps-rps/rps-game-server/Services/ITournamentService.cs b/apps-rps/rps-game-server/Services/ITournamentService.cs index 651ccf9..5e439a2 100644 --- a/apps-rps/rps-game-server/Services/ITournamentService.cs +++ b/apps-rps/rps-game-server/Services/ITournamentService.cs @@ -5,28 +5,28 @@ namespace RpsGameServer.Services; public interface ITournamentService { // Player registration - RegisterPlayerResponse RegisterPlayer(string playerName); - bool UnregisterPlayer(int playerId); - bool UnregisterAllPlayers(); + RegisterPlayerResponse RegisterPlayer(string playerName, int roomId = 1); + bool UnregisterPlayer(int playerId, int roomId = 1); + bool UnregisterAllPlayers(int roomId = 1); // Tournament management - Tournament GetTournament(); - bool StartTournament(); - bool EndTournament(); - bool ResetTournament(); + Tournament GetTournament(int roomId = 1); + bool StartTournament(int roomId = 1); + bool EndTournament(int roomId = 1); + bool ResetTournament(int roomId = 1); // Round management - bool StartRound(int roundNumber, string? question = null, string? correctAnswer = null); - bool EndRound(int roundNumber); - bool ResetCurrentRound(); - Round? GetCurrentRound(); + bool StartRound(int roomId, int roundNumber, string? question = null, string? correctAnswer = null); + bool EndRound(int roomId, int roundNumber); + bool ResetCurrentRound(int roomId = 1); + Round? GetCurrentRound(int roomId = 1); // Player actions - TournamentStatusResponse GetTournamentStatus(int playerId); - SubmitAnswerResponse SubmitAnswer(SubmitAnswerRequest request); + TournamentStatusResponse GetTournamentStatus(int playerId, int roomId = 1); + SubmitAnswerResponse SubmitAnswer(SubmitAnswerRequest request, int roomId = 1); // Results and leaderboard - List GetLeaderboard(bool hideForLastTwoRounds = false); - List GetRoundResults(int roundNumber); - List GetPlayerResults(int playerId); + List GetLeaderboard(int roomId = 1, bool hideForLastTwoRounds = false); + List GetRoundResults(int roomId, int roundNumber); + List GetPlayerResults(int playerId, int roomId = 1); } \ No newline at end of file diff --git a/apps-rps/rps-game-server/Services/TournamentHistoryService.cs b/apps-rps/rps-game-server/Services/TournamentHistoryService.cs index 179c255..cce6656 100644 --- a/apps-rps/rps-game-server/Services/TournamentHistoryService.cs +++ b/apps-rps/rps-game-server/Services/TournamentHistoryService.cs @@ -60,7 +60,8 @@ public async Task SaveTournamentAsync(Tournament tournament) EndedAt = tournament.EndedAt, Status = tournament.Status, TotalPlayers = tournament.Players.Count, - TotalRounds = tournament.Rounds.Count + TotalRounds = tournament.Rounds.Count, + RoomId = tournament.RoomId }; // Find winner diff --git a/apps-rps/rps-game-server/Services/TournamentService.cs b/apps-rps/rps-game-server/Services/TournamentService.cs index 18d4f39..2b111b9 100644 --- a/apps-rps/rps-game-server/Services/TournamentService.cs +++ b/apps-rps/rps-game-server/Services/TournamentService.cs @@ -5,9 +5,17 @@ namespace RpsGameServer.Services; public class TournamentService : ITournamentService { - private readonly Tournament _tournament = new(); + private readonly Dictionary _tournaments = new() + { + { 1, new Tournament { RoomId = 1 } }, + { 2, new Tournament { RoomId = 2 } } + }; + private readonly Dictionary _nextPlayerIds = new() + { + { 1, 1 }, + { 2, 1 } + }; private readonly object _lock = new(); - private int _nextPlayerId = 1; private readonly Random _random = new(); private readonly IQuestionService _questionService; private readonly IServiceProvider _serviceProvider; @@ -20,19 +28,26 @@ public TournamentService(IQuestionService questionService, IServiceProvider serv _logger = logger; } - public Tournament GetTournament() + public Tournament GetTournament(int roomId = 1) { lock (_lock) { - return _tournament; + if (!_tournaments.ContainsKey(roomId)) + { + _tournaments[roomId] = new Tournament { RoomId = roomId }; + _nextPlayerIds[roomId] = 1; + } + return _tournaments[roomId]; } } - public RegisterPlayerResponse RegisterPlayer(string playerName) + public RegisterPlayerResponse RegisterPlayer(string playerName, int roomId = 1) { lock (_lock) { - if (_tournament.Status != TournamentStatus.Pending) + var tournament = GetTournament(roomId); + + if (tournament.Status != TournamentStatus.Pending) { return new RegisterPlayerResponse { @@ -43,34 +58,34 @@ public RegisterPlayerResponse RegisterPlayer(string playerName) var player = new Player { - Id = _nextPlayerId++, + Id = _nextPlayerIds[roomId]++, Name = playerName, - RegisteredAt = DateTime.UtcNow + RegisteredAt = DateTime.UtcNow, + RoomId = roomId }; - _tournament.Players.Add(player); + tournament.Players.Add(player); return new RegisterPlayerResponse { PlayerId = player.Id, - Message = $"Player {playerName} registered successfully with ID {player.Id}" + Message = $"Player {playerName} registered successfully with ID {player.Id} in Room {roomId}" }; } } - public bool UnregisterPlayer(int playerId) + public bool UnregisterPlayer(int playerId, int roomId = 1) { lock (_lock) { - var player = _tournament.Players.FirstOrDefault(p => p.Id == playerId); + var tournament = GetTournament(roomId); + var player = tournament.Players.FirstOrDefault(p => p.Id == playerId); if (player == null) return false; - // Remove player from tournament - _tournament.Players.Remove(player); + tournament.Players.Remove(player); - // Remove player from all round results - foreach (var round in _tournament.Rounds) + foreach (var round in tournament.Rounds) { var playerResult = round.PlayerResults.FirstOrDefault(pr => pr.PlayerId == playerId); if (playerResult != null) @@ -83,68 +98,69 @@ public bool UnregisterPlayer(int playerId) } } - public bool UnregisterAllPlayers() + public bool UnregisterAllPlayers(int roomId = 1) { lock (_lock) { - if (!_tournament.Players.Any()) + var tournament = GetTournament(roomId); + + if (!tournament.Players.Any()) return false; - // Clear all players from tournament - _tournament.Players.Clear(); + tournament.Players.Clear(); - // Clear all player results from all rounds - foreach (var round in _tournament.Rounds) + foreach (var round in tournament.Rounds) { round.PlayerResults.Clear(); } - // Reset player ID counter - _nextPlayerId = 1; + _nextPlayerIds[roomId] = 1; return true; } } - public bool StartTournament() + public bool StartTournament(int roomId = 1) { lock (_lock) { - if (_tournament.Status != TournamentStatus.Pending) + var tournament = GetTournament(roomId); + + if (tournament.Status != TournamentStatus.Pending) return false; - _tournament.Status = TournamentStatus.InProgress; - _tournament.StartedAt = DateTime.UtcNow; - _tournament.CurrentRound = 1; + tournament.Status = TournamentStatus.InProgress; + tournament.StartedAt = DateTime.UtcNow; + tournament.CurrentRound = 1; - // Initialize rounds for (int i = 1; i <= Tournament.MaxRounds; i++) { - _tournament.Rounds.Add(new Round { RoundNumber = i }); + tournament.Rounds.Add(new Round { RoundNumber = i }); } return true; } } - public bool EndTournament() + public bool EndTournament(int roomId = 1) { lock (_lock) { - if (_tournament.Status != TournamentStatus.InProgress) + var tournament = GetTournament(roomId); + + if (tournament.Status != TournamentStatus.InProgress) return false; - _tournament.Status = TournamentStatus.Completed; - _tournament.EndedAt = DateTime.UtcNow; + tournament.Status = TournamentStatus.Completed; + tournament.EndedAt = DateTime.UtcNow; - // Save tournament to history asynchronously _ = Task.Run(async () => { try { using var scope = _serviceProvider.CreateScope(); var historyService = scope.ServiceProvider.GetRequiredService(); - await historyService.SaveTournamentAsync(_tournament); + await historyService.SaveTournamentAsync(tournament); _logger.LogInformation("Tournament saved to history successfully"); } catch (Exception ex) @@ -157,45 +173,44 @@ public bool EndTournament() } } - public bool ResetTournament() + public bool ResetTournament(int roomId = 1) { lock (_lock) { - // Reset tournament state but keep players - _tournament.Status = TournamentStatus.Pending; - _tournament.CurrentRound = 1; - _tournament.StartedAt = null; - _tournament.EndedAt = null; + var tournament = GetTournament(roomId); + + tournament.Status = TournamentStatus.Pending; + tournament.CurrentRound = 1; + tournament.StartedAt = null; + tournament.EndedAt = null; - // Reset all player scores but keep the players registered - foreach (var player in _tournament.Players) + foreach (var player in tournament.Players) { player.TotalScore = 0; } - // Clear all rounds - _tournament.Rounds.Clear(); + tournament.Rounds.Clear(); return true; } } - public bool StartRound(int roundNumber, string? question = null, string? correctAnswer = null) + public bool StartRound(int roomId, int roundNumber, string? question = null, string? correctAnswer = null) { lock (_lock) { - if (_tournament.Status != TournamentStatus.InProgress) + var tournament = GetTournament(roomId); + + if (tournament.Status != TournamentStatus.InProgress) return false; - var round = _tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); + var round = tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); if (round == null || round.Status != RoundStatus.Pending) return false; - // Use sequential question based on round number if not provided QuestionAnswer qa; if (string.IsNullOrWhiteSpace(question) || string.IsNullOrWhiteSpace(correctAnswer)) { - // Get question by order (round number) to ensure exact sequence var sequentialQuestion = _questionService.GetQuestionByOrderAsync(roundNumber).GetAwaiter().GetResult(); if (sequentialQuestion != null) { @@ -203,7 +218,6 @@ public bool StartRound(int roundNumber, string? question = null, string? correct } else { - // Fallback to random if no question found for this order _logger.LogWarning($"No question found for round {roundNumber}, falling back to random question"); qa = _questionService.GetRandomQuestionAsync().GetAwaiter().GetResult(); } @@ -222,8 +236,7 @@ public bool StartRound(int roundNumber, string? question = null, string? correct round.ServerMove = GenerateRandomMove(); round.StartedAt = DateTime.UtcNow; - // Initialize player results for this round - foreach (var player in _tournament.Players) + foreach (var player in tournament.Players) { round.PlayerResults.Add(new PlayerRoundResult { @@ -232,52 +245,51 @@ public bool StartRound(int roundNumber, string? question = null, string? correct }); } - _tournament.CurrentRound = roundNumber; + tournament.CurrentRound = roundNumber; return true; } } - public bool EndRound(int roundNumber) + public bool EndRound(int roomId, int roundNumber) { lock (_lock) { - var round = _tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); + var tournament = GetTournament(roomId); + var round = tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); if (round == null || round.Status != RoundStatus.InProgress) return false; round.Status = RoundStatus.Completed; round.EndedAt = DateTime.UtcNow; - // Calculate scores for this round - CalculateRoundScores(round); + CalculateRoundScores(round, tournament); - // Automatically move to next round if not the last round if (roundNumber < Tournament.MaxRounds) { - _tournament.CurrentRound = roundNumber + 1; + tournament.CurrentRound = roundNumber + 1; } return true; } } - public bool ResetCurrentRound() + public bool ResetCurrentRound(int roomId = 1) { lock (_lock) { - var currentRound = GetCurrentRound(); + var currentRound = GetCurrentRound(roomId); if (currentRound == null) return false; - // Reset round status + var tournament = GetTournament(roomId); + currentRound.Status = RoundStatus.Pending; currentRound.StartedAt = null; currentRound.EndedAt = null; currentRound.Question = string.Empty; currentRound.CorrectAnswer = string.Empty; - currentRound.ServerMove = Move.Rock; // Default value + currentRound.ServerMove = Move.Rock; - // Reset all player results for this round foreach (var playerResult in currentRound.PlayerResults) { playerResult.Answer = null; @@ -287,13 +299,11 @@ public bool ResetCurrentRound() playerResult.SubmittedAt = null; } - // Reset player total scores only for this round's contribution - foreach (var player in _tournament.Players) + foreach (var player in tournament.Players) { var playerResult = currentRound.PlayerResults.FirstOrDefault(pr => pr.PlayerId == player.Id); if (playerResult != null) { - // Subtract this round's score from total (recalculated scores will be 0) player.TotalScore -= playerResult.Score; } } @@ -302,38 +312,42 @@ public bool ResetCurrentRound() } } - public Round? GetCurrentRound() + public Round? GetCurrentRound(int roomId = 1) { lock (_lock) { - return _tournament.Rounds.FirstOrDefault(r => r.RoundNumber == _tournament.CurrentRound); + var tournament = GetTournament(roomId); + return tournament.Rounds.FirstOrDefault(r => r.RoundNumber == tournament.CurrentRound); } } - public TournamentStatusResponse GetTournamentStatus(int playerId) + public TournamentStatusResponse GetTournamentStatus(int playerId, int roomId = 1) { lock (_lock) { - var currentRound = GetCurrentRound(); + var tournament = GetTournament(roomId); + var currentRound = GetCurrentRound(roomId); var playerResult = currentRound?.PlayerResults.FirstOrDefault(pr => pr.PlayerId == playerId); return new TournamentStatusResponse { - TournamentStatus = _tournament.Status, - CurrentRound = _tournament.CurrentRound, + TournamentStatus = tournament.Status, + CurrentRound = tournament.CurrentRound, CurrentRoundStatus = currentRound?.Status, CurrentQuestion = currentRound?.Status == RoundStatus.InProgress ? currentRound.Question : null, CanSubmit = currentRound?.Status == RoundStatus.InProgress && - (playerResult == null || (playerResult != null && !playerResult.HasSubmitted)) + (playerResult == null || (playerResult != null && !playerResult.HasSubmitted)), + RoomId = roomId }; } } - public SubmitAnswerResponse SubmitAnswer(SubmitAnswerRequest request) + public SubmitAnswerResponse SubmitAnswer(SubmitAnswerRequest request, int roomId = 1) { lock (_lock) { - var round = _tournament.Rounds.FirstOrDefault(r => r.RoundNumber == request.RoundNumber); + var tournament = GetTournament(roomId); + var round = tournament.Rounds.FirstOrDefault(r => r.RoundNumber == request.RoundNumber); if (round == null || round.Status != RoundStatus.InProgress) { return new SubmitAnswerResponse @@ -375,16 +389,18 @@ public SubmitAnswerResponse SubmitAnswer(SubmitAnswerRequest request) } } - public List GetLeaderboard(bool hideForLastTwoRounds = false) + public List GetLeaderboard(int roomId = 1, bool hideForLastTwoRounds = false) { lock (_lock) { - if (hideForLastTwoRounds && _tournament.CurrentRound >= Tournament.MaxRounds - 1) + var tournament = GetTournament(roomId); + + if (hideForLastTwoRounds && tournament.CurrentRound >= Tournament.MaxRounds - 1) { return new List(); } - var leaderboard = _tournament.Players + var leaderboard = tournament.Players .Select(p => new LeaderboardEntry { PlayerId = p.Id, @@ -404,16 +420,17 @@ public List GetLeaderboard(bool hideForLastTwoRounds = false) } } - public List GetRoundResults(int roundNumber) + public List GetRoundResults(int roomId, int roundNumber) { lock (_lock) { - var round = _tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); + var tournament = GetTournament(roomId); + var round = tournament.Rounds.FirstOrDefault(r => r.RoundNumber == roundNumber); if (round == null) return new List(); return round.PlayerResults - .Join(_tournament.Players, pr => pr.PlayerId, p => p.Id, (pr, p) => new RoundResultEntry + .Join(tournament.Players, pr => pr.PlayerId, p => p.Id, (pr, p) => new RoundResultEntry { PlayerId = p.Id, PlayerName = p.Name, @@ -430,11 +447,12 @@ public List GetRoundResults(int roundNumber) } } - public List GetPlayerResults(int playerId) + public List GetPlayerResults(int playerId, int roomId = 1) { lock (_lock) { - return _tournament.Rounds + var tournament = GetTournament(roomId); + return tournament.Rounds .SelectMany(r => r.PlayerResults) .Where(pr => pr.PlayerId == playerId) .OrderBy(pr => pr.RoundNumber) @@ -448,7 +466,7 @@ private Move GenerateRandomMove() return moves[_random.Next(moves.Length)]; } - private void CalculateRoundScores(Round round) + private void CalculateRoundScores(Round round, Tournament tournament) { foreach (var result in round.PlayerResults) { @@ -460,27 +478,23 @@ private void CalculateRoundScores(Round round) int score = 0; - // Score for correct answer if (result.AnswerCorrect) { score += 30; } - // Score for winning rock-paper-scissors if (DetermineWinner(result.Move.Value, round.ServerMove)) { score += 20; } else if (result.Move.Value == round.ServerMove) { - // Tie gives half points score += 10; } result.Score = score; - // Update player total score - var player = _tournament.Players.FirstOrDefault(p => p.Id == result.PlayerId); + var player = tournament.Players.FirstOrDefault(p => p.Id == result.PlayerId); if (player != null) { player.TotalScore += score; @@ -492,17 +506,14 @@ private bool ValidateAnswer(string playerAnswer, string correctAnswer, string? a { if (string.IsNullOrWhiteSpace(answerRule) || answerRule.Equals("Exact", StringComparison.OrdinalIgnoreCase)) { - // Default exact matching (case-insensitive) return string.Equals(playerAnswer, correctAnswer, StringComparison.OrdinalIgnoreCase); } if (answerRule.Equals("FuzzyMatch", StringComparison.OrdinalIgnoreCase)) { - // Fuzzy matching - check if player answer contains the correct answer (case-insensitive) return playerAnswer.Contains(correctAnswer, StringComparison.OrdinalIgnoreCase); } - // Default to exact matching for unknown rules return string.Equals(playerAnswer, correctAnswer, StringComparison.OrdinalIgnoreCase); } diff --git a/apps-rps/rps-game-server/Views/Home/Index.cshtml b/apps-rps/rps-game-server/Views/Home/Index.cshtml index 01ff253..9402fdd 100644 --- a/apps-rps/rps-game-server/Views/Home/Index.cshtml +++ b/apps-rps/rps-game-server/Views/Home/Index.cshtml @@ -1,20 +1,35 @@ @model RpsGameServer.Models.Tournament @{ ViewData["Title"] = "Tournament Dashboard"; + var roomId = ViewBag.RoomId ?? 1; } + + +
-

Tournament Status: @Model.Status

+

Room @roomId - Tournament Status: @Model.Status

@if (Model.Status == RpsGameServer.Models.TournamentStatus.Pending) {

Tournament is waiting for players to join!

- + View Tournament Lobby
@@ -39,6 +54,7 @@

Click to start round @currentRound.RoundNumber with a random question.

+
} @@ -169,6 +185,7 @@ {
+ @@ -193,6 +210,7 @@ @if (Context.Session.GetString("AdminAuthenticated") == "true") { +
} @@ -213,10 +231,10 @@ {

🏆 Tournament Complete! 🏆

- + View Champions - + Detailed Results