CommunityProgramación y desarrollogithub.com

cedh-playgroup

Gestiona un grupo de juego cEDH (Commander competitivo) utilizando datos de torneos de TopDeck.gg. Permite añadir jugadores y sus mazos, obtener resultados de torneos, analizar el meta, registrar partidas de práctica, notas y disponibilidad, y formar pods equilibrados para las noches de práctica.

¿Qué es cedh-playgroup?

Esta habilidad está diseñada para gestionar un grupo de juego de cEDH (Commander competitivo) combinando datos de torneos de TopDeck.gg con registros locales. Su objetivo principal es organizar noches de práctica efectivas: permite añadir jugadores con sus mazos y perfiles de TopDeck, sincronizar automáticamente los resultados de torneos recientes, y generar informes de meta con tres perspectivas (local, grandes eventos y global). Además, permite registrar partidas de práctica, notas sobre el metajuego, disponibilidad de los miembros, y construir pods de práctica que enfrenten a los jugadores contra los mazos más importantes del meta actual, manteniendo el equilibrio entre los pods, evitando repeticiones de enfrentamientos y enfocándose en las malas matchup de cada jugador. Los resultados de los pods, informes de meta y convocatorias pueden publicarse directamente en un canal de Discord del grupo. Toda la lógica pesada se ejecuta mediante scripts de Python (solo librería estándar), y el agente se encarga de interpretar la salida y ofrecer consejos accionables.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ejw179/cedh-playgroup/tree/main/cedh-playgroup

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

cEDH Playgroup

Esta habilidad mantiene un registro pequeño y editable manualmente de un grupo de juego cEDH y lo combina con datos de torneos de TopDeck.gg. Tiene cuatro funciones principales, además de publicar sus resultados en el Discord del grupo:

  1. Jugadores: añadir personas mediante su ID de perfil de TopDeck, junto con los mazos que poseen.
  2. Meta: qué se está jugando y qué está ganando, últimos 30 días por defecto.
  3. Entrada: resultados de práctica, mazos actuales, notas del meta, disponibilidad.
  4. Pods: organizar una noche de práctica para que el grupo obtenga enfrentamientos útiles.

Toda la aritmética vive en scripts de Python en scripts/ (solo librería estándar). Tu trabajo es ejecutarlos, leer su salida y convertirla en consejos que el grupo pueda usar. No recalcules estadísticas manualmente. Los scripts manejan los casos límite (nombres de compañeros, empates, muestras pequeñas), y hacerlo dos veces corre el riesgo de obtener dos respuestas diferentes.

Configuración y ubicación de los archivos

  • Scripts: scripts/ junto a este archivo. Ejecútalos como python <directorio-del-skill>/scripts/<nombre>.py ....
  • Datos: ~/cedh-playgroup/ (se puede sobrescribir con la variable de entorno CEDH_PLAYGROUP_DIR). roster.json, games.json, notes.json, availability.json y cache/tournaments.json (la descarga de TopDeck). Son JSON plano, así que está bien mostrarlos al usuario o dejar que los edite a mano.
  • Clave de API de TopDeck: variable de entorno TOPDECK_API_KEY. Es gratuita en https://topdeck.gg/account. Si falta, indícalo y explica cómo configurarla. No le pidas al usuario que pegue la clave en el chat.

La atribución es una condición de la API de TopDeck. Toda respuesta que use datos de torneos termina con: Datos de torneos de TopDeck.gg (https://topdeck.gg). Los scripts lo imprimen. Mantenlo al resumir.

Frescura: sincronizar primero

TopDeck no tiene un endpoint de "resultados para el jugador X". sync.py descarga todos los torneos de EDH completados en una ventana de fechas, y todas las estadísticas de jugadores y meta provienen de ese caché. Antes de un informe de meta, estadísticas de jugadores o pods, verifica qué tan antiguo es el caché (la lista synced en cache/tournaments.json). Si tiene más de un día aproximadamente, o no cubre la ventana que necesitas, sincroniza:

python scripts/sync.py              # últimos 30 días, descargados de a una semana
python scripts/sync.py --days 60    # también necesario para la columna de tendencia "Δ share" del meta
python scripts/sync.py --import saved_response.json   # fuera de línea / pruebas

El endpoint masivo tiene límite de tasa. El script retrocede automáticamente ante HTTP 429, así que déjalo ejecutar. Una sincronización de 30 días puede tomar uno o dos minutos.

1. Jugadores

python scripts/playgroup.py add-player "Eric" --topdeck https://topdeck.gg/profile/<id> --deck kinnan
python scripts/playgroup.py set-decks "Sam" --deck najeela --deck "tymna kraum" --primary najeela
python scripts/playgroup.py set-decks "Lee" --deck "magda" --add      # añadir sin reemplazar
python scripts/playgroup.py set-decks "Jo" --deck glarb --gauntlet-pilot yes
python scripts/playgroup.py remove-player "Max"
python scripts/analyze.py players            # todos; o: players Eric Sam
  • --topdeck acepta un enlace de perfil, ya sea en forma (topdeck.gg/profile/<id> o topdeck.gg/profile/%40handle), un @handle, o un ID simple. Las clasificaciones de torneos solo conocen el ID de jugador en bruto, así que el script abre la página del perfil y lee el ID de su etiqueta meta player-id. Imprime el nombre para mostrar de TopDeck que encontró. Si ese nombre no se parece a la persona que el usuario quiso decir, indícalo. Si la búsqueda falla (sin conexión), almacena lo que se dio y advierte. Vuelve a añadir al jugador más tarde para que sus estadísticas coincidan. Si el usuario da solo un nombre, pide el enlace a su perfil de TopDeck. Coincidir por nombre para mostrar puede recoger a desconocidos con el mismo nombre.
  • Los nombres de comandantes se comparan difusamente con la ortografía de TopDeck ("tymna kraum" → "Tymna the Weaver / Kraum, Ludevic's Opus"). El script imprime la coincidencia que eligió. Si muestra alternativas, confirma con el usuario. playgroup.py resolve <name> verifica un nombre sin guardar nada.
  • Un piloto de gauntlet está dispuesto a jugar uno de los mazos compartidos del grupo en lugar del suyo. Así es como los mazos del meta superior que nadie posee terminan en los pods.
  • Un jugador con cero eventos de TopDeck en la ventana no es un error. Simplemente no ha jugado un evento registrado últimamente. Ofrece sync.py --days 90 para más historial.

Al informar sobre jugadores, comienza con lo que es útil para la práctica: mazo actual, forma reciente (récord y resultados finales), y lo que dice su registro de práctica. No te limites a reimprimir la tabla.

2. Informe de meta

python scripts/analyze.py meta                       # 30 días, eventos de 16+ jugadores, top 25
python scripts/analyze.py meta --days 14 --min-players 32 --top 15
python scripts/analyze.py matchups "kinnan"          # contra quién gana y pierde este mazo
python scripts/analyze.py matchups --player Eric     # lo mismo, pero desde las mesas de un jugador

Columnas: entradas, cuota de meta, Δ cuota respecto a la ventana igual anterior, tasa de victorias, tasa de empates, conversión a top cut y mejor resultado final.

Tres lentes sobre el meta. Elige la que corresponda a la pregunta, o muestra el meta local y el grande lado a lado cuando el usuario solo pregunte "¿qué hay en el meta?":

LenteComandoResponde
Localmeta --localA lo que realmente se enfrentará nuestro grupo: eventos dentro del radio guardado desde casa, cualquier tamaño, últimos 30 días
Grandes eventosmeta --bigLo que gana en torneos grandes: más de 100 jugadores, en cualquier lugar, últimos 90 días (son raros)
GlobalmetaTodo en TopDeck con 16+ jugadores

Establece el área de casa y el umbral "grande" una vez con playgroup.py home --zip 18201 --miles 100 --big-min 101, o usa un --zip Z --miles M único. La vista local usa las coordenadas de los eventos de TopDeck. Los eventos en línea y los eventos sin ubicación no se pueden ubicar, y el informe dice cuántos faltan. Las muestras locales son pequeñas, así que llama a las cuotas "quién aparece" y a las tasas de victoria "pistas". Donde lo local y lo grande no coinciden, esa es la parte útil: un mazo popular localmente pero débil en eventos grandes, o uno que gana eventos grandes pero que nadie cerca de ti juega todavía. pods.py --local o --big hace que "enfrentarse al meta superior" use esa lente.

Leerlo bien importa más que imprimirlo:

  • Las tasas de victoria son números de cuatro jugadores. Alrededor del 25% es el promedio, y menor una vez que se cuentan los empates. El informe imprime el promedio observado de la ventana. Un mazo con 30% es muy fuerte. Nunca leas las tasas de victoria contra el 50%.
  • Las muestras pequeñas mienten. Por debajo de unas 20 entradas, o unas 10 mesas compartidas en matchups, llama al número una pista, no un hallazgo.
  • La popularidad (cuota) y la fuerza (tasa de victorias, conversión) son preguntas diferentes. Señala los mazos que rinden por encima de su cuota, y los mazos populares que convierten mal.
  • --min-players 16 mantiene a los pequeños locales fuera de la vista del meta. Si el usuario pregunta sobre su escena local, bájalo.
  • Incorpora las notes.json del grupo cuando sea relevante ("notaste muchas Magda en la tienda local, y también ha subido un 2% globalmente").

Da forma a la respuesta como: 3–5 conclusiones con viñetas, luego la tabla (recórtala a lo que importe), luego lo que significa para la práctica de este grupo (por ejemplo, "nadie aquí tiene una Najeela, y está en el #3; vale la pena una copia de gauntlet").

3. Entrada: resultados, mazos, notas, disponibilidad

python scripts/playgroup.py log-game --date 2026-11-08 \
    --seat "Eric=Kinnan" --seat "Sam=Najeela" --seat "Lee=Tymna Kraum" --seat "Kim=Yuriko" \
    --winner Sam                     # o --winner draw; añade --no-order si se desconoce el orden de turno
python scripts/playgroup.py note "Últimamente hay muchas Magda en la tienda local" --author Eric --tag meta
python scripts/playgroup.py avail 2026-11-08 Eric Sam Lee Kim Jo      # --replace para sobrescribir
python scripts/playgroup.py gauntlet-deck add "Najeela" --list <url>
python scripts/playgroup.py show games      # roster | games | notes | avail

La gente reporta resultados informalmente ("sam ganó con najeela, yo lee y kim con kinnan tymna yuriko, luego un empate"). Convierte cada partida en una llamada log-game.

El orden de asientos es el orden de turno, e importa: en las mesas de 4 jugadores de TopDeck, el asiento 1 gana aproximadamente un 23% y el asiento 4 aproximadamente un 14%. Enumera los --seat en orden de turno (asiento 1 primero). Si el informe no deja claro el orden de turno, pregunta una vez ("¿quién fue primero?"). Si no lo saben, registra con --no-order. La partida aún cuenta para los récords y enfrentamientos, solo que no para las estadísticas de asiento. Dile al grupo el mejor formato de informe, porque alimenta el perfil de asiento de todos: G1 (orden de turno): Eric Kinnan, Sam Najeela, Lee Tymna/Kraum, Kim Yuriko. Ganador Sam. Si algo que cambia las estadísticas es ambiguo, como quién ganó o qué mazo usaba alguien, pregunta antes de registrar. Después, lee en voz alta lo que se registró en una línea por partida para que los errores se detecten.

Si el usuario pega varias partidas, regístralas todas y luego resume la noche: quién ganó qué, y cualquier racha activa.

4. Pods de práctica

Quién juega esta sesión

La asistencia cambia cada semana, así que decide quién está antes de construir los pods. En orden de preferencia:

  1. El usuario nombra a las personas ("todos menos Jeff", "Eric, Ryan, Saige, Alex").
  2. Una lista ya guardada para esa fecha (playgroup.py show avail).
  3. Pregunta. Si tu cliente tiene una herramienta de preguntas de opción múltiple, lista la plantilla como casillas de verificación, dividiéndola en varias preguntas si hay más nombres de los que permite una pregunta, y pregunta también por la fecha. De lo contrario, pregunta en una línea.

Guarda la respuesta para que los pods, la publicación en Discord y los informes posteriores coincidan:

python scripts/playgroup.py avail 2026-11-08 --all --remove Jeff Saige   # todos excepto
python scripts/playgroup.py avail 2026-11-08 Ishmael                      # añadir un sí tardío
python scripts/playgroup.py avail 2026-11-08 --remove Ryan                # quitar un no tardío
python scripts/pods.py --date 2026-11-08                                  # construir desde la lista guardada
python scripts/pods.py --all --except Jeff                                # único, sin guardar

Si la lista cambia después de publicar los pods, reconstruye y di quién se movió. No reubiques silenciosamente a personas que ya vieron su pod.

python scripts/pods.py --date 2026-11-08               # usa la disponibilidad de esa noche
python scripts/pods.py --players Eric Sam Lee Kim Jo Ana Max
python scripts/pods.py --date 2026-11-08 --focus Eric  # las malas matchup de Eric cuentan 4 veces
python scripts/pods.py --date 2026-11-08 --deck "Sam=Tymna Kraum" --w-repeat 0

El constructor busca disposiciones de asientos y elecciones de mazos y las puntúa según cuatro objetivos, todos activados por defecto. Desactiva un objetivo con su peso a 0, o súbelo para enfatizarlo:

ObjetivoBanderaQué recompensa
Enfrentarse al meta superior--w-metaMazos del meta superior (top-N) en cada mesa (--top-meta 15)
Pods equilibrados--w-balanceFuerza media similar (récord del jugador + tasa de victorias del mazo) por pod
Evitar repeticiones--w-repeatNo reubicar parejas que se sentaron juntas en partidas registradas recientes
Apuntar a matchup débiles--w-matchupSentar a un jugador frente a mazos contra los que ellos pierden (ver más abajo)

¿De quién son las malas matchup? "Trabajar en mis malas matchup" se refiere a la persona, no al mazo. El constructor estima la tasa de victorias de cada jugador contra cada comandante oponente a partir de sus propias mesas de TopDeck y partidas de práctica. Estas son muestras pequeñas, por lo que las reduce hacia el rendimiento general de ese mazo en el mismo emparejamiento. Un piloto que tiene 1/11 contra Rograkh/Thrasios aún cuenta como débil allí, incluso si los jugadores de Kinnan en general les va bien contra eso. Antes de construir pods para alguien que preguntó sobre sus matchup, mira la evidencia tú mismo:

python scripts/analyze.py matchups --player Eric --days 90   # mesas propias de Eric, cualquier tamaño de evento
python scripts/analyze.py matchups kinnan                    # el mazo a nivel general, para contrastar

Cuando los dos no coinciden, dilo claramente ("Kinnan está bien contra Najeela en general, pero tú tienes 0/3 contra la de Sam"). Es un consejo útil: la brecha está en las líneas de juego del jugador, no en el mazo.

Asientos. El constructor también establece el orden de turno de cada pod. Con --seat-mode practice (el predeterminado), cada jugador va en el asiento donde su récord es más débil en relación con lo que ese asiento normalmente gana. Tener 0/6 en el asiento 2 es malo. Tener 2/10 en el asiento 4 es más o menos el promedio. --seat-mode best ensaya sus mejores asientos, por ejemplo antes de un torneo, y random solo baraja. Evidencia de asiento y matchup por jugador:

python scripts/analyze.py profile            # todos; o: profile Eric Ryan

Explicar los pods: este es el punto. Para cada asiento, el script imprime el recuento de evidencia del jugador, lectura del asiento (peor, mejor, o aún sin lectura, con récord y puntos contra el promedio del asiento), los enfrentamientos en esta mesa ("necesita práctica contra ..." y "favorecido contra ...", con su propio récord y la tasa general), y matchup débiles que no están en esta mesa. Convierte eso en un breve "por qué" basado en datos por jugador, por ejemplo:

Asiento 1: Alex con Tayam. El asiento 1 es el peor asiento de Alex (1/10, unos 7 puntos por debajo de lo que el asiento 1 suele ganar), así que lo practica aquí. Además, tiene 0/7 contra Sisay en torneos, y la Sisay de Darison está en esta mesa. Está favorecido contra Ob Nixilis, así que esa es una que debería ganar.

Reglas para esas explicaciones:

  • Cita los récords reales (1/10, 0/7). Son lo que lo hace creíble.
  • Di "muestra pequeña" cuando se base en menos de ~6 partidas. No vendas un 0/2 como algo definitivo.
  • Si un jugador no tiene datos (sin enlace de TopDeck, sin partidas registradas), di que su asiento es un marcador de posición y que los informes de práctica lo completarán. No inventes una razón.
  • Termina con qué datos de práctica adicionales podrían mejorar. Cada partida registrada en orden de turno mejora las lecturas de asiento y matchup.

El tamaño de los pods es de cuatro, con los grupos de tres absorbiendo el resto. Con 5 jugadores, uno se sienta fuera y rota. Si el usuario prefiere jugar un pod de 5, di que es su decisión y sienta a todos. La línea "Sitting out" de la salida nombra a quién rotar.

Presenta cada pod con por qué está construido así, en lenguaje sencillo: qué mazos del meta verán, qué matchup débil practica cada jugador, y cualquier emparejamiento repetido que no se pudo evitar. Las notas del script son material en bruto. Elige la una o dos que importen por pod, y no las pegues todas. Si se asignó un mazo de gauntlet, asegúrate de que el piloto lo sepa.

Skills relacionados