From 71bfebbf71f7dbabc25a53baed4f80eb51a6197b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:17:18 +0000
Subject: [PATCH 1/2] Initial plan
From 3ba9eedb9b0de6a8b45e2fd01fbb7341f7c5fc74 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 9 Nov 2025 22:30:25 +0000
Subject: [PATCH 2/2] Add room support for multiple concurrent tournaments
Co-authored-by: qkfang <138417504+qkfang@users.noreply.github.com>
---
apps-rps/rps-game-agent/api_client.py | 11 +-
apps-rps/rps-game-agent/app.py | 19 +-
apps-rps/rps-game-agent/game_processor.py | 5 +-
apps-rps/rps-game-agent/templates/index.html | 16 +-
.../Controllers/AdminController.cs | 29 +--
.../Controllers/HomeController.cs | 94 ++++----
.../Controllers/PlayerController.cs | 23 +-
.../Controllers/TournamentController.cs | 12 +-
apps-rps/rps-game-server/Models/GameModels.cs | 5 +
.../Services/ITournamentService.cs | 32 +--
.../Services/TournamentHistoryService.cs | 3 +-
.../Services/TournamentService.cs | 203 +++++++++---------
.../rps-game-server/Views/Home/Index.cshtml | 26 ++-
13 files changed, 272 insertions(+), 206 deletions(-)
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;
}
+
+
+
@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 @@
{
}
@@ -213,10 +231,10 @@
{
🏆 Tournament Complete! 🏆
-
+
View Champions
-
+
Detailed Results