feat(phase3): Implement complete game logic with WebSocket events
Timer System: - Add TimerManager service with asyncio for server-side timers - Support steal time reduction (50% time) - Automatic timer cancellation on answer Question Loading: - Add QuestionService to load daily questions from PostgreSQL - Generate 8×5 board (categories × difficulties) - Filter by date_active and approved status Database Integration: - Create GameSession in PostgreSQL when game starts - Update scores during game and on finish - Store db_session_id in Redis for cross-reference Replay Integration: - Save all game events: question_selected, answer_submitted, steal_attempted, steal_passed, game_finished - Generate unique replay code on game finish Achievement Integration: - Initialize PlayerStats in Redis when joining room - Update stats on every answer (streak, category, speed, etc.) - Check achievements on game finish for all players Game Finish: - Automatic finish when all questions answered - Manual finish by host - Emit game_finished with winner, scores, replay_code, achievements Phase 3 tasks completed: - F3.1: Timer manager with asyncio - F3.2: Question service for board loading - F3.3: GameSession PostgreSQL integration - F3.4: Replay event saving - F3.5: Achievement stats tracking - F3.6: Complete game finish flow Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.services.room_manager import room_manager
|
||||
from app.services.ai_validator import ai_validator
|
||||
from app.services.question_service import question_service
|
||||
from app.models.game_session import GameSession
|
||||
from app.config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
@@ -200,5 +204,105 @@ class GameManager:
|
||||
return datetime.utcnow() + timedelta(seconds=time_seconds)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Database Integration Methods
|
||||
# ============================================================
|
||||
|
||||
async def create_db_session(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
room_code: str
|
||||
) -> GameSession:
|
||||
"""Crea una sesión de juego en PostgreSQL."""
|
||||
session = GameSession(
|
||||
room_code=room_code,
|
||||
status="waiting"
|
||||
)
|
||||
db.add(session)
|
||||
await db.commit()
|
||||
await db.refresh(session)
|
||||
return session
|
||||
|
||||
async def get_db_session(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
room_code: str
|
||||
) -> Optional[GameSession]:
|
||||
"""Obtiene sesión de BD por room_code."""
|
||||
result = await db.execute(
|
||||
select(GameSession).where(GameSession.room_code == room_code)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def update_db_session(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
room_code: str,
|
||||
**kwargs
|
||||
) -> Optional[GameSession]:
|
||||
"""Actualiza sesión en BD."""
|
||||
session = await self.get_db_session(db, room_code)
|
||||
if session:
|
||||
for key, value in kwargs.items():
|
||||
if hasattr(session, key):
|
||||
setattr(session, key, value)
|
||||
await db.commit()
|
||||
await db.refresh(session)
|
||||
return session
|
||||
|
||||
async def start_game_with_db(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
room_code: str
|
||||
) -> Optional[dict]:
|
||||
"""
|
||||
Inicia juego: crea sesión en BD, carga tablero, actualiza Redis.
|
||||
"""
|
||||
# Crear sesión en BD
|
||||
db_session = await self.create_db_session(db, room_code)
|
||||
|
||||
# Cargar tablero del día
|
||||
board = await question_service.get_board_for_game(db)
|
||||
|
||||
if not board:
|
||||
# No hay preguntas para hoy
|
||||
return None
|
||||
|
||||
# Iniciar en Redis (método existente)
|
||||
room = await self.start_game(room_code, board)
|
||||
|
||||
if room:
|
||||
# Guardar session_id en Redis para referencia
|
||||
room["db_session_id"] = db_session.id
|
||||
await room_manager.update_room(room_code, room)
|
||||
|
||||
# Actualizar BD
|
||||
await self.update_db_session(
|
||||
db, room_code,
|
||||
status="playing"
|
||||
)
|
||||
|
||||
return room
|
||||
|
||||
async def finish_game(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
room_code: str,
|
||||
team_a_score: int,
|
||||
team_b_score: int,
|
||||
questions_used: list
|
||||
) -> Optional[GameSession]:
|
||||
"""Finaliza el juego y guarda en BD."""
|
||||
session = await self.update_db_session(
|
||||
db, room_code,
|
||||
status="finished",
|
||||
team_a_score=team_a_score,
|
||||
team_b_score=team_b_score,
|
||||
questions_used=questions_used,
|
||||
finished_at=datetime.utcnow()
|
||||
)
|
||||
return session
|
||||
|
||||
|
||||
# Singleton instance
|
||||
game_manager = GameManager()
|
||||
|
||||
Reference in New Issue
Block a user