Magic: The Gathering -- Collection & Deck Compatibility Analyzer
MTG Brain es una aplicacion web que te permite gestionar tu coleccion de cartas de Magic: The Gathering, importar mazos desde Moxfield, y analizar que tan compatible es un mazo con las cartas que ya posees. Si te faltan cartas, el motor de compatibilidad sugiere sustitutos de tu propia coleccion usando dos estrategias: un algoritmo determinista basado en roles funcionales, o un modelo de IA local (LLM) que evalua sinergia y rol.
Todo se ejecuta localmente con Docker. No requiere cuentas externas, API keys, ni conexion a servicios cloud (excepto Scryfall para descargar datos de cartas).
- Funcionalidades
- Arquitectura
- Stack Tecnologico
- Requisitos Previos
- Instalacion
- Primer Uso
- Guia de Uso
- API REST
- Estructura del Proyecto
- Motor de Compatibilidad
- Integracion con LLM (Ollama)
- Desarrollo Local
- Solucion de Problemas
- Roadmap
- Licencia
- Sincronizacion con Scryfall: Descarga las ~280,000 cartas de Magic usando bulk-data streaming. Incluye soporte para cartas de doble cara (DFC), split, adventure y transform.
- Etiquetado funcional automatico: Clasifica cada carta en roles funcionales (RAMP, REMOVAL, DRAW, COUNTER, BOARD_WIPE, TUTOR, PROTECTION, FINISHER, etc.) mediante reglas regex sobre el texto oracle.
- Importacion desde Manabox: Sube tu CSV exportado desde la app Manabox. Detecta cantidad, foil, condicion, idioma y binder.
- Busqueda avanzada: Filtra por nombre, colores (WUBRG), rango de CMC, rareza, tipo y keywords. Paginacion completa.
- Estadisticas: Cartas unicas, total de copias, binders.
- Importacion desde Moxfield: Pega el texto de un mazo en formato Moxfield (ej:
1 Sol Ring (C21) 263). Reconoce secciones Commander, Mainboard, Sideboard y Maybeboard. - Resolucion inteligente: Al importar, busca la version exacta (set + collector number), luego por nombre preferiendo versiones que ya tienes en tu coleccion.
- Visualizacion por categorias: Ver las cartas del mazo organizadas por seccion con imagenes, coste de mana y tipo.
- Comparacion mazo vs. coleccion: Clasifica cada carta como OWNED (la tienes), PARTIAL (tienes algunas copias) o MISSING (no la tienes).
- Porcentaje de propiedad: Calcula que porcentaje del mazo puedes construir con tu coleccion actual.
- Sustitutos inteligentes: Para cartas que te faltan, sugiere reemplazos de tu coleccion usando dos estrategias:
- Roles Funcionales (determinista, instantaneo): Puntua candidatos por tags funcionales compartidos, compatibilidad de color, similitud de CMC y tipo de carta.
- IA Local (Ollama) (opcional, requiere GPU): Un LLM local evalua sinergia, rol funcional y eficiencia de mana. Respuestas en espanol con razonamiento explicado.
- Filtrado por formato: En Commander, solo sugiere cartas legales en el formato y compatibles con la identidad de color del comandante.
- Deteccion de comandante: Identifica automaticamente el comandante (por seccion o heuristicamente) y lo excluye de los sustitutos.
- Streaming en tiempo real (SSE): La estrategia LLM envia progreso en tiempo real al navegador via Server-Sent Events.
http://localhost:3000
|
+-------------v--------------+
| SvelteKit UI |
| (TypeScript + Tailwind |
| + DaisyUI, tema oscuro) |
+-------------+--------------+
|
/api/* proxy
|
+-------------v--------------+
| Spring Boot API |
| (Java 21, Maven) |
| |
| card/ collection/ deck/ |
| compatibility/ |
+------+----------+----------+
| |
+------------v--+ +---v-----------+
| PostgreSQL | | Ollama |
| 16-alpine | | (LLM local) |
| | | Qwen2.5-3B |
+---------------+ +----------------+
- Clean Architecture por modulo: cada bounded context (
card,collection,deck,compatibility) tiene capas separadas:domain/-- Entidades JPA y enumsapplication/-- Servicios y logica de negocioinfrastructure/-- Repositorios, clientes HTTP, configuracioninterfaces/-- Controladores REST y DTOs de respuesta
- Strategy Pattern para algoritmos de compatibilidad (extensible: solo implementar
CompatibilityStrategy) - DDD (Domain-Driven Design): Cuatro bounded contexts independientes
- Database-as-Code: Schema versionado en
init.sql(Hibernate en modovalidate)
| Capa | Tecnologia | Version |
|---|---|---|
| Backend | Java (Eclipse Temurin) | 21 |
| Spring Boot | 3.2.2 | |
| Spring Data JPA / Hibernate | ||
| Maven | 3.9 | |
| OpenCSV | 5.9 | |
| Lombok | ||
| Frontend | SvelteKit | 2.x |
| TypeScript | 5.x | |
| Tailwind CSS + DaisyUI | 3.4 / 4.12 | |
| Vite | 5.x | |
| Base de datos | PostgreSQL | 16 (Alpine) |
Extensiones: uuid-ossp, pg_trgm |
||
| IA Local | Ollama | latest |
| Modelo: Qwen2.5-3B-Instruct (Q4_K_M) | 1.93 GB | |
| Infraestructura | Docker + Docker Compose | |
| APIs externas | Scryfall API | Bulk Data |
- Docker Desktop instalado y en ejecucion
- Windows: Docker Desktop for Windows (requiere WSL2)
- Mac: Docker Desktop for Mac
- Linux: Docker Engine + Docker Compose
- 4 GB de RAM minimo disponible para Docker (recomendado 8 GB)
- 5 GB de espacio en disco (imagenes Docker + base de datos con cartas de Scryfall)
- GPU Nvidia con 4+ GB de VRAM (ej: GTX 1650, RTX 3060, etc.)
- Drivers Nvidia actualizados con soporte CUDA
- Docker Desktop con soporte GPU habilitado (en Windows, funciona automaticamente con WSL2)
- Sin GPU, el LLM funciona en CPU pero sera significativamente mas lento
git clone https://github.com/tu-usuario/mtg-brain.git
cd mtg-brain# Primera vez (construye las imagenes Docker)
docker-compose up --build
# Ejecuciones posteriores
docker-compose up
# En segundo plano (sin ver logs)
docker-compose up -dLa primera ejecucion tarda varios minutos: descarga imagenes de Docker, compila el backend con Maven, e instala las dependencias de Node.js.
Espera a ver estos mensajes en los logs:
mtg-brain-api | Started MtgBrainApplication in X.XX seconds
mtg-brain-ui | Local: http://localhost:3000
Accede a:
| Servicio | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:8082/api/ping |
| Health Check | http://localhost:8082/actuator/health |
| Base de datos | localhost:5433 (user: mtguser, pass: mtgpass_dev, db: mtgbrain) |
| Ollama API | http://localhost:11434 |
Si tienes GPU Nvidia y quieres usar la estrategia de IA:
# 1. Descargar el modelo GGUF desde HuggingFace
# (Qwen2.5-3B-Instruct-Q4_K_M.gguf, ~1.93 GB)
# Colocarlo en la carpeta models/ del proyecto
# 2. Copiar el modelo al contenedor de Ollama
docker cp models/Qwen2.5-3B-Instruct-Q4_K_M.gguf mtg-brain-ollama:/root/models/
# 3. Crear un Modelfile dentro del contenedor
docker exec mtg-brain-ollama sh -c "echo 'FROM /root/models/Qwen2.5-3B-Instruct-Q4_K_M.gguf' > /root/models/Modelfile"
# 4. Registrar el modelo en Ollama
docker exec mtg-brain-ollama sh -c "ollama create qwen2.5-3b-mtg -f /root/models/Modelfile"
# 5. Verificar que el modelo esta disponible
docker exec mtg-brain-ollama ollama listSin este paso, la aplicacion funciona perfectamente pero solo con la estrategia de Roles Funcionales (determinista).
Una vez la aplicacion esta corriendo, sigue estos pasos en orden:
# Desde la terminal (tarda 3-8 minutos, descarga ~280,000 cartas)
curl -X POST http://localhost:8082/api/cards/syncO desde cualquier cliente REST (Postman, etc.): POST http://localhost:8082/api/cards/sync
# Clasifica todas las cartas en roles funcionales (30-60 segundos)
curl -X POST http://localhost:8082/api/cards/tag- Exporta tu coleccion desde Manabox en formato CSV
- Ve a http://localhost:3000/collection/import
- Arrastra o selecciona el archivo CSV
- Revisa el resumen de importacion
- Ve a http://localhost:3000/decks/import
- Copia el contenido de un mazo desde Moxfield (boton "Export" > "Copy to clipboard")
- Pega el texto, selecciona el formato (Commander, Modern, etc.)
- Importa y revisa las cartas resueltas
- Ve a http://localhost:3000/analyze
- Selecciona un mazo de la lista
- (Opcional) Elige la estrategia de analisis si el LLM esta disponible
- Revisa el porcentaje de propiedad, cartas que te faltan y los sustitutos sugeridos
La interfaz tiene cuatro secciones principales accesibles desde la barra superior:
- Inicio (
/) -- Dashboard con estado del sistema y accesos rapidos - Coleccion (
/collection) -- Explorar y filtrar tu coleccion de cartas - Mazos (
/decks) -- Ver y gestionar mazos importados - Analizar (
/analyze) -- Motor de compatibilidad mazo vs. coleccion
La importacion acepta el formato CSV de Manabox. Campos reconocidos:
- Nombre de carta, edicion (set code), numero de colector
- Cantidad, foil, condicion (NM/LP/MP/HP/D), idioma
- Precio de compra, binder
Si una carta del CSV no se encuentra en la base de datos (porque aun no has sincronizado Scryfall, o es un token), aparecera como warning pero no interrumpira la importacion.
El formato aceptado es el texto de exportacion de Moxfield:
Commander
1 Volo, Guide to Monsters (AFR) 238
Mainboard
1 Sol Ring (C21) 263
1 Arcane Signet (CMR) 298
4 Forest (MID) 276
...
Sideboard
1 Nature's Claim (IMA) 177
- Las secciones son opcionales (si no hay cabecera, todo va a Mainboard)
- El set y collector number son opcionales (ayudan a encontrar la version exacta)
- Si tienes una version de la carta en tu coleccion, el sistema la prefiere automaticamente
- OWNED (verde): Tienes suficientes copias en tu coleccion
- PARTIAL (amarillo): Tienes algunas copias pero necesitas mas (ej: necesitas 4, tienes 2)
- MISSING (rojo): No tienes ninguna copia. El sistema sugiere sustitutos con:
- Score (0-100): Puntuacion de similitud con la carta faltante
- Razones: Explicacion de por que se sugiere ese sustituto
- El comandante se marca con una insignia especial y no recibe sustitutos (es insustituible)
| Metodo | Endpoint | Descripcion |
|---|---|---|
GET |
/api/ping |
Health check del backend |
| Metodo | Endpoint | Descripcion |
|---|---|---|
GET |
/api/cards/search?name=&typeLine=&cmc=&page=&size= |
Buscar cartas con filtros |
GET |
/api/cards/{id} |
Obtener carta por Scryfall UUID |
GET |
/api/cards/{id}/versions |
Todas las impresiones de una carta |
GET |
/api/cards/count |
Total de cartas en la BD |
POST |
/api/cards/sync |
Sincronizar bulk-data de Scryfall |
POST |
/api/cards/tag |
Generar tags funcionales |
| Metodo | Endpoint | Descripcion |
|---|---|---|
POST |
/api/collection/import (multipart) |
Importar CSV de Manabox |
GET |
/api/collection?name=&colors=&cmcMin=&cmcMax=&types=&keywords=&rarities=&page=&size= |
Buscar en coleccion con filtros |
GET |
/api/collection/stats |
Estadisticas de la coleccion |
GET |
/api/collection/binders |
Listar binders |
| Metodo | Endpoint | Descripcion |
|---|---|---|
POST |
/api/decks/import (JSON) |
Importar mazo desde texto Moxfield |
GET |
/api/decks |
Listar todos los mazos |
GET |
/api/decks/{id} |
Detalle de un mazo con cartas |
DELETE |
/api/decks/{id} |
Eliminar mazo |
| Metodo | Endpoint | Descripcion |
|---|---|---|
GET |
/api/compatibility/analyze/{deckId}?strategy= |
Analizar compatibilidad (respuesta sincrona) |
GET |
/api/compatibility/analyze/{deckId}/stream?strategy= |
Analizar con streaming SSE (progreso en tiempo real) |
GET |
/api/compatibility/strategies |
Listar estrategias disponibles |
Estrategias disponibles: functional_role (siempre), llm (si Ollama esta configurado)
Eventos SSE del endpoint /stream:
progress--{batch, totalBatches, cardsAnalyzed, totalCards, subsFound}complete-- Resultado completo del analisiserror--{message}
mtg-brain/
|-- docker-compose.yml # Orquestacion de 4 servicios
|-- database/
| +-- init.sql # Schema PostgreSQL (4 tablas + indices + triggers)
|
|-- backend/ # Spring Boot 3.2 / Java 21
| |-- Dockerfile # Multi-stage: Maven build + JRE Alpine runtime
| |-- pom.xml
| +-- src/main/java/com/mtgbrain/
| |-- MtgBrainApplication.java
| |-- config/ # CORS, health check
| |-- card/ # Bounded Context: Cartas
| | |-- domain/ # Card.java (entidad JPA)
| | |-- application/ # CardSyncService, CardSearchService, FunctionalTagger
| | |-- infrastructure/ # CardRepository, ScryfallClient, ScryfallCardDto
| | +-- interfaces/ # CardController, CardSyncController
| |-- collection/ # Bounded Context: Coleccion
| | |-- domain/ # CollectionCard.java
| | |-- application/ # CollectionImportService, CollectionQueryService
| | |-- infrastructure/ # CollectionRepository, ManaboxCsvDto
| | +-- interfaces/ # CollectionController
| |-- deck/ # Bounded Context: Mazos
| | |-- domain/ # Deck.java, DeckCard.java, DeckCategory enum
| | |-- application/ # DeckImportService, DeckQueryService
| | |-- infrastructure/ # DeckRepository
| | +-- interfaces/ # DeckController
| +-- compatibility/ # Bounded Context: Motor de compatibilidad
| |-- application/ # CompatibilityService, CompatibilityStrategy (interface)
| | # FunctionalRoleStrategy, LLMStrategy
| |-- infrastructure/ # OllamaClient, OllamaConfig
| +-- interfaces/ # CompatibilityController
|
+-- frontend/ # SvelteKit + TypeScript
|-- Dockerfile # Node 20 Alpine, dev server con HMR
|-- package.json
|-- tailwind.config.js # Tema oscuro personalizado (Catppuccin Mocha)
+-- src/
|-- app.html # Shell HTML
|-- app.css # Estilos globales + Tailwind
|-- lib/
| |-- actions/ # hoverZoom.ts (popup de zoom al hacer hover)
| |-- components/
| | |-- card/ # CardImage.svelte
| | |-- collection/ # CollectionCardItem, CollectionFilters
| | +-- ui/ # EmptyState, LoadingSpinner, Pagination, StatusBadge, TabNavigation
| |-- services/
| | |-- api.ts # Cliente HTTP centralizado (fetch + timeout)
| | +-- scryfall.ts # Constructor de URLs de imagenes de Scryfall
| +-- types/ # Tipos TypeScript (card, collection, deck, compatibility)
+-- routes/
|-- +layout.svelte # Layout raiz (header, tabs, footer)
|-- +page.svelte # Dashboard (/)
|-- +error.svelte # Pagina de error global
|-- collection/
| |-- +page.svelte # Explorador de coleccion con filtros
| +-- import/+page.svelte # Importacion de CSV
|-- decks/
| |-- +page.svelte # Listado de mazos
| |-- [id]/+page.svelte # Detalle de mazo
| +-- import/+page.svelte # Importacion de texto
+-- analyze/
|-- +page.svelte # Selector de mazo para analizar
+-- [deckId]/+page.svelte # Resultados del analisis
4 tablas en PostgreSQL:
| Tabla | Descripcion | Registros tipicos |
|---|---|---|
cards |
Datos de referencia de Scryfall (inmutables) | ~280,000 |
collection_cards |
Coleccion personal del usuario | Variable |
decks |
Mazos importados (metadatos) | Variable |
deck_cards |
Cartas dentro de cada mazo | Variable |
Extensiones habilitadas: uuid-ossp (UUIDs), pg_trgm (busqueda fuzzy tolerante a typos).
- Carga el mazo con todas sus cartas
- Carga la coleccion completa del usuario
- Compara por
oracle_id(no porscryfall_id, asi diferentes impresiones de la misma carta cuentan como match) - Clasifica cada carta: OWNED, PARTIAL, o MISSING
- Filtra candidatos a sustituto por:
- Legalidad en el formato del mazo (parsea el JSON
legalities) - Compatibilidad de identidad de color (en Commander: colores del sustituto deben ser subconjunto de los del comandante)
- Legalidad en el formato del mazo (parsea el JSON
- Excluye de los sustitutos: el comandante, tierras basicas, sideboard y maybeboard
- Delega la busqueda de sustitutos a la estrategia seleccionada
- Retorna el resultado con porcentaje de propiedad y sustitutos por carta
Puntua cada candidato de 0 a 100:
| Criterio | Puntos max | Logica |
|---|---|---|
| Tags funcionales compartidos | +70 | +50 base por 1 tag comun, +10 por cada adicional |
| Identidad de color compatible | +20 | Colores del sustituto son subconjunto de los de la carta faltante |
| Similitud de CMC | +15 | +15 si igual, +8 si diff=1, +4 si diff=2 |
| Mismo tipo de carta | +15 | Criatura, instantaneo, conjuro, artefacto, etc. |
Umbral minimo: 30 puntos. Maximo 5 sustitutos por carta.
- Pre-filtra candidatos con scoring rapido (tags/color/CMC/tipo) para reducir ~2000 cartas a los top 10
- Agrupa las cartas faltantes en sub-lotes de 3 (para respetar el contexto del modelo)
- Envia un prompt estructurado a Ollama pidiendo respuesta en JSON con score y razones en espanol
- Parsea la respuesta con tolerancia (acepta claves en ingles y espanol, matching case-insensitive)
- Soporta cancelacion via callback para liberar la GPU entre lotes
- Fallback: si Ollama falla en un lote, continua con el siguiente (no interrumpe el analisis completo)
MTG Brain usa Ollama para ejecutar un modelo de IA completamente local, sin enviar datos a servicios externos.
- Qwen2.5-3B-Instruct (cuantizado Q4_K_M)
- Tamano: 1.93 GB
- Cabe completamente en una GTX 1650 (4 GB VRAM) con ~40 tokens/segundo
- Rendimiento razonable para evaluacion de sustitutos MTG
- El backend pre-filtra candidatos a sustituto usando el algoritmo rapido
- Envia al LLM un prompt con la carta faltante y los top candidatos
- El LLM evalua sinergia, rol funcional, eficiencia de mana y potencia
- Devuelve un JSON con scores (0-100) y razones en espanol
- El frontend muestra progreso en tiempo real via Server-Sent Events
Para usar un modelo diferente (ej: Llama 3, Mistral, Phi-3):
# 1. Descargar el .gguf desde HuggingFace
# 2. Copiar al contenedor
docker cp tu-modelo.gguf mtg-brain-ollama:/root/models/
# 3. Crear Modelfile
docker exec mtg-brain-ollama sh -c "echo 'FROM /root/models/tu-modelo.gguf' > /root/models/Modelfile"
# 4. Registrar en Ollama
docker exec mtg-brain-ollama sh -c "ollama create nombre-modelo -f /root/models/Modelfile"
# 5. Actualizar la variable de entorno en docker-compose.yml
# OLLAMA_MODEL: nombre-modelo
# 6. Reiniciar el backend
docker-compose restart backendNota: Modelos mayores a 4 GB de VRAM no cabran en una GTX 1650. El modelo Qwen2.5-3B fue elegido por su balance entre calidad y tamano.
Si no tienes GPU o no quieres usar el LLM, cambia en docker-compose.yml:
OLLAMA_ENABLED: "false"La aplicacion funcionara normalmente con la estrategia de Roles Funcionales.
# Arrancar todos los servicios
docker-compose up --build
# Arrancar en segundo plano
docker-compose up -d
# Ver logs de todos los servicios
docker-compose logs -f
# Ver logs solo del backend
docker-compose logs -f backend
# Parar servicios
docker-compose stop
# Parar y eliminar contenedores (mantiene datos)
docker-compose down
# Eliminar TODO incluido datos de la BD
docker-compose down -v
# Reconstruir un servicio especifico
docker-compose up --build backend
# Acceder a la shell del backend
docker-compose exec backend sh
# Acceder a PostgreSQL
docker-compose exec postgres psql -U mtguser -d mtgbrainEl backend usa un Dockerfile multi-stage: Maven build + JRE Alpine runtime (~150 MB).
Para desarrollo con IDE:
- Abrir
backend/como proyecto Maven en IntelliJ IDEA - Configurar Java 21 SDK
- Ejecutar
MtgBrainApplication.javadirectamente - Requiere PostgreSQL corriendo (puedes usar solo
docker-compose up postgres)
El frontend corre en modo desarrollo con Vite HMR. El directorio src/ esta montado como volumen Docker, asi que los cambios en el codigo se reflejan automaticamente sin reconstruir el contenedor.
- Tema visual: Oscuro personalizado basado en Catppuccin Mocha con acentos violeta/dorado
- Proxy: Las peticiones a
/api/*se redirigen al backend via Vite proxy (evita problemas de CORS)
Para desarrollo sin Docker:
cd frontend
npm install
npm run dev -- --host 0.0.0.0| Servicio | Puerto interno | Puerto externo | Razon del remapeo |
|---|---|---|---|
| PostgreSQL | 5432 | 5433 | Evitar conflicto con otros PostgreSQL |
| Backend | 8080 | 8082 | Evitar conflicto con otros servicios en 8080 |
| Frontend | 3000 | 3000 | Sin conflicto |
| Ollama | 11434 | 11434 | Sin conflicto |
# Verificar que PostgreSQL esta healthy
docker-compose ps
docker-compose logs postgresSi la BD no inicia, puede ser un conflicto de puertos:
# Ver si algo usa el puerto 5433
netstat -an | findstr 5433 # Windows
netstat -an | grep 5433 # Linux/Mac- Asegurate de tener conexion a internet
- La descarga es de ~300 MB, puede tardar varios minutos
- Si falla a mitad, simplemente vuelve a ejecutar
POST /api/cards/sync
- Las imagenes se sirven directamente desde Scryfall (CDN externo)
- Verifica tu conexion a internet
- Las cartas de doble cara (DFC/transform/split) ya estan soportadas
- Verifica que Ollama esta corriendo:
docker-compose ps ollama - Verifica que el modelo existe:
docker exec mtg-brain-ollama ollama list - Verifica que
OLLAMA_ENABLEDes"true"en docker-compose.yml - Revisa los logs:
docker-compose logs backend | grep -i ollama
Esto puede ser un problema de cache del navegador:
# Forzar reconstruccion del frontend
docker-compose up --build --force-recreate frontendLuego haz hard-refresh en el navegador: Ctrl + Shift + R
Es normal que aparezcan warnings para tokens y cartas de sets especiales. Estos no son errores -- simplemente indica cartas del CSV que no se encontraron en la BD de Scryfall. Las cartas normales se importan correctamente.
- Setup del proyecto (Docker Compose, schema PostgreSQL)
- Sincronizacion con Scryfall API (bulk-data streaming, ~280k cartas)
- Soporte para cartas de doble cara (DFC, transform, split, adventure)
- Etiquetado funcional automatico (regex sobre oracle text)
- Importar coleccion desde Manabox (CSV)
- Busqueda avanzada de coleccion con filtros
- Importar mazos desde Moxfield (TXT)
- Motor de compatibilidad (ownership + sustitutos)
- Filtrado por legalidad de formato y color identity (Commander)
- Deteccion automatica de comandante
- Estrategia de Roles Funcionales (determinista)
- Integracion con LLM local (Ollama + Qwen2.5-3B)
- Streaming SSE para progreso de analisis LLM
- UI completa (dashboard, coleccion, mazos, analisis)
- Deteccion de partner commanders
- Crear/editar mazos desde la UI
- Historial de analisis
- Comparar multiples mazos
- Exportar resultados de analisis
- Tests unitarios y de integracion
Este proyecto esta bajo la Licencia MIT.
Proyecto personal de aprendizaje. No afiliado con Wizards of the Coast ni Scryfall. Los datos de cartas son propiedad de Wizards of the Coast. Las imagenes se obtienen de Scryfall, que las proporciona bajo sus terminos de uso.