Repository navigation
Estadísticas
English · Español
Las estadísticas del mazo (curva de maná, colores, tipos, funciones, probabilidades de robo, precios, legalidad por formato, problemas de construcción, estimación del bracket de Commander) se calculan por completo en Rust. El diálogo solo pinta un valor DeckStats. El core es puro recuento sobre una lista de StatInput; la capa de la app reúne los datos de Scryfall (primero la caché, la red para lo que falte o esté caducado) y se los pasa.
Código fuente: src-tauri/crates/proxy-core/src/stats/ (mod.rs, funciones.rs, mana.rs, prob.rs), src-tauri/src/commands/stats.rs, src/features/stats/ y la regla cuentan en src-tauri/crates/proxy-core/src/model.rs.

flowchart LR
UI["StatsDialog (7 pestañas)"] -->|"llama a deck_stats(cards, kind)"| CMD["commands/stats.rs: calcular"]
CMD --> CU["model::cuentan(cards)"]
CU --> CACHE["caché de Scryfall (SQLite)"]
CACHE -->|"falta / más de 24 h / sin texto de reglas"| NET["/cards/collection, 75 por lote"]
CMD --> IN["Vec<StatInput>"]
IN --> CORE["proxy_core::stats::stats()"]
CORE --> DS["DeckStats"]
DS --> UI
LIB["biblioteca.rs detalles_de"] -->|"calcular_sin_red (solo caché)"| CMD
Regla de frontera (Decisión): el core (proxy-core) no tiene Tauri, ni red, ni base de datos. stats() es una función pura (&[StatInput], Option<DeckKind>) -> DeckStats, lo que la hace trivial de probar. Todo lo impuro (lecturas de caché, HTTP, precio alternativo) está en commands/stats.rs.
model::cuentan(cards) -> (Vec<&DeckCard>, Apartadas) decide qué copias físicas cuentan para el tamaño del mazo, los límites de copias, las estadísticas y la lista exportada del mazo. Las marcas salen de DeckCard.marca (MarcaCarta::{Variante, Opcional, Banquillo}; se ponen en el menú de la vista previa, véase Vista previa en vivo). Todas las cartas marcadas se imprimen igualmente; la marca solo cambia lo que se cuenta.
| Marca | ¿Cuenta? |
|---|---|
| ninguna | sí |
Opcional (opcional) |
no (opcionales) |
Banquillo (banquillo) |
no (banquillo) |
Variante (variante de diseño) |
Si existe sin marca una copia de la misma carta (nombre sin distinguir mayúsculas y recortado): no cuenta ninguna de las variantes. Si todas las copias de ese nombre son variantes: cuenta exactamente una (la primera). El resto va a variantes. |
Apartadas { variantes, opcionales, banquillo } (+ total()) se devuelve y calcular lo copia en DeckStats.setAside (s.set_aside = apartadas), porque el propio stats() solo ve la entrada ya filtrada y deja set_aside en su valor por defecto. La pestaña de resumen muestra un aviso cuando setAside no es cero. El mismo cuentan lo usa proxy_core::decklist::exportar para que la lista exportada coincida con las estadísticas (Exportación). Test: las_marcadas_no_cuentan en model.rs y las_cartas_marcadas_no_cuentan_para_el_mazo en commands/stats.rs.
Puntos de entrada:
-
deck_stats(cards: Vec<DeckCard>, kind: Option<DeckKind>) -> AppResult<DeckStats>(comando de Tauri asíncrono; usa el cliente real de Scryfall). -
calcular_sin_red(db, cards)(visible en el crate): el mismo cálculo conSinRed, unCardApicuyas llamadas fallan todas conNetwork, de modo que solo se usa la caché. La biblioteca de mazos (detalles_dedebiblioteca.rs) lo usa para calcular colores, precio, etc. sin red y deprisa (Biblioteca de mazos). -
calcular(db, api, cards, kind)es genérica sobreCardApipara que los tests inyecten un Scryfall falso.
Pasos de calcular:
-
cuentan(cards). - Para cada carta contada que enlaza con una carta de Scryfall (
id_scryfall: las fuentesScryfallyToken, yCustomcon unscryfallId; las cartas en blanco y las propias sin vincular no lo tienen), la busca en la caché (scryfall::cache::leer). Una carta cacheada está «fresca» si se descargó hace menos de 24 h (PRECIOS_MS) Ykeywords.is_some()(una señal de que la entrada se guardó después de que la app empezara a conservar el texto de reglas; las entradas más antiguas se vuelven a pedir). Los ids caducados o ausentes van a una lista de peticiones; mientras tanto se siguen usando los datos caducados. - Pide la lista por
/cards/collectionen lotes de 75 (POR_LOTE), guardando los resultados de vuelta en la caché. Ante cualquier error (sin conexión) el bucle se detiene con un log de aviso y las estadísticas se calculan con lo que tiene la caché: el diálogo nunca falla por culpa de la red. -
Precio alternativo inglés. Scryfall no da precios a las impresiones que no están en inglés. Para cada carta que no esté en inglés y no tenga ni EUR ni USD (los precios foil cuentan, véase más abajo) y tenga un set conocido, busca la impresión inglesa con el mismo set y número de coleccionista (
cache::leer_impresion(set, number, "en"), fresca dentro de 24 h, si no se pide porIdentifier::SetNumber). Entoncesentrada()usa los precios de la impresión inglesa solo cuando la propia carta no tiene ninguno (testcon_precio_propio_no_se_usa_el_ingles). - Elección del precio (
precios()):eury si noeur_foil,usdy si nousd_foil(algunas impresiones existen solo en foil). - Construye un
StatInputpor cada carta contada que no sea en blanco: a partir de la carta de Scryfall (entrada(): colores de la carta o de su primera cara,texto_completo()como texto de reglas,coste()como coste de maná de la cara frontal, poder/resistencia con la primera cara como alternativa,game_changer, legalidades...). Fichas: la fuenteToken, o una carta vinculada cuya línea de tipo empieza porToken, o una carta propia de una fuente externa con una línea de tipo de ficha. Las cartas sin datos de Scryfall se convierten en unStatInputvacío con soloid,nameeis_token: se cuentan como «desconocidas». -
stats(&entrada, kind); después se rellenaset_aside.
StatInput (no se exporta a TS) contiene una copia: id, name, cmc, mana_cost, colors, color_identity, type_line, oracle_text, keywords, produced_mana, power, toughness, rarity, set, set_name, released_at, price_eur, price_usd, legalities, game_changer, is_token. DeckStats (exportado con #[ts(export)], camelCase) es una estructura grande; los grupos:
| Grupo | Campos |
|---|---|
| Clase y tamaño |
kind, detectedKind, setAside, cards (copias con datos, sin fichas), deckSize (todas las copias que no son ficha, incluidas las desconocidas), lands, nonlands, tokens, unknown, unknownCards, tokenCards
|
| Curva |
averageCmc (sin tierras), averageCmcWithLands, medianCmc, totalManaValue, curve: TramoCurva[8] (cmc 0..7+, byType en el orden de TIPOS_CURVA, permanents, spells) |
| Colores |
colors: ColorMana[6] (W U B R G C: cards, pips, landSources, otherSources), colorIdentity, colorCount (0..5 colores, sin tierras), xSpells
|
| Tipos |
types, supertypes, subtypes (los 30 primeros), keywords (los 30 primeros), creatures { count, averagePower, averageToughness, power[0..7+] }, rarity, sets, oldest, newest
|
| Funciones |
roles: Rol[14] (key, count, cards) |
| Precios |
prices { totalEur, totalUsd, withoutEur, withoutUsd, cards, byType, buckets } (tramos <1, 1-5, 5-20, 20-50, 50+ en EUR) |
| Probabilidad | probability { deckSize, lands, openingLands[0..7], expectedLands, landDrops[1..8], cards, roles } |
| Construcción |
recommendedLands, cheapRampDraw, legality[9 formats], problems, bracket
|
-
Desconocida frente a ficha frente a contada. Una ficha va a
tokenCardsy se excluye de todo lo demás. Una carta que no es ficha y no tiene ni línea de tipo ni cmc esunknown(cuenta paradeckSizey las probabilidades, para nada más). -
Tipo principal (
tipo_principal): mira solo la cara frontal y lo anterior a la raya larga, y toma la PRIMERA coincidencia en este orden de prioridad: Creature, Planeswalker, Battle, Instant, Sorcery, Artifact, Enchantment, Land, y si no «Other». Consecuencias visibles en el código: una «Artifact Creature» es una Creature; una «Artifact Land» se clasifica como Artifact (así quees_tierra, que compruebatipo_principal == "Land", no la cuenta como tierra); una «Land Creature» es una Creature. - Curva excluye las tierras; los costes de 7 o más comparten el último tramo; los permanentes son todo lo que no es Instant ni Sorcery.
-
Pips (
mana.rsleer): cuenta los símbolos de color del coste de la cara frontal; el híbrido{U/B}cuenta para ambos colores, el fyrexiano{G/P}para su color;{X}activa un indicador (xSpells). Las cartas partidas{1}{R} // {1}{U}cuentan ambas mitades. -
Fuentes de maná: a partir de
produced_mana, las tierras van alandSourcesy las que no son tierras aotherSources. La pestaña «Maná» avisa cuando la proporción de pips de un color supera su proporción de fuentes de tierra en más de 10 puntos (AVISO_FUENTES = 0.1ensrc/features/stats/mana.tsx). -
Clase de mazo (
deducir): menos de 50 cartas que no son ficha es Limited; de 90 a 110 cartas donde se repiten como mucho dos nombres que no sean tierras básicas ni de «cualquier número de copias» es Commander; en otro caso, Constructed. El usuario puede sobrescribir la clase detectada en el diálogo (argumentokind);detectedKindsiempre informa de la deducida. -
Tierras recomendadas (
tierras_recomendadas): fórmula al estilo Karsten. Commander:31.42 + 3.13 * avgCmc - 0.28 * cheap; Constructed (60):19.59 + 1.90 * avgCmc - 0.28 * cheap; Limited: el valor de 60 cartas escalado por 40/60.cheap= cartas que no son tierra con cmc <= 2 que son rampa o robo (cheapRampDraw). Redondeado a un decimal. -
Problemas:
copies(más de 1 copia en Commander o 4 en Constructed, excluyendo las tierras básicas y las cartas con texto «any number of cards named» o «up to»,sin_limite_de_copias);size(Commander debe ser exactamente 100, Constructed al menos 60, Limited al menos 40). -
Legalidad: para los 9 formatos de
FORMATOS(standard, pioneer, modern, legacy, vintage, commander, pauper, historic, brawl) una carta es un problema salvo que su estado sealegalorestricted. Un formato es legal solo si hay cartas y ninguna infractora; un mazo vacío no es legal en nada (un_deck_vacio_no_es_legal_en_nada). -
Precios: el precio por nombre se toma de la primera copia; los totales suman cada copia; la lista se ordena por
count * price(EUR, y si no USD). Redondeado a céntimos. -
Bracket (solo Commander,
Bracket): nivel 4 si hay alguna denegación masiva de tierras o más de 3 Game Changers; si no, nivel 3 si hay algún Game Changer, 2 o más cartas de turno extra o más de 3 tutores; si no, 2. No puede detectar combos, y el bracket 1 depende de la temática, así que nunca se devuelve. Los textos de la pestaña ensrc/features/stats/textos.ts(BRACKETS) lo dicen.
14 claves en FUNCIONES: ramp, draw, removal, wipe, counter, tutor, protection, recursion, tokens, anthem, lifegain, mill, extraTurn, landDenial. Una carta puede tener varias. Se deducen mediante expresiones regulares sobre el texto de reglas en inglés (FUENTES, compiladas una vez en un LazyLock), después de que normalizar pase a minúsculas y quite el texto recordatorio entre paréntesis. Es una heurística: orientativa, como una persona clasificando a ojo.
-
ramp: cartas que no son tierra y producen maná (produced_manano vacío) o coinciden con un patrón de rampa (tierra buscada al campo de batalla, jugada de tierra extra, Treasure, reducción de coste). Las tierras nunca son rampa. -
removalywipeusanalguno_ajeno: una coincidencia con el grupo con nombretuyo(«you control») se ignora, así que «exile target creature you control» (un parpadeo) no es eliminación. -
tutor(es_tutor): «search your library for» algo que no sea un tipo de tierra. -
lifegain/milltambién aceptan las palabras claveLifelink/Mill. -
todos_los_patrones_compilanes un test de protección: los patrones que no compilan se descartarían en silencio (compilarusafilter_map), así que mantén ese test en verde al añadir patrones.
Distribución hipergeométrica con log-binomiales (ln_comb). exacta(total, successes, n, k) es P(X = k); al_menos es P(X >= k); por_vistas(total, copies) es P(al menos una copia) tras ver 7, 8, ... 7 + ROBOS (12) cartas; valores redondeados a 4 decimales (redondear). Probabilidades:
-
openingLands[k]: P(exactamente k tierras en las 7 iniciales), k = 0..7.expectedLands = 7 * lands / deckSize. -
landDrops[t]para el turno t = 1..8: P(al menos t tierras en el turno t):play(jugando primero) ve7 + t - 1cartas,drawve7 + t. -
cardsyroles: curvasbySeenpor nombre de carta (ordenadas por nombre) y por función con al menos una carta. La calculadora del diálogo indexabySeenpor turno:indice(turn, onThePlay)= 0 para la mano inicial,turn - 1jugando primero,turnrobando primero. -
deckSizeaquí es cada copia que no es ficha (incluidas las desconocidas), así que las cartas propias sin vincular siguen acortando bien las probabilidades. No se modela ningún mulligan (la interfaz lo dice).
StatsDialog (stats-dialog.tsx) se abre cuando useUi.dialogo.tipo === "estadisticas" (botón de la barra de herramientas de la vista previa, desactivado con un mazo vacío). Al abrirse (y siempre que cambien las cartas del mazo o la clase elegida) llama a deck_stats con la lista de cartas del store del mazo; una bandera vigente descarta las respuestas tardías. Muestra un spinner (y sigue mostrando el resultado anterior mientras se actualiza), un selector de clase de mazo («Automático: », «Commander (100)», «Construido (60)», «Limitado (40)») y un interruptor EUR/USD que solo afecta a la visualización.
| Pestaña | Fichero | Muestra |
|---|---|---|
| Resumen | resumen.tsx |
Cifras (cartas, tierras frente a las recomendadas, coste medio, valor de maná, precio, bracket o probabilidad de tierras iniciales), aviso de setAside, lista de cartas desconocidas, problemas de copias/tamaño, curva de maná (por tipo o permanentes frente a hechizos), colores, fichas usadas |
| Maná | mana.tsx |
Pips frente a fuentes de tierra por color, aviso de pocas fuentes, distribución del número de colores |
| Tipos | tipos.tsx |
Tipos, rareza, criaturas (poder/resistencia medios, histograma de poder), subtipos, palabras clave, sets y rango de fechas |
| Funciones | funciones.tsx |
Recuento por función con listas de cartas desplegables |
| Probabilidades | probabilidades.tsx |
Distribución de tierras de la mano inicial, jugadas de tierra por turno, calculadora de robo (función o carta, jugando primero/robando primero, turno) |
| Precios | precios.tsx |
Total, media, la más cara, sin precio, por tipo, tramos de precio, tabla de cartas ordenada por precio total |
| Legalidad | legalidad.tsx |
Legalidad por formato con infractoras, problemas de construcción, detalles del bracket de Commander |
Las piezas compartidas están en piezas.tsx (Cifra, Bloque, Lista, ListaCartas). Los precios los formatea formatoMoneda(lang, moneda) (textos.ts) y las fechas de salida de tipos.tsx, toLocaleDateString, los dos con localeDe(lang) (src/lib/idiomas.ts), así que siguen el idioma de la interfaz. ListaCartas y la tabla de precios tienen un botón «seleccionar estas cartas en el deck»: seleccionarEnDeck(ids, text) selecciona esas copias en la vista previa, cierra el diálogo y lo anuncia (por eso CartaCuenta/CartaPrecio llevan los ids de las copias). textos.ts contiene los nombres de visualización y los colores (COLORES, TIPOS, RAREZAS, FUNCIONES, CLASES_MAZO, BRACKETS, formatoMoneda) y todas las cadenas pasan por t(); Rust devuelve claves, nunca prosa. Capturas: docs/presentacion/estadisticas-{resumen,funciones,probabilidades,precios}.jpg.
- Core: tests de
stats/mod.rs(cuenta_curva_colores_tipos_y_precio,probabilidades_del_deck,la_clase_de_mazo_y_sus_problemas,el_bracket,las_tierras_de_karsten,el_tipo_principal,un_deck_vacio_no_es_legal_en_nada),funciones.rs(rampa,robo_eliminacion_y_barrido,tutores_y_lo_demas,casos_de_un_deck_real,todos_los_patrones_compilan,copias_sin_limite),prob.rs(la_hipergeometrica,por_vistas_crece,mas_cartas_de_las_que_hay),mana.rs(lee_los_simbolos). - App: tests de
commands/stats.rscon un Scryfall falso (pide_de_una_vez_solo_lo_que_no_esta_fresco,los_precios_viejos_y_la_cache_de_antes_se_vuelven_a_pedir,sin_red_se_cuenta_con_la_cache,mas_de_setenta_y_cinco_cartas_van_en_varios_lotes,una_impresion_sin_precio_usa_el_de_la_inglesa,fichas_propias_y_huecos,las_cartas_marcadas_no_cuentan_para_el_mazo,un_deck_vacio_no_pide_nada). - Frontend:
src/features/stats/__tests__/textos.test.ts.
- Nueva estadística: añade el campo a
DeckStats(con el deriveTS), calcúlalo enstats()con un test, ejecutapnpm bindings, añade la interfaz en la pestaña adecuada y las cadenas (fuente en español ent(), traducciones ensrc/i18n/<código>/de cada idioma). - Nueva categoría de función: añade la clave a
FUNCIONES, una lista de patrones aFUENTES/Patrones, una entradaparesenfunciones()y una entrada enFUNCIONESdetextos.ts. - Nuevo dato necesario de Scryfall: añádelo a
StatInput, rellénalo enentrada()y recuerda que las cartas cacheadas que no lo tengan deben volver a pedirse (véase la señal de frescurakeywords.is_some()).
Use and build · Uso y desarrollo
- User-Guide · Guía de usuario
- Development-Setup · Preparación del entorno de desarrollo
- Testing-and-CI · Tests y CI
- Releases-and-Auto-Updates · Versiones y actualizaciones automáticas
- Conventions · Convenciones
Architecture · Arquitectura
- Architecture · Arquitectura
- IPC-and-Error-Handling · IPC y manejo de errores
- Rust-App-Layer · Capa de aplicación Rust
Data · Datos
- Deck-Model · Modelo del mazo
- Storage-and-Blobs · Almacenamiento y blobs
- Deck-Package-Format · Formato del paquete de mazo
- Deck-Library · Biblioteca de mazos
Getting cards · Conseguir cartas
- Decklist-Parsing · Parseo de la lista del mazo
- Scryfall-Integration · Integración con Scryfall
- External-Sources · Fuentes externas
- Card-Identification-and-OCR · Identificación de cartas y OCR
Layout, images, PDF · Maquetación, imagen y PDF
- Layout-Engine · Motor de maquetación
- Imaging-and-PDF · Imagen y PDF
- Export · Exportación
- Live-Preview · Vista previa en vivo
- Statistics · Estadísticas
Card designer · Diseñador de cartas
- Designer-Overview · Visión general del diseñador
- Designer-Engine · Motor del diseñador
- Designer-Composition · Composición del diseñador
- Frame-Packs · Packs de marcos
- Designer-App-Layer · Capa de aplicación del diseñador
- Designer-UI · Interfaz del diseñador
- Card-Editor-Dialog · Diálogo Editar carta
Game table · Mesa de juego
Frontend · Frontend
- Frontend-Architecture · Arquitectura del frontend
- State-Stores · Stores de estado
- Internationalization · Internacionalización
- Themes-and-Skins · Temas y skins
- Settings-and-Shell · Ajustes y shell
- Tutorials · Tutoriales
- Decklist-Panel · Panel de la lista del mazo
- Add-Card-and-Variants · Añadir carta y variantes
- Set-Browser · Explorador de sets
Reference · Referencia