From c1e98bc2de426f6490f63ff6eb7172204806e74f Mon Sep 17 00:00:00 2001 From: Mercurio <47455213+NotLugozzi@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:18:54 +0100 Subject: [PATCH] 2v2 match endpoints. --- calls.py | 4 +-- main.py | 90 +++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/calls.py b/calls.py index dd84141..0718fd5 100644 --- a/calls.py +++ b/calls.py @@ -114,9 +114,9 @@ def get_leaderboard(): conn = get_db_connection() cursor = conn.cursor() try: - cursor.execute("SELECT display_name, current_elo, uid FROM users WHERE current_elo IS NOT NULL ORDER BY current_elo DESC;") + cursor.execute("SELECT display_name, openskill_mu, current_elo, uid FROM users WHERE current_elo IS NOT NULL ORDER BY current_elo DESC;") players = cursor.fetchall() - player_elo_list = [{"player_name": player["display_name"], "elo_rating": player["current_elo"], "friend_code": player["uid"]} for player in players] + player_elo_list = [{"player_name": player["display_name"],"osk_mu":player["openskill_mu"], "elo_rating": player["current_elo"], "friend_code": player["uid"]} for player in players] return player_elo_list finally: cursor.close() diff --git a/main.py b/main.py index 50957a7..2ef4435 100644 --- a/main.py +++ b/main.py @@ -24,7 +24,7 @@ class RegisterRequest(BaseModel): class JoinMatch2v2Request(BaseModel): token: str match_id: int - slot: str + slot: int class LoginRequest(BaseModel): email: str @@ -358,12 +358,12 @@ def get_profile(request: ProfileRequest): conn = get_db_connection() cursor = conn.cursor() try: - cursor.execute("SELECT uid, display_name, current_elo FROM users WHERE session_token = %s;", (request.token,)) + cursor.execute("SELECT uid, display_name, current_elo, openskill_mu, openskill_sigma FROM users WHERE session_token = %s;", (request.token,)) user = cursor.fetchone() if not user: raise HTTPException(status_code=404, detail="User not found") - uid, display_name, current_elo = user["uid"], user["display_name"], user["current_elo"] + uid, display_name, current_elo, oskmu, osksig = user["uid"], user["display_name"], user["current_elo"], user["openskill_mu"], user["openskill_sigma"] cursor.execute( """ @@ -396,6 +396,8 @@ def get_profile(request: ProfileRequest): "name": display_name, "uid": uid, "elo": current_elo, + "osk_mu": oskmu, + "osk_sig": osksig, "matches": [ { "match_id": match["match_id"], @@ -417,7 +419,7 @@ def get_profile(request: ProfileRequest): conn = get_db_connection() cursor = conn.cursor() try: - cursor.execute("SELECT uid, display_name, current_elo FROM users WHERE token = %s;", (request.token,)) + cursor.execute("SELECT uid, display_name, current_elo, openskill_mu, openskill_sigma FROM users WHERE token = %s;", (request.token,)) user = cursor.fetchone() if not user: raise HTTPException(status_code=404, detail="User not found") @@ -456,6 +458,8 @@ def get_profile(request: ProfileRequest): "name": display_name, "uid": uid, "elo": current_elo, + "openskill_rate": openskill_mu, + "openskill_stdev": openskill_sigma, "matches": [ { "match_id": match["match_id"], @@ -494,39 +498,44 @@ def get_latest_commit_hashes(): @app.post("/joinmatch_2v2") def join_match_2v2(request: JoinMatch2v2Request): conn = get_db_connection() - cursor = conn.cursor() + cursor = conn.cursor() # Use RealDictCursor try: player_uid = get_uid_by_token(request.token) - - # Map slot names to database column names valid_slots = { - "player2_team1": "player2_team1_uid", - "player1_team2": "player1_team2_uid", - "player2_team2": "player2_team2_uid", + 2: "player2_team1_uid", + 3: "player1_team2_uid", + 4: "player2_team2_uid", } - + if request.slot not in valid_slots: raise HTTPException(status_code=400, detail=f"Invalid slot: {request.slot}") column_name = valid_slots[request.slot] - - # Check if the match exists and the slot is available + + # Fetch current match data cursor.execute( - f""" - SELECT {column_name} + """ + SELECT player1_team1_uid, player2_team1_uid, player1_team2_uid, player2_team2_uid FROM matches_2v2 WHERE match_id = %s; """, (request.match_id,) ) - slot_status = cursor.fetchone() - if not slot_status: + match = cursor.fetchone() # RealDictCursor returns a dictionary + if not match: raise HTTPException(status_code=404, detail="Match not found") - if slot_status[column_name] is not None: + # Access player slots using dictionary keys + player1_team1_uid = match['player1_team1_uid'] + player2_team1_uid = match['player2_team1_uid'] + player1_team2_uid = match['player1_team2_uid'] + player2_team2_uid = match['player2_team2_uid'] + + # Check if the slot is already occupied + if match[column_name] is not None: raise HTTPException(status_code=400, detail=f"Slot '{request.slot}' is already occupied") - # Update the match to assign the player to the specified slot + # Update the match with the new player's UID cursor.execute( f""" UPDATE matches_2v2 @@ -537,10 +546,41 @@ def join_match_2v2(request: JoinMatch2v2Request): ) conn.commit() + # Build the list of player UIDs + player_uids = [ + uid for uid in [player1_team1_uid, player2_team1_uid, player1_team2_uid, player2_team2_uid] + if uid is not None + ] + [player_uid] + + # Fix the WHERE IN clause for PostgreSQL + placeholders = ', '.join(['%s'] * len(player_uids)) + query = f""" + SELECT uid, display_name + FROM users + WHERE uid IN ({placeholders}); + """ + cursor.execute(query, player_uids) + user_data = cursor.fetchall() + user_map = {user['uid']: user['display_name'] for user in user_data} + + # Check if all other slots are filled + can_edit = all( + match[slot] is not None + for slot in valid_slots.values() + if slot != column_name + ) + return { "message": f"Player successfully joined as {request.slot}", "match_id": request.match_id, "slot": request.slot, + "players": [ + { + "uid": uid, + "name": user_map.get(uid, "Unknown") + } for uid in player_uids + ], + "canEdit": can_edit } except Exception as e: @@ -552,11 +592,14 @@ def join_match_2v2(request: JoinMatch2v2Request): + + @app.post("/endfour") async def end_four_match_openskill(request: EndFourMatch): conn = get_db_connection() cursor = conn.cursor() try: + # Fetch player IDs and their current ratings cursor.execute( """ SELECT player1_team1_uid, player2_team1_uid, player1_team2_uid, player2_team2_uid @@ -588,6 +631,7 @@ async def end_four_match_openskill(request: EndFourMatch): for row in cursor.fetchall() } + # Default ratings for any missing players default_rating = model.rating(mu=25, sigma=8.333) players = { player1_team1_uid: players.get(player1_team1_uid, default_rating), @@ -596,17 +640,21 @@ async def end_four_match_openskill(request: EndFourMatch): player2_team2_uid: players.get(player2_team2_uid, default_rating), } + # Calculate team scores team1_score = request.player1_team1_score + request.player2_team1_score team2_score = request.player1_team2_score + request.player2_team2_score + # Assign ranks based on scores ranks = [0, 1] if team1_score > team2_score else [1, 0] + # Update ratings using OpenSkill updated_ratings = model.rate( [[players[player1_team1_uid], players[player2_team1_uid]], [players[player1_team2_uid], players[player2_team2_uid]]], ranks=ranks ) + # Flatten the results for database updates updates = [ (uid, rating.mu, rating.sigma) for uid, rating in zip( @@ -615,6 +663,7 @@ async def end_four_match_openskill(request: EndFourMatch): ) ] + # Update user ratings in the database for uid, new_mu, new_sigma in updates: cursor.execute( """ @@ -625,6 +674,7 @@ async def end_four_match_openskill(request: EndFourMatch): (new_mu, new_sigma, uid) ) + # Update match results cursor.execute( """ UPDATE matches_2v2 @@ -637,7 +687,7 @@ async def end_four_match_openskill(request: EndFourMatch): (request.player1_team1_score, request.player2_team1_score, request.player1_team2_score, request.player2_team2_score, team1_score, team2_score, - 1 if team1_score > team2_score else 2, + 1 if team1_score > team2_score else 2, # Determine the winner team request.match_id) )