- Update all code references to new name - Rename design document - Update package.json - Update frontend titles and branding Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
22 KiB
22 KiB
Trivy - Documento de Diseño
Fecha: 2026-01-26 Versión: 1.0 Estado: Aprobado
1. Visión General
1.1 Descripción
Trivy es una aplicación web de trivia multiplayer en tiempo real, inspirada en el formato de Jeopardy. Permite partidas entre 2 equipos de hasta 4 jugadores cada uno, con preguntas organizadas por categorías y niveles de dificultad.
1.2 Características Principales
- Partidas en tiempo real con WebSockets
- 8 categorías temáticas: Nintendo, Xbox, PlayStation, Anime, Música, Películas, Libros, Historia-Cultura
- Tablero estilo Jeopardy (5 niveles de dificultad por categoría)
- Sistema de "robo" de puntos entre equipos
- Validación de respuestas mediante IA (Claude)
- Generación automática de preguntas con aprobación de administrador
- 5 temas visuales intercambiables
- Sistema de logros
- Replays de partidas
- Sonidos temáticos
2. Mecánicas del Juego
2.1 Flujo de Partida
- Creación de sala: Un jugador crea sala y recibe código de 6 caracteres
- Lobby: Jugadores se unen con el código, eligen equipo (máx 4 por equipo), ingresan nombre
- Inicio: El host inicia cuando hay al menos 1 jugador por equipo
- Tablero: Se muestra el tablero con 8 categorías × 5 preguntas (100-500 pts)
- Turno: El jugador en rotación del equipo activo selecciona una pregunta
- Respuesta: Tiene X segundos (según dificultad) para escribir respuesta
- Validación IA: Claude valida si la respuesta es correcta
- Robo opcional: Si falla, equipo contrario decide si intenta robar
- Siguiente turno: El equipo ganador elige siguiente pregunta
- Final: Partida termina cuando se agotan las preguntas
2.2 Sistema de Turnos
- Rotación obligatoria: Cada pregunta la responde un miembro diferente del equipo
- El que acierta elige: El equipo que responde correctamente (o roba) elige la siguiente pregunta
2.3 Sistema de Puntos y Tiempo
| Dificultad | Puntos | Tiempo |
|---|---|---|
| 1 (Fácil) | 100 | 15 seg |
| 2 | 200 | 20 seg |
| 3 (Media) | 300 | 25 seg |
| 4 | 400 | 35 seg |
| 5 (Difícil) | 500 | 45 seg |
2.4 Mecánica de Robo
- Voluntario: El equipo contrario decide si intenta robar o pasar
- Penalización: Si fallan el robo, pierden la mitad de los puntos de la pregunta
- Tiempo reducido: El equipo que roba tiene la mitad del tiempo original
2.5 Formato de Respuestas
- Respuesta abierta (el jugador escribe libremente)
- Validación semántica con IA (acepta sinónimos, variaciones, errores de ortografía menores)
3. Arquitectura Técnica
3.1 Stack Tecnológico
┌─────────────────────────────────────────────────────────────┐
│ FRONTEND (React) │
│ - Vite + React 18 + TypeScript │
│ - Tailwind CSS + Framer Motion (animaciones) │
│ - Socket.io-client (tiempo real) │
│ - Zustand (estado global) │
│ - 5 temas visuales intercambiables │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ BACKEND (Python FastAPI) │
│ - FastAPI + Uvicorn │
│ - python-socketio (WebSockets) │
│ - SQLAlchemy (ORM) │
│ - Anthropic SDK (validación IA) │
│ - APScheduler (tareas programadas) │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│PostgreSQL│ │ Redis │ │ Claude │
│(datos) │ │ (estado) │ │ (IA) │
└──────────┘ └──────────┘ └──────────┘
3.2 Responsabilidades por Componente
- PostgreSQL: Preguntas, categorías, historial de partidas, configuración, logros
- Redis: Estado de salas activas, turnos, temporizadores, sesiones WebSocket
- Claude API: Validar respuestas, generar preguntas nuevas
4. Modelo de Datos
4.1 PostgreSQL
-- Categorías
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
icon VARCHAR(50),
color VARCHAR(7)
);
-- Preguntas
CREATE TABLE questions (
id SERIAL PRIMARY KEY,
category_id INTEGER REFERENCES categories(id),
question_text TEXT NOT NULL,
correct_answer VARCHAR(500) NOT NULL,
alt_answers TEXT[], -- Respuestas alternativas válidas
difficulty INTEGER CHECK (difficulty BETWEEN 1 AND 5),
points INTEGER NOT NULL,
time_seconds INTEGER NOT NULL,
date_active DATE,
status VARCHAR(20) DEFAULT 'pending', -- pending, approved, used
fun_fact TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Sesiones de juego
CREATE TABLE game_sessions (
id SERIAL PRIMARY KEY,
room_code VARCHAR(6) UNIQUE NOT NULL,
status VARCHAR(20) DEFAULT 'waiting', -- waiting, playing, finished
team_a_score INTEGER DEFAULT 0,
team_b_score INTEGER DEFAULT 0,
current_team VARCHAR(1), -- 'A' o 'B'
questions_used INTEGER[],
created_at TIMESTAMP DEFAULT NOW(),
finished_at TIMESTAMP
);
-- Eventos de juego (para replays)
CREATE TABLE game_events (
id SERIAL PRIMARY KEY,
session_id INTEGER REFERENCES game_sessions(id),
event_type VARCHAR(50) NOT NULL,
player_name VARCHAR(100),
team VARCHAR(1),
question_id INTEGER REFERENCES questions(id),
answer_given TEXT,
was_correct BOOLEAN,
was_steal BOOLEAN DEFAULT FALSE,
points_earned INTEGER,
timestamp TIMESTAMP DEFAULT NOW()
);
-- Administradores
CREATE TABLE admins (
id SERIAL PRIMARY KEY,
username VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
4.2 Redis (Estado Volátil)
room:{code} → {
players: [{name, team, position, socketId}],
teams: {A: [], B: []},
currentTeam: 'A',
currentPlayerIndex: {A: 0, B: 0},
status: 'waiting|playing|finished',
timer: timestamp,
canSteal: false,
currentQuestion: questionId
}
player:{socket_id} → {name, room, team, position}
5. Sistema de IA
5.1 Validación de Respuestas
VALIDATION_PROMPT = """
Eres un validador de trivia. Determina si la respuesta del jugador
es correcta comparándola con la respuesta oficial.
Pregunta: {question}
Respuesta correcta: {correct_answer}
Respuestas alternativas válidas: {alt_answers}
Respuesta del jugador: {player_answer}
Considera válido si:
- Es sinónimo o variación de la respuesta correcta
- Tiene errores menores de ortografía
- Usa abreviaciones comunes (ej: "BOTW" = "Breath of the Wild")
- Es conceptualmente equivalente
Responde SOLO con JSON: {"valid": true/false, "reason": "breve explicación"}
"""
5.2 Generación de Preguntas
GENERATION_PROMPT = """
Genera 5 preguntas de trivia para la categoría {category}.
Dificultad: {difficulty} (1=muy fácil, 5=muy difícil)
Formato JSON por pregunta:
{
"question": "texto de la pregunta",
"correct_answer": "respuesta principal",
"alt_answers": ["variación1", "variación2"],
"fun_fact": "dato curioso opcional"
}
Requisitos:
- Las preguntas deben ser verificables y precisas
- Evitar ambigüedades
- Ajustar complejidad según dificultad
- Para gaming: incluir referencias a juegos, personajes, mecánicas
- Para cultura: hechos históricos, arte, literatura
"""
5.3 Flujo de Aprobación
- Admin solicita generar preguntas desde panel
- Claude genera batch de preguntas
- Quedan en estado
pending - Admin revisa, edita si necesario, aprueba o rechaza
- Preguntas aprobadas se asignan a fecha futura
6. Temas Visuales
6.1 Temas Disponibles
| Tema | Paleta Principal | Características |
|---|---|---|
| DRRR (Dollars) | Negro, amarillo neón (#FFE135), cyan (#00FFFF) | Chat estilo Dollars, efectos glitch, tipografía urbana, bordes neón pulsantes |
| Retro Arcade | Púrpura (#9B59B6), rosa (#E91E63), cyan pixelado | Pixel art UI, tipografía 8-bit (Press Start 2P), scanlines, efectos CRT |
| Moderno Minimalista | Blanco (#FFFFFF), grises, acento azul (#3498DB) | Limpio, sombras suaves, tipografía sans-serif, transiciones elegantes |
| Gaming RGB | Negro (#0D0D0D) con gradientes RGB | Efectos de luz LED, gradientes animados, bordes brillantes, estilo "gamer" |
| Anime Clásico 90s | Pasteles, rosa (#FFB6C1), lavanda (#E6E6FA) | Estrellas brillantes, efectos sparkle, bordes redondeados, estilo shoujo |
6.2 Implementación
/src/themes/
├── ThemeProvider.tsx # Context para tema activo
├── index.ts # Exporta todos los temas
├── drrr/
│ ├── variables.css
│ ├── components.tsx
│ └── sounds/
├── retro-arcade/
├── minimal/
├── gaming-rgb/
└── anime-90s/
7. Sistema de Sonidos
7.1 Eventos con Sonido
| Evento | DRRR | Retro | Minimal | RGB | Anime 90s |
|---|---|---|---|---|---|
| Correcto | Glitch digital | 8-bit coin | Soft chime | Synth rise | Sparkle |
| Incorrecto | Static buzz | 8-bit fail | Low tone | Bass drop | Comedic |
| Robo | Suspense | Power-up | Click | Laser | Drama sting |
| Timer (tick) | Heartbeat | Beeps | Ticks | Pulse | Tension |
| Timer (urgente) | Fast heartbeat | Fast beeps | Fast ticks | Fast pulse | Panic |
| Victoria | Epic synth | Fanfare | Elegant | EDM drop | Anime victory |
| Derrota | Glitch fade | Game over | Soft close | Power down | Sad piano |
| Selección | Click neón | 8-bit select | Pop | RGB sweep | Cute pop |
7.2 Almacenamiento
- Archivos en formato WebM/OGG para compatibilidad
- Precargados al seleccionar tema
- Volumen configurable por usuario
8. Sistema de Logros
8.1 Lista de Logros
| ID | Nombre | Condición | Icono |
|---|---|---|---|
| 1 | Primera Victoria | Ganar tu primera partida | 🏆 |
| 2 | Racha de 3 | Responder 3 correctas seguidas | 🔥 |
| 3 | Racha de 5 | Responder 5 correctas seguidas | 🔥🔥 |
| 4 | Ladrón Novato | Primer robo exitoso | 🦝 |
| 5 | Ladrón Maestro | 5 robos exitosos en una partida | 🦝👑 |
| 6 | Especialista Nintendo | 10 correctas en Nintendo | 🍄 |
| 7 | Especialista Xbox | 10 correctas en Xbox | 🎮 |
| 8 | Especialista PlayStation | 10 correctas en PlayStation | 🎯 |
| 9 | Especialista Anime | 10 correctas en Anime | ⛩️ |
| 10 | Especialista Música | 10 correctas en Música | 🎵 |
| 11 | Especialista Películas | 10 correctas en Películas | 🎬 |
| 12 | Especialista Libros | 10 correctas en Libros | 📚 |
| 13 | Especialista Historia | 10 correctas en Historia-Cultura | 🏛️ |
| 14 | Invicto | Ganar sin fallar ninguna pregunta | ⭐ |
| 15 | Velocista | Responder correctamente en menos de 3 segundos | ⚡ |
| 16 | Comeback | Ganar estando 500+ puntos abajo | 🔄 |
| 17 | Dominio Total | Responder las 5 preguntas de una categoría correctamente | 👑 |
| 18 | Arriesgado | Responder correctamente 3 preguntas de 500 pts | 🎰 |
8.2 Almacenamiento
- localStorage por navegador (sin cuentas)
- Estructura:
{odooId: {achievements: [...], stats: {...}}} - Mostrados al final de cada partida (nuevos desbloqueados)
9. Sistema de Replays
9.1 Datos Capturados
Usando la tabla game_events, cada evento registra:
- Tipo de evento (question_selected, answer_submitted, steal_attempted, etc.)
- Jugador y equipo
- Pregunta seleccionada
- Respuesta dada
- Resultado (correcto/incorrecto)
- Puntos ganados/perdidos
- Timestamp preciso
9.2 Reproducción
- Al finalizar partida, opción "Ver Replay"
- Carga eventos ordenados por timestamp
- Reproduce animación acelerada (x2, x4, x8)
- Muestra:
- Tablero con preguntas revelándose
- Respuestas de jugadores
- Marcador actualizándose
- Momentos de robo
- Controles: Play/Pause, velocidad, timeline scrubber
9.3 Compartir
- Código único de replay (basado en session_id)
- URL compartible:
/replay/{code}
10. Panel de Administración
10.1 Funcionalidades
┌─────────────────────────────────────────────────────────────┐
│ PANEL ADMINISTRADOR │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📊 Dashboard │
│ - Partidas activas en tiempo real │
│ - Estadísticas del día │
│ - Preguntas pendientes de aprobación │
│ │
│ ❓ Gestión de Preguntas │
│ - CRUD de preguntas por categoría │
│ - Generar con IA (botón por categoría/dificultad) │
│ - Cola de aprobación │
│ - Asignar a fechas │
│ - Importar/exportar CSV │
│ │
│ 📅 Calendario │
│ - Vista mensual de preguntas programadas │
│ - Alertas de días sin contenido │
│ │
│ 🎮 Monitor │
│ - Salas activas │
│ - Cerrar salas problemáticas │
│ │
│ ⚙️ Configuración │
│ - Tiempos y puntos por dificultad │
│ - Penalización de robo │
│ - API keys │
│ │
└─────────────────────────────────────────────────────────────┘
10.2 Autenticación
- Login con usuario/contraseña
- JWT para sesiones
- Rutas protegidas
/admin/*
11. Comunicación en Partida
11.1 Chat de Equipo
- Visible solo para miembros del mismo equipo
- Mensajes en tiempo real via WebSocket
- Historial durante la partida
11.2 Reacciones Globales
- Emojis predefinidos que todos pueden ver
- Limitado para evitar spam (1 cada 3 segundos)
- Emojis disponibles: 👏 😮 😂 🔥 💀 🎉 😭 🤔
12. Estructura del Proyecto
Trivy/
├── backend/
│ ├── app/
│ │ ├── __init__.py
│ │ ├── main.py
│ │ ├── config.py
│ │ ├── models/
│ │ │ ├── __init__.py
│ │ │ ├── category.py
│ │ │ ├── question.py
│ │ │ ├── game_session.py
│ │ │ ├── game_event.py
│ │ │ └── admin.py
│ │ ├── schemas/
│ │ │ ├── __init__.py
│ │ │ ├── question.py
│ │ │ ├── game.py
│ │ │ └── admin.py
│ │ ├── services/
│ │ │ ├── __init__.py
│ │ │ ├── ai_validator.py
│ │ │ ├── ai_generator.py
│ │ │ ├── game_manager.py
│ │ │ └── room_manager.py
│ │ ├── api/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── game.py
│ │ │ └── replay.py
│ │ └── sockets/
│ │ ├── __init__.py
│ │ └── game_events.py
│ ├── requirements.txt
│ ├── Dockerfile
│ ├── alembic.ini
│ └── alembic/
│ └── versions/
│
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ │ ├── game/
│ │ │ │ ├── Board.tsx
│ │ │ │ ├── QuestionCard.tsx
│ │ │ │ ├── Timer.tsx
│ │ │ │ ├── ScoreBoard.tsx
│ │ │ │ └── AnswerInput.tsx
│ │ │ ├── lobby/
│ │ │ │ ├── CreateRoom.tsx
│ │ │ │ ├── JoinRoom.tsx
│ │ │ │ ├── TeamSelect.tsx
│ │ │ │ └── PlayerList.tsx
│ │ │ ├── chat/
│ │ │ │ ├── TeamChat.tsx
│ │ │ │ └── EmojiReactions.tsx
│ │ │ ├── replay/
│ │ │ │ ├── ReplayPlayer.tsx
│ │ │ │ └── ReplayControls.tsx
│ │ │ ├── achievements/
│ │ │ │ ├── AchievementPopup.tsx
│ │ │ │ └── AchievementList.tsx
│ │ │ └── ui/
│ │ │ ├── Button.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── Input.tsx
│ │ │ └── Toast.tsx
│ │ ├── themes/
│ │ │ ├── ThemeProvider.tsx
│ │ │ ├── index.ts
│ │ │ ├── drrr/
│ │ │ ├── retro-arcade/
│ │ │ ├── minimal/
│ │ │ ├── gaming-rgb/
│ │ │ └── anime-90s/
│ │ ├── hooks/
│ │ │ ├── useSocket.ts
│ │ │ ├── useGame.ts
│ │ │ ├── useSound.ts
│ │ │ └── useAchievements.ts
│ │ ├── stores/
│ │ │ ├── gameStore.ts
│ │ │ ├── themeStore.ts
│ │ │ └── soundStore.ts
│ │ ├── pages/
│ │ │ ├── Home.tsx
│ │ │ ├── Lobby.tsx
│ │ │ ├── Game.tsx
│ │ │ ├── Replay.tsx
│ │ │ ├── Results.tsx
│ │ │ └── admin/
│ │ │ ├── Dashboard.tsx
│ │ │ ├── Questions.tsx
│ │ │ ├── Calendar.tsx
│ │ │ ├── Monitor.tsx
│ │ │ └── Settings.tsx
│ │ ├── services/
│ │ │ ├── socket.ts
│ │ │ └── api.ts
│ │ ├── types/
│ │ │ └── index.ts
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── public/
│ │ └── sounds/
│ ├── package.json
│ ├── Dockerfile
│ ├── vite.config.ts
│ ├── tailwind.config.js
│ └── tsconfig.json
│
├── docker-compose.yml
├── .env.example
├── .gitignore
├── README.md
└── docs/
└── plans/
└── 2026-01-26-webtriviasmulti-design.md
13. Despliegue
13.1 Docker Compose
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- VITE_API_URL=http://localhost:8000
- VITE_WS_URL=ws://localhost:8000
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://trivia:trivia@db:5432/trivia
- REDIS_URL=redis://redis:6379
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- JWT_SECRET=${JWT_SECRET}
depends_on:
- db
- redis
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=trivia
- POSTGRES_USER=trivia
- POSTGRES_PASSWORD=trivia
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel run
environment:
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
depends_on:
- frontend
- backend
volumes:
postgres_data:
redis_data:
13.2 Variables de Entorno
# Backend
DATABASE_URL=postgresql://trivia:trivia@db:5432/trivia
REDIS_URL=redis://redis:6379
ANTHROPIC_API_KEY=sk-ant-...
JWT_SECRET=your-secret-key
# Frontend
VITE_API_URL=https://trivia.tudominio.com/api
VITE_WS_URL=wss://trivia.tudominio.com
# Cloudflare
CLOUDFLARE_TUNNEL_TOKEN=your-tunnel-token
14. Roadmap Futuro
Fase 3 - Competitivo
- Ranking global (requiere cuentas opcionales)
- Torneos programados
- Temporadas con recompensas
Fase 4 - Social
- Compartir resultados en redes
- Salas recurrentes
- Desafíos diarios
Fase 5 - Contenido
- Categorías rotativas por eventos
- Preguntas de la comunidad
- Modo "Experto"
Fase 6 - Técnico
- PWA instalable
- API pública
- Modo offline
15. Referencias
Documento generado el 2026-01-26