You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
5.0 KiB
Python

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import Optional, List
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), "../../../shared"))
from database.connection import get_db
from database.models import GameSession, Player, GameAnswer, Question
from utils.scoring import ScoringEngine
from websocket.socket_manager import socket_manager
app = FastAPI(title="Game Service", version="1.0.0")
class StartGameRequest(BaseModel):
player_name: str
class AnswerSubmissionRequest(BaseModel):
session_id: int
question_id: int
answer: str
hint_used: bool = False
class GameSessionResponse(BaseModel):
id: int
player_id: int
total_score: int
questions_answered: int
time_remaining: Optional[int] = None
current_question: Optional[dict] = None
@app.get("/health")
def health_check():
return {"status": "healthy", "service": "game-service"}
@app.post("/start", response_model=GameSessionResponse)
def start_game(request: StartGameRequest, db: Session = Depends(get_db)):
player = db.query(Player).filter(Player.name == request.player_name).first()
if not player:
player = Player(name=request.player_name)
db.add(player)
db.commit()
db.refresh(player)
session = GameSession(player_id=player.id)
db.add(session)
db.commit()
db.refresh(session)
return GameSessionResponse(
id=session.id,
player_id=session.player_id,
total_score=session.total_score,
questions_answered=session.questions_answered,
time_remaining=ScoringEngine.get_max_session_duration()
)
@app.post("/answer")
async def submit_answer(request: AnswerSubmissionRequest, db: Session = Depends(get_db)):
session = db.query(GameSession).filter(GameSession.id == request.session_id).first()
if not session:
raise HTTPException(status_code=404, detail="Game session not found")
question = db.query(Question).filter(Question.id == request.question_id).first()
if not question:
raise HTTPException(status_code=404, detail="Question not found")
existing_attempts = db.query(GameAnswer).filter(
GameAnswer.session_id == request.session_id,
GameAnswer.question_id == request.question_id
).count()
if existing_attempts >= ScoringEngine.MAX_ATTEMPTS:
raise HTTPException(status_code=400, detail="Maximum attempts reached")
attempt_number = existing_attempts + 1
is_correct = request.answer.lower().strip() == question.correct_answer.lower().strip()
points = ScoringEngine.calculate_points(is_correct, request.hint_used, attempt_number)
answer = GameAnswer(
session_id=request.session_id,
question_id=request.question_id,
attempt_number=attempt_number,
user_answer=request.answer,
is_correct=is_correct,
hint_used=request.hint_used,
points_earned=points
)
db.add(answer)
if is_correct:
session.total_score += points
session.questions_answered += 1
db.commit()
# Notify real-time subscribers
try:
player_name = session.player.name if session.player else "Unknown"
await socket_manager.notify_answer_submitted(
session_id=request.session_id,
player_name=player_name,
is_correct=is_correct,
points_earned=points,
total_score=session.total_score
)
except Exception as e:
print(f"WebSocket notification failed: {e}")
return {
"correct": is_correct,
"points_earned": points,
"attempts_remaining": ScoringEngine.MAX_ATTEMPTS - attempt_number,
"total_score": session.total_score
}
@app.get("/session/{session_id}")
def get_game_session(session_id: int, db: Session = Depends(get_db)):
session = db.query(GameSession).filter(GameSession.id == session_id).first()
if not session:
raise HTTPException(status_code=404, detail="Game session not found")
return GameSessionResponse(
id=session.id,
player_id=session.player_id,
total_score=session.total_score,
questions_answered=session.questions_answered
)
@app.get("/leaderboard")
def get_leaderboard(db: Session = Depends(get_db)):
result = db.execute("""
SELECT p.name as player_name, gs.total_score, gs.questions_answered, gs.ended_at
FROM game_sessions gs
JOIN players p ON gs.player_id = p.id
WHERE gs.ended_at IS NOT NULL
ORDER BY gs.total_score DESC, gs.ended_at ASC
LIMIT 10
""").fetchall()
return [
{
"player_name": row.player_name,
"score": row.total_score,
"questions_answered": row.questions_answered,
"completed_at": row.ended_at
}
for row in result
]
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8001)