feat: 5 categorías rotativas por partida + pool de 200 preguntas + mejoras UI
Cambios principales: - Tablero ahora muestra 5 categorías aleatorias (de 8 disponibles) - Pool de 200 preguntas (8 cats × 5 diffs × 5 opciones) - Preguntas rotan aleatoriamente entre partidas - Diseño mejorado estilo Jeopardy con efectos visuales - Socket singleton para conexión persistente - Nuevos sonidos: game_start, player_join, question_reveal, hover, countdown - Control de volumen vertical - Barra de progreso del timer en modal de preguntas - Animaciones mejoradas con Framer Motion Backend: - question_service: selección aleatoria de 5 categorías - room_manager: fix retorno de create_room - game_events: carga board desde DB, await en enter_room Frontend: - Game.tsx: tablero dinámico, efectos hover, mejor scoreboard - useSocket: singleton service, eventos con sonidos - SoundControl: slider vertical - soundStore: 5 nuevos efectos de sonido Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
from typing import Optional, List, Dict
|
||||
from datetime import date
|
||||
from sqlalchemy import select, and_
|
||||
import random
|
||||
from sqlalchemy import select, and_, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.question import Question
|
||||
from app.models.category import Category
|
||||
|
||||
# Number of categories per game
|
||||
CATEGORIES_PER_GAME = 5
|
||||
|
||||
|
||||
class QuestionService:
|
||||
async def get_daily_questions(
|
||||
@@ -64,16 +68,49 @@ class QuestionService:
|
||||
target_date: Optional[date] = None
|
||||
) -> Dict[str, List[dict]]:
|
||||
"""
|
||||
Genera el tablero 8×5 para el juego.
|
||||
Si no hay suficientes preguntas, retorna lo disponible.
|
||||
Genera el tablero 5×5 para el juego.
|
||||
Selecciona 5 categorías aleatorias y 1 pregunta por dificultad.
|
||||
|
||||
Returns:
|
||||
Dict con category_id como string (para JSON) -> lista de preguntas
|
||||
"""
|
||||
board = await self.get_daily_questions(db, target_date)
|
||||
full_board = await self.get_daily_questions(db, target_date)
|
||||
|
||||
# Convertir keys a string para JSON
|
||||
return {str(k): v for k, v in board.items()}
|
||||
if not full_board:
|
||||
return {}
|
||||
|
||||
# Get available category IDs that have questions
|
||||
available_categories = list(full_board.keys())
|
||||
|
||||
# Select random categories (up to CATEGORIES_PER_GAME)
|
||||
num_categories = min(CATEGORIES_PER_GAME, len(available_categories))
|
||||
selected_categories = random.sample(available_categories, num_categories)
|
||||
|
||||
# Build the game board with selected categories
|
||||
game_board: Dict[str, List[dict]] = {}
|
||||
|
||||
for cat_id in selected_categories:
|
||||
questions_by_difficulty: Dict[int, List[dict]] = {}
|
||||
|
||||
# Group questions by difficulty
|
||||
for q in full_board[cat_id]:
|
||||
diff = q["difficulty"]
|
||||
if diff not in questions_by_difficulty:
|
||||
questions_by_difficulty[diff] = []
|
||||
questions_by_difficulty[diff].append(q)
|
||||
|
||||
# Select one random question per difficulty
|
||||
selected_questions = []
|
||||
for difficulty in range(1, 6): # 1-5
|
||||
if difficulty in questions_by_difficulty:
|
||||
questions = questions_by_difficulty[difficulty]
|
||||
selected_q = random.choice(questions)
|
||||
selected_questions.append(selected_q)
|
||||
|
||||
if selected_questions:
|
||||
game_board[str(cat_id)] = selected_questions
|
||||
|
||||
return game_board
|
||||
|
||||
async def get_question_by_id(
|
||||
self,
|
||||
|
||||
@@ -59,9 +59,9 @@ class RoomManager:
|
||||
)
|
||||
|
||||
# Add player to room
|
||||
await self.add_player(room_code, player_name, "A", socket_id)
|
||||
room = await self.add_player(room_code, player_name, "A", socket_id)
|
||||
|
||||
return room_state
|
||||
return room
|
||||
|
||||
async def get_room(self, room_code: str) -> Optional[dict]:
|
||||
"""Get room state by code."""
|
||||
|
||||
@@ -50,7 +50,7 @@ def register_socket_events(sio: socketio.AsyncServer):
|
||||
await room_manager.init_player_stats(room["code"], player_name)
|
||||
|
||||
# Join socket room
|
||||
sio.enter_room(sid, room["code"])
|
||||
await sio.enter_room(sid, room["code"])
|
||||
|
||||
await sio.emit("room_created", {"room": room}, to=sid)
|
||||
|
||||
@@ -75,7 +75,7 @@ def register_socket_events(sio: socketio.AsyncServer):
|
||||
await room_manager.init_player_stats(room_code, player_name)
|
||||
|
||||
# Join socket room
|
||||
sio.enter_room(sid, room_code)
|
||||
await sio.enter_room(sid, room_code)
|
||||
|
||||
# Notify all players
|
||||
await sio.emit("player_joined", {"room": room}, room=room_code)
|
||||
@@ -147,13 +147,18 @@ def register_socket_events(sio: socketio.AsyncServer):
|
||||
)
|
||||
return
|
||||
|
||||
# Get board from data or generate
|
||||
board = data.get("board", {})
|
||||
|
||||
updated_room = await game_manager.start_game(room_code, board)
|
||||
# Load board from database and start game
|
||||
async with await get_db_session() as db:
|
||||
updated_room = await game_manager.start_game_with_db(db, room_code)
|
||||
|
||||
if updated_room:
|
||||
await sio.emit("game_started", {"room": updated_room}, room=room_code)
|
||||
else:
|
||||
await sio.emit(
|
||||
"error",
|
||||
{"message": "No hay preguntas disponibles para hoy. Contacta al administrador."},
|
||||
to=sid
|
||||
)
|
||||
|
||||
@sio.event
|
||||
async def select_question(sid, data):
|
||||
|
||||
Reference in New Issue
Block a user