diff --git a/main.py b/main.py index b998630..7f8b1e0 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,8 @@ from fastapi.middleware.cors import CORSMiddleware from calls import * from db import get_db_connection import requests - +from openskill.models import PlackettLuce +model = PlackettLuce() app = FastAPI() app.add_middleware( @@ -20,6 +21,11 @@ class RegisterRequest(BaseModel): display_name: str password: str +class JoinMatch2v2Request(BaseModel): + token: str + match_id: int + slot: str + class LoginRequest(BaseModel): email: str password: str @@ -51,6 +57,12 @@ class EndMatchRequest(BaseModel): player1_score: int player2_score: int +class EndFourMatch(BaseModel): + match_id: int + player1_team1_score: int + player2_team1_score: int + player1_team2_score: int + player2_team2_score: int @app.post("/register") def register(request: RegisterRequest): @@ -459,40 +471,107 @@ def get_latest_commit_hashes(): import trueskill ts = trueskill.TrueSkill(mu=25.0, sigma=8.333, beta=4.166, tau=0.083, draw_probability=0.1) -class EndFourMatch(BaseModel): - match_id: int - player1_team1_score: int - player2_team1_score: int - player1_team2_score: int - player2_team2_score: int -def get_rolling_base(): - conn = get_db_connection() - cursor = conn.cursor() - cursor.execute( - """ - SELECT AVG(current_elo) AS rolling_base - FROM users - WHERE current_elo != 0; - """ - ) - result = cursor.fetchone() - return result["rolling_base"] if result else 10 - -def elo_to_trueskill(elo, base, k=40): - mu = (elo - base) / k - sigma = 8.333 - return ts.create_rating(mu=mu, sigma=sigma) - -@app.post("/endfour") -async def end_four_match(request: EndFourMatch): +@app.post("/creatematch_2v2") +def create_match_2v2(request: CreateMatchRequest): conn = get_db_connection() cursor = conn.cursor() try: + player1_uid = get_uid_by_token(request.token) + cursor.execute( + """ + INSERT INTO matches_2v2 (player1_team1_uid) + VALUES (%s) + RETURNING match_id; + """, + (player1_uid,) + ) + match_id = cursor.fetchone()["match_id"] + conn.commit() + return {"match_id": match_id} + except Exception as e: + conn.rollback() + raise HTTPException(status_code=400, detail=str(e)) + finally: + cursor.close() + conn.close() + +class JoinMatch2v2Request(BaseModel): + token: str + match_id: int + slot: str # e.g., "player1_team1", "player2_team1", "player1_team2", "player2_team2" + +@app.post("/joinmatch_2v2") +def join_match_2v2(request: JoinMatch2v2Request): + conn = get_db_connection() + cursor = conn.cursor() + 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", + } + + 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 + cursor.execute( + f""" + SELECT {column_name} + FROM matches_2v2 + WHERE match_id = %s; + """, + (request.match_id,) + ) + slot_status = cursor.fetchone() + if not slot_status: + raise HTTPException(status_code=404, detail="Match not found") + + if slot_status[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 + cursor.execute( + f""" + UPDATE matches_2v2 + SET {column_name} = %s + WHERE match_id = %s; + """, + (player_uid, request.match_id) + ) + conn.commit() + + return { + "message": f"Player successfully joined as {request.slot}", + "match_id": request.match_id, + "slot": request.slot, + } + + except Exception as e: + conn.rollback() + raise HTTPException(status_code=400, detail=str(e)) + finally: + cursor.close() + conn.close() + + + +@app.post("/endfour_openskill") +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 - FROM fourmatches + FROM matches_2v2 WHERE match_id = %s; """, (request.match_id,) @@ -505,35 +584,50 @@ async def end_four_match(request: EndFourMatch): cursor.execute( """ - SELECT uid, trueskill_mu, trueskill_sigma + SELECT uid, trueskill_mu AS mu, trueskill_sigma AS sigma FROM users WHERE uid IN (%s, %s, %s, %s); """, (player1_team1_uid, player2_team1_uid, player1_team2_uid, player2_team2_uid) ) players = { - row["uid"]: ts.create_rating(mu=row["trueskill_mu"], sigma=row["trueskill_sigma"]) + row["uid"]: model.rating(mu=row["mu"], sigma=row["sigma"]) 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), + player2_team1_uid: players.get(player2_team1_uid, default_rating), + player1_team2_uid: players.get(player1_team2_uid, default_rating), + 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] - new_ratings = ts.rate( + # 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( [player1_team1_uid, player2_team1_uid, player1_team2_uid, player2_team2_uid], - [new_ratings[0][0], new_ratings[0][1], new_ratings[1][0], new_ratings[1][1]] + [updated_ratings[0][0], updated_ratings[0][1], updated_ratings[1][0], updated_ratings[1][1]] ) ] + + # Update user ratings in the database for uid, new_mu, new_sigma in updates: cursor.execute( """ @@ -547,18 +641,23 @@ async def end_four_match(request: EndFourMatch): # Update match results cursor.execute( """ - UPDATE fourmatches + UPDATE matches_2v2 SET player1_team1_score = %s, player2_team1_score = %s, - player1_team2_score = %s, player2_team2_score = %s + player1_team2_score = %s, player2_team2_score = %s, + team1_score = %s, team2_score = %s, + winner_team = %s WHERE match_id = %s; """, (request.player1_team1_score, request.player2_team1_score, - request.player1_team2_score, request.player2_team2_score, request.match_id) + request.player1_team2_score, request.player2_team2_score, + team1_score, team2_score, + 1 if team1_score > team2_score else 2, # Determine the winner team + request.match_id) ) conn.commit() - return {"message": "Match ended and TrueSkill ratings updated successfully"} + return {"message": "Match ended and OpenSkill ratings updated successfully"} except Exception as e: conn.rollback() @@ -567,4 +666,5 @@ async def end_four_match(request: EndFourMatch): cursor.close() conn.close() + '''