PlayerModel y aprendizaje¶
Diseño completo: design/07-player-model.md. Código: src/musst/learning/.
Qué aprende¶
| Métrica | Definición (éxitos / oportunidades) |
|---|---|
aggression |
envites / turnos con el lance abierto |
bluff_frequency |
envites con mano débil revelada / envites con mano revelada |
risk_tolerance |
aceptaciones con mano débil / aceptaciones con mano revelada |
acceptance_rate, rejection_rate, raise_rate |
respuestas a envites |
ordago_rate |
órdagos / oportunidades |
mus_rate |
mus / turnos de mus |
Cada métrica se guarda por contexto: lance (grande, chica, pares, juego, punto,
mus) × marcador (ahead, behind, close, even). Así el modelo captura "farolea más en
grande cuando va por detrás".
Cómo estima¶
- Tasa Beta con prior (jugador típico) + back-off jerárquico: un contexto con poca muestra se
encoge hacia su padre
(lance, *)y este hacia el global. - Cada estimación lleva su confianza (
n / (n + 6)) y una etiqueta Baja/Media/Alta. Nunca se afirma "miente": "P(farol) estimada 68 % (confianza media, n = 14)".
Memorias¶
| Memoria | Implementación |
|---|---|
| Corto plazo | Observation.history de la jugada actual |
| Sesión | Learner.recent (olvido rápido, factor 0.6 por partida) |
| Largo plazo | ModelStore: memoria, fichero JSON o base de datos (factor 0.98) |
| Perfil | PlayerModel.profile() → pantalla de estadísticas |
Solo se guardan contadores, no transcripciones.
Ciclo post-partida¶
Learner.learn_game(historial público, {asiento: id}):
- Extrae decisiones con su contexto (
learning/extract.py) usando solo eventos públicos y las manos reveladas al final de cada jugada. - Actualiza los modelos largo y reciente.
- Detecta cambios de estrategia: si la divergencia de Jensen-Shannon entre el modelo largo y el reciente supera 0.03 bits, reduce la confianza del modelo largo a la mitad.
- Genera el resumen privado ("Aceptó 8 de 10 envites", "tendencia a farolear en grande", …).
Validación con jugadores sintéticos¶
learning/synthetic.py define SyntheticPlayer(bluff, value_threshold, risk_tolerance,
shift_after). Los tests comprueban que:
- un jugador con
bluff=0.8se distingue de uno conbluff=0.0(diferencia > 0.2, confianza > 0.5); risk_tolerance=0.9y0.1producen perfiles distintos;- un cambio de 0 % a 95 % de faroles se detecta como cambio de estrategia.
Perfil y estilo (ampliación)¶
Además de las tasas anteriores, el modelo mide con las manos enseñadas al final de cada jugada:
| Métrica | Significado |
|---|---|
bet_with_strong |
P(envida al abrir | mano buena, fuerza ≥ 0.65). Bajo = juega de tapado |
bet_with_weak |
P(envida al abrir | mano floja, fuerza < 0.4). Alto = farolero |
accept_with_weak |
P(acepta | mano floja) |
stakes |
Piedras medias envidadas con mano buena, media y floja |
PlayerModel.style() resume todo en una etiqueta con su confianza: farolero, arriesgado,
conservador, de tapado, cauto, equilibrado, imprevisible (cambió de estrategia en la
última partida) o sin datos aún.
Intuir las cartas: el agente de probabilidad pondera cada mundo simulado por P(envite | fuerza) aprendida de ese jugador (interpolando entre lo que hace con mano floja y con mano buena), mezclada con el modelo por defecto según la confianza. Si un jugador cauto envida, sube mucho la probabilidad de que lleve buena mano; si es farolero, bastante menos.
Conteo (CountingAgent): mis cartas y mis descartes (salvo que se rebaraje), y cuántas
cartas pide cada jugador: pedir una carta indica que guardaba buena mano.