Files
Trivy/docs/plans/2026-01-26-trivy-design.md
consultoria-as e5a2b016a0 chore: Rename project from WebTriviasMulti to Trivy
- 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>
2026-01-26 09:22:04 +00:00

660 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
1. **Creación de sala**: Un jugador crea sala y recibe código de 6 caracteres
2. **Lobby**: Jugadores se unen con el código, eligen equipo (máx 4 por equipo), ingresan nombre
3. **Inicio**: El host inicia cuando hay al menos 1 jugador por equipo
4. **Tablero**: Se muestra el tablero con 8 categorías × 5 preguntas (100-500 pts)
5. **Turno**: El jugador en rotación del equipo activo selecciona una pregunta
6. **Respuesta**: Tiene X segundos (según dificultad) para escribir respuesta
7. **Validación IA**: Claude valida si la respuesta es correcta
8. **Robo opcional**: Si falla, equipo contrario decide si intenta robar
9. **Siguiente turno**: El equipo ganador elige siguiente pregunta
10. **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
```sql
-- 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
```python
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
```python
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
1. Admin solicita generar preguntas desde panel
2. Claude genera batch de preguntas
3. Quedan en estado `pending`
4. Admin revisa, edita si necesario, aprueba o rechaza
5. 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
1. Al finalizar partida, opción "Ver Replay"
2. Carga eventos ordenados por timestamp
3. Reproduce animación acelerada (x2, x4, x8)
4. Muestra:
- Tablero con preguntas revelándose
- Respuestas de jugadores
- Marcador actualizándose
- Momentos de robo
5. 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
```yaml
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
```env
# 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
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
- [Socket.IO](https://socket.io/)
- [Anthropic Claude API](https://docs.anthropic.com/)
- [React](https://react.dev/)
- [Tailwind CSS](https://tailwindcss.com/)
- [Framer Motion](https://www.framer.com/motion/)
---
*Documento generado el 2026-01-26*