DEV Community

Ugur Akyol
Ugur Akyol

Posted on

🐢 Turtle Bites - A simple gaming leaderboard API

In this series we build and run multi component backend systems in increasing complexity using RecursionTurtle. Each component is introduced out of real need to solve technical problem. Systems are implementations of real world uses cases — albeit simplified.

For the below system, you can go to: https://recursionturtle.com/collections/fundamentals/1 and press designed system to get the solution — run and test!

A simple Gaming Leaderboar API

We’ll design and implement a simple backend service to manage a gaming leaderboard. The service needs to support:

Submitting Scores:

  • Players will submit their scores through an API. The system must process these submissions efficiently and store them for quick retrieval.
  • Fetching the Leaderboard: The system will expose a real-time leaderboard API to display the top players, ranked by their scores.
# Example API call to submit a player's score
requests.post(
    "http://localhost:8000/scores", 
    json={"player_name": "player123", "score": 1500}
)

# Example API call to fetch the leaderboard
response = requests.get(
    "http://localhost:8000/leaderboard",
    params={"top_n": 3}  # Fetch the top 3 players
)
print(response.json())

Enter fullscreen mode Exit fullscreen mode
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
import uvicorn

app = FastAPI()

class ScoreSubmission(BaseModel):
    player_name: str
    score: int

class LeaderboardEntry(BaseModel):
    player_name: str
    score: int

# In-memory storage for leaderboard data (acting as a database)
leaderboard = []

# The simulator will call this endpoint to start the test, Please don't change this!
@app.get("/health", tags=["healthcheck"], summary="Perform a Health Check")
async def get_health():
    return {"status": "OK"}

# Endpoint to submit a player's score
@app.post("/scores", status_code=201, summary="Submit a player's score")
async def submit_score(score_submission: ScoreSubmission):
    if not score_submission.player_name or score_submission.score < 0:
        raise HTTPException(status_code=400, detail="Invalid player name or score")

    # Add or update the player's score in the leaderboard
    for entry in leaderboard:
        if entry.player_name == score_submission.player_name:
            if score_submission.score > entry.score:
                entry.score = score_submission.score  # Update only if the new score is higher
            return {"message": "Score submitted successfully"}

    # If player not found, add a new entry
    leaderboard.append(LeaderboardEntry(player_name=score_submission.player_name, score=score_submission.score))
    return {"message": "Score submitted successfully"}

# Endpoint to retrieve the leaderboard
@app.get("/leaderboard", response_model=List[LeaderboardEntry], summary="Retrieve the top players")
async def get_leaderboard(top_n: Optional[int] = 10):
    if top_n <= 0:
        raise HTTPException(status_code=400, detail="top_n must be a positive integer")
    # Sort leaderboard by score in descending order and return the top N players
    sorted_leaderboard = sorted(leaderboard, key=lambda x: x.score, reverse=True)
    return sorted_leaderboard[:top_n]

if __name__ == "__main__":
    uvicorn.run(app, host='0.0.0.0', port=80)
Enter fullscreen mode Exit fullscreen mode

Takeaways

  • Its always good to add a healthcheck endpoint to the API. Even though frameworks like docker compose supports healthchecks at yaml config, they provide container level healthchecks. Hence the API might not be fully up and the system may respond as healthy.

Top comments (0)