Skip to main content

treta

IA adversaria online para videojuegos: aprende del jugador, contraataca, finta e improvisa — manteniéndose casi imposible de predecir.

PyPI Python 3.10+ Dependencias: 0 Licencia MIT

Python puro, cero dependencias, determinista por semilla, ~O(|A|²) por turno (apta para 60 Hz).

Registro de experimento: EXP-008 (línea esraderey de microlibrerías zero-dep).

pip install treta

La idea en una frase

Los enemigos de videojuego fallan por dos extremos: los guionizados se vuelven legibles (el jugador los "resuelve") y los aleatorios se vuelven incompetentes. treta resuelve el dilema tratando la impredecibilidad como una cantidad medida y optimizada: el agente entrena un autoadversario —un predictor de su propia conducta, de la misma clase que usaría un jugador hábil— y castiga en su función de valor las acciones que ese observador anticiparía. Explota al rival exactamente hasta donde puede sin volverse legible.

Uso

from treta import Agente

ACCIONES = ["piedra", "papel", "tijera"]
VENCE = {"piedra": "tijera", "papel": "piedra", "tijera": "papel"}

# pago(a_ia, a_jugador) -> float (opcional; si falta, se estima de las recompensas)
def pago(a, j):
    return 0.0 if a == j else (1.0 if VENCE[a] == j else -1.0)

ia = Agente(ACCIONES, pago=pago, semilla=42)

# bucle del juego (movimientos simultáneos o por turnos):
accion = ia.decidir()               # acción del enemigo este turno
ia.observar(accion_del_jugador)     # cierra el turno; aprende online
                                    # (o ia.observar(j, recompensa=r) sin matriz de pagos)

ia.predictibilidad()   # [0,1] cuán legible es la IA para un observador competente
ia.certeza_jugador()   # [0,1] cuán bien modela al jugador
ia.improvisando()      # True durante un gambito
ia.fintando()          # True durante una finta (sembrando o cobrando un cebo)
ia.instantanea()       # telemetría para HUD/depuración

Olvido explícito (0.3.0): para eventos que el juego conoce y la IA no (nueva partida, cambio de mapa, rival sustituido), olvidar() descuenta memoria por canales y con dosis:

ia.olvidar(predictores=0.5,   # fracción de evidencia conductual a olvidar [0, 1]
           pagos=0.7,         # fracción de la matriz de pagos aprendida
           regret=0.5,        # fracción del arrepentimiento acumulado
           recompensas=True,  # medias de recompensa, freno y estancamiento
           historial=True,    # historia reciente (contextos y red)
           confianza=True)    # certeza del jugador, predictibilidad y presión

Las fracciones intermedias conservan las medias y descuentan su peso: el agente no cambia de opinión, pero vuelve a ser persuadible. 1.0 borra (los pagos vuelven al prior; el regret, al uniforme; los n-gramas se vacían). Con los valores por defecto es un no-op exacto, y no puede llamarse con un turno abierto.

Alfabetos separados (0.3.0): si la IA y el jugador no comparten acciones (la IA produce unidades y el jugador elige rutas; un jefe con ataques propios), acciones_jugador separa los conjuntos — decidir() devuelve etiquetas propias, observar() espera las del jugador y pago(a_propia, a_jugador) recibe cada una en su dominio:

ia = Agente(["agresiva", "defensiva"],
            acciones_jugador=["ruta_norte", "ruta_centro", "ruta_sur"],
            semilla=42)

Omitido, ambos lados comparten acciones y el agente es bit-idéntico al de siempre.

Memoria de valores (olvido_pagos, 0.3.0): los pagos aprendidos son medias permanentes — tras cien recompensas de +1, veinte de −1 apenas las mueven (quedan en +0.667). El detector de deriva ya descuenta la evidencia ante un cambio brusco; para entornos que derivan despacio (bajo su umbral), olvido_pagos descuenta esa fracción de la evidencia de pagos con cada observación registrada — una media exponencial de memoria ~1/f: 0.05 recuerda 20 observaciones. El defecto 0.0 no toca nada (bit-idéntico); en el turno en que la alarma del detector dispara, su descuento calibrado sustituye al continuo (no componen). Y ojo con las dosis diminutas (0.02): pueden rendir peor que nada, porque siguen la deriva lo justo para callar la alarma sin sustituir su descuento.

Matriz y recompensas (modo_pago): si pasas pago= y recompensas reales en observar(), el defecto "hibrido" trata la matriz como un prior por celda (prior_matriz pseudo-observaciones) que las recompensas superan al acumularse — si la matriz jura que A es correcta pero el entorno paga B, el agente cruza a B. "matriz" restaura la semántica 0.1.x (la matriz manda; las recompensas solo mueven telemetría y fintas). "aprendido" aprende los valores solo de lo observado: la matriz no pesa como prior, pero cuando un turno no trae recompensa hace de oráculo y esa observación se registra — sin recompensas externas nunca, acabará aprendiendo la matriz celda a celda. Sin recompensas externas, híbrido y matriz son bit-idénticos.

decidir(extra=...) acepta cualquier rasgo observable hasheable (fase del nivel, distancia discretizada, arma equipada). Condiciona los modelos de contexto y, desde 0.2.0, también los valores: cada valor distinto de extra abre un bucket con su propia matriz de pagos estimada y su propio RM+, encogidos hacia los globales con prior_estado pseudo-observaciones (un estado recién visto hereda la conducta global y gana identidad con los datos). max_estados acota la tabla: los estados excedentes caen al global y 0 desactiva el mecanismo. Dos avisos: en modo_pago="matriz" la matriz es la autoridad sobre utilidades y solo el arrepentimiento se condiciona por estado — en híbrido (defecto) y aprendido los buckets condicionan también los pagos — en híbrido con jerarquía estado → global → matriz; en aprendido el escalón final es la media global aprendida, no la matriz —; y extra es un bucket discreto, no un vector de rasgos: discretiza grueso (mapa, fase, banda de economía), no pases valores casi-únicos.

Siete mecanismos, un agente

Mecanismo Qué aporta Base
Modelo del jugador: mezcla bayesiana de expertos n-grama (órdenes 0..k, olvido perezoso) + red neuronal pura (RedMicro, MLP online de entrada dispersa) Predice la siguiente acción del jugador; los n-gramas memorizan patrones exactos, la red generaliza PPM/CTW (Cleary & Witten 1984; Willems 1995), mezcla bayesiana con participación fija
Autoadversario (la innovación de EXP-008) El mismo aparato apuntado a la propia IA. Su acierto define predictibilidad, que entra como castigo en el valor esperado: EV'(a) = EV(a) − λ·presión·p_yo(a). La IA evita literalmente lo que un observador esperaría de ella, en proporción a cuánto la están leyendo Emparentado con Safe Opponent Exploitation (Ganzfried & Sandholm 2012), resuelto aquí como regularizador online barato
Fintas de segunda intención El autoadversario como sistema de puntería: ante un rival ilegible que lee y contraataca, siembra un cebo, verifica por acuerdo que el rival responde a la imagen propia (su jugada prevista es la mejor respuesta a p_yo) y cobra la contra-contra mientras el acuerdo dure. Sangría de siembra acotada, salida al segundo cobro perdido, respiros anti-patrón y enfriamiento exponencial ante sondeos fallidos Enseñanza estratégica (Camerer, Ho & Chong 2002); la "segunda intención" de la esgrima
Regret Matching+ Piso teórico-de-juegos: contra un adversario que contraataca, empuja la política mixta hacia maximin Hart & Mas-Colell 2000; Tammelin 2014 (RM+/CFR+)
Improvisación por novedad Archivo acotado de gambitos (motivos de 2–4 acciones) con descriptor conductual; ante estancamiento o legibilidad alta ejecuta el motivo más novedoso (búsqueda de novedad + calidad, QD-lite) Lehman & Stanley 2011; Mouret & Clune 2015
Detección de deriva en dos canales Page-Hinkley conductual (log-pérdida del modelo del jugador: si el jugador cambia de estilo, olvido de predictores) y, desde 0.3.0, Page-Hinkley de pago (error entre recompensa y estimación de valor, con unidad propia y tope de pico): si el entorno cambia lo que paga sin que el rival cambie de conducta, descuenta la evidencia de valor conservando las medias y readapta en ~10 turnos donde antes tardaba >130 Page 1954
Valores por estado (0.2.0) La misma acción puede ganar en un mapa y perder en otro: pagos estimados y política RM+ por bucket de extra, encogidos al global por peso de datos. Sin extra, el agente es bit-idéntico a 0.1.0 Partial pooling jerárquico (empírico-Bayes); bandit contextual de brazos discretos

La presión modula el castigo: contra un rival estático (que no explota los patrones de la IA) el impuesto de impredecibilidad es mínimo y la explotación es agresiva; contra un rival que lee y contraataca, el castigo, la temperatura y la mezcla con RM+ suben solos.

Banco adversario (reproducible: python ejemplos/duelo.py)

Piedra-papel-tijera de suma cero (azar = pago 0, predicción por azar = 33.3 %), 4000 rondas, semilla 7. El observador externo es una mezcla de n-gramas (órdenes 0–3) que intenta predecir a la IA — el mismo modelo que usaría un jugador metódico.

Rival Pago medio IA (últ. mitad) Acierto del observador sobre la IA
Repetidor (acción fija) +0.868 88.7 %
Ciclador (periodo 4) +0.712 78.4 %
Imitador (copia a la IA) +0.774 79.2 %
Aleatorio uniforme +0.036 35.0 %
Contra-predictor (predice a la IA con sus mismos modelos y contraataca) +0.217 25.5 % (muy bajo el azar)
Cambiante (deriva en t=1200; últ. 400) +0.765 78.0 %

El banco incluye además un duelo con pagos dependientes del estado (la misma acción gana en el estado A y pierde en el B, con jugada rival constante): con valores por estado la IA cobra +0.89..+0.94 donde la versión global queda clavada en ~0.00 (semillas 7/11/23; tests/test_adversarios.py::test_condiciona_valores_por_estado).

Y un duelo de deriva de recompensa (el rival repite siempre la misma acción; lo que cambia es qué acción paga): el canal conductual es ciego ahí por construcción — en 0.2.0 la readaptación tardaba 134..218 turnos con el contador de derivas en cero. El canal de pago la detecta con exactamente una alarma y readapta en 8..13 turnos (semillas 7/11/23; test_detecta_deriva_de_recompensa), sin una sola falsa alarma en los duelos estacionarios, por estado (también con visitas desiguales entre estados) y de matriz-contra-entorno del banco, y a lo sumo una aislada bajo recompensa ruidosa (σ=0.3, 2000 turnos).

Desde 0.3.0 el banco añade un duelo de deriva gradual (la recompensa se desliza bajo el umbral del detector: la media permanente arrastra un sesgo que olvido_pagos=0.05 drena, recortando la pérdida post-cruce de ~0.12 a ~0.08 por turno; test_olvido_continuo_drena_la_deriva_gradual) y un duelo asimétrico (3 doctrinas propias contra 2 rutas del jugador cicladas: +0.72..+0.80 donde la mejor respuesta ciega renta +0.5; test_asimetrico_explota_rutas).

Lectura: explota con fuerza todo patrón (pago cercano a +1 donde lo hay) y queda neutral ante el azar puro. El acierto alto del observador contra rivales estáticos no es un fallo: nadie castiga ahí la legibilidad, así que la presión es ~0 y el agente elige explotación máxima. Contra el único rival que sí lee y contraataca, la IA gana con claridad y su acción cae muy por debajo del azar para el observador: el castigo por auto-predictibilidad la aleja de lo esperado y las fintas cobran, además, la imagen que el rival se forma de ella (robusto en semillas 7/11/23: pago +0.217..+0.239, acierto 23.8..25.5 %). Contra el Cambiante, Page-Hinkley detecta la deriva (1 alarma) y el pago se recupera a +0.765 en 400 rondas.

Garantías y límites (honestos)

  • Garantiza: determinismo por semilla; política válida en todo paso; coste por turno O(k + |A|²) sin asignaciones grandes; con matriz de pagos exacta (modo_pago="matriz", o híbrido sin recompensas externas), RM+ acumula arrepentimientos exactos que empujan la mezcla hacia maximin — la garantía formal de arrepentimiento sublineal aplica a RM+ jugado en solitario, no a la política mixta final (además, los turnos de gambito no actualizan el regret): el respaldo de la no-explotabilidad es el banco empírico.
  • No hace: espacios de acción continuos, percepción (píxeles), planificación a varios turnos ni aprendizaje entre partidas persistido (serializa tú instantanea + estado si lo necesitas). El "castigo por auto-predictibilidad" es un regularizador empírico, no un equilibrio exacto: el banco de arriba es su evidencia.
  • Alfabetos de 2 a 32 acciones por lado (propias y del jugador, compartidos o separados con acciones_jugador). Para juegos con estado rico, discretiza lo observable y pásalo por extra: n-gramas y valores por estado lo condicionan todo salvo la red neuronal interna, que solo ve las últimas jugadas (con muchos estados casi-únicos, los buckets se fragmentan y el encogimiento los devuelve al global).
  • Las fintas asumen rival adversario (aprox. suma cero: su mejor respuesta minimiza tu pago). En juegos sin ese antagonismo la compuerta de margen las deja inertes.

Desarrollo

pip install -e ".[test]"
pytest            # unidades + propiedades (Hypothesis) + banco adversario

Historial de cambios: CHANGELOG.md.

Licencia

MIT © esraderey

Metadata

Release files for treta 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for treta 0.3.0
File Size Uploaded
treta-0.3.0.tar.gz 50.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for treta 0.3.0
File Interpreter ABI Platform
treta-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 81.7 kB

Release files / treta-0.3.0.tar.gz

Download URL treta-0.3.0.tar.gz
Size 50.8 kB
Tags Source
SHA-256 checksum
How to use checksums
76d9cbdd11efb6ae0d14737626598a8462e061aad5180d0c71bb27603e9d8061
BLAKE2b-256 checksum
How to use checksums
c3c9e80144bc4ce7ab3f36f1c9ccd04e755ced4bb91e711bbacf1e3f04ade295
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release files / treta-0.3.0-py3-none-any.whl

Download URL treta-0.3.0-py3-none-any.whl
Size 30.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8dd94e2f649a3f7cd5af084f1384c3d4d344e76a8ee537de9a7e0261dff97cea
BLAKE2b-256 checksum
How to use checksums
e2ec0f431cdc5423bd34e7c2c7b7aef2d528c34af2ad3c6b0c6e78cf3fbbcc46
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page