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
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) |