¿Qué está haciendo mi adversario?

En una partida de CodedArena, saber en todo momento qué está haciendo el adversario de vuestro personaje es clave para la victoria.

¿Qué está haciendo mi adversario?

Atención: Contenido visualmente obsoleto. Contiene imágenes y reproducciones animadas de CodedArena 1.0.

Este artículo está en proceso de revisión para adaptarlo a la apariencia visual de CodedArena 2.0.


Una de las variables de los personajes menos utilizada, y en cambio una de las más útiles, es action_name.

¿Qué es action_name, y para qué sirve?

Ahora lo veremos, con algunas de sus posibles aplicaciones.

Saber qué hace otro personaje

La variable action_name, nos permite saber qué acción está realizando en un determinado momento otro personaje, adversario o aliado.

Esta variable contiene una cadena, en concreto el nombre de la acción que se está llevando a cabo.

Por ejemplo, si un adversario está invocando:

return ["cast", "incinerate", target]

cuando consultemos la variable action_name, encontraremos la cadena incinerate.

Un ejemplo de código

Veamos el siguiente ejemplo:

target = None

for mob in mobs:
    if mob.name != self.name:
        target = mob
        break

if target.action_name == "nothing" or len(target.action_name) == 0:
    return "No action!"
else:
    return target.action_name

Si vais a Escaramuzas con vuestro personaje, y probáis este código, pasaran partidas como la que se ve a continuación.

Como habéis visto, nuestro personaje ha estado diciendo constantemente la acción del adversario, ¡si este estaba realizando alguna!

Si un personaje está realizando una acción que no se puede interrumpir, como por ejemplo Move, la variable action_name puede contener la cadena "nothing" o la cadena vacía "".

¿Y qué utilidades puede tener?

¡Muchas!¡Todo aquello que os permita entender qué hace otro personaje, y reaccionar en consecuencia!

via GIPHY

Pongamos algunos ejemplos, y vamos viéndolos uno a uno.

Interrumpir un hechizo

Hay hechizos que, con una probabilidad u otra, pueden llegar a interrumpir, cegar y/o silenciar a nuestro adversario, como es el caso de Holy Splash, Blind o Interrupt.

Y también sabemos que hay hechizos con los que pueden atacar a nuestro personaje que son más dañinos que otros.

Así pues, si tuvieseis que utilizar un hechizo de interrupción, ¿lo harías en cualquier momento, o lo reservaríais para interrumpir un hechizo determinado del adversario?

He aquí una de las posibilidades de utilizar action_name y saber qué hace nuestro adversario.

Veamos un ejemplo:

target = None

for mob in mobs:
    if mob.name != self.name:
        target = mob
        break

x = "none"
y = "none"

if self.x > target.x:
    x = "left"
elif self.x < target.x:
    x = "right"
if self.y > target.y:
    y = "up"
elif self.y < target.y:
    y = "down"

if self.max_distance_of("holy splash") < self.distance_to(target):
    return ["move", x, y]

if target.action_name == "nothing" or len(target.action_name) == 0:
    return "No action!"
else:
    if target.action_name == "incinerate":
        if self.is_ready("holy splash"):
            return ["cast", "holy splash", target]
    else:
        return target.action_name

Nuestro código ha cambiado un poco, pero no mucho. Hemos añadido algo de código para poner a nuestro personaje siempre a distancia de lanzamiento de Holy Splash, y por otro lado si nuestro personaje detecta que el adversario está invocando Incinerate, contraatacará invocando Holy Splash, con la esperanza de cegar al adversario (¡tra cosa es que ocurra!).

Como podéis ver en la siguiente animación, cada vez que nuestro adversario trata de invocar Incinerate, nuestro personaje ya no lo dice, sino que invoca Holy Splash:

Saber qué tipo de adversario tienes delante

Otra de las posibilidades son saber qué tipo de adversario tienes delante, cómo lucha, qué tipo de hechizos lanza, y actuar en consecuencia.

¿Es un mago cuerpo a cuerpo, a distancia, de DoTs (Damage over Time) como Incenerate o Corruption?

Si sabemos qué hace el adversario, podemos establecer una estrategia u otra para nuestro personaje.

¡Veamos un ejemplo! (es largo, pero ten paciencia, ¡es muy útil!)

# Inicializamos variables
target = None
ranged_spells = ["incinerate", "corruption"]
melee_spells = ["slam", "rend"]

# Creamos las variables de la memoria del personaje que necesitamos para contar
if "last_spell" not in self.memory:
    self.memory["last_spell"] = None
    self.memory["ranged_spells_count"] = 0
    self.memory["melee_spells_count"] = 0
    self.memory["other_spells_count"] = 0

for mob in mobs:
    if mob.name != self.name:
        target = mob
        break

if target.action_name == "nothing" or len(target.action_name) == 0:
    self.memory["last_spell"] = None
    return "No action!"
else:
    # Como el adversario está invocando un hechizo, recuperamos los contadores
    last_spell = self.memory["last_spell"]
    ranged_spells_count = self.memory["ranged_spells_count"]
    melee_spells_count = self.memory["melee_spells_count"]
    other_spells_count = self.memory["other_spells_count"]
       
    # Miramos si el adversario ha empezado a invocar un hechizo, o lleva un rato haciéndolo
    if last_spell is None:
        # Como ha empezado a invocar un nuevo hechizo, lo almacenamos para no volver a incrementar los contadores mientras dura la invocación
        self.memory["last_spell"] = target.action_name

        # Comprobamos de qué tipo de hechizo se trata, y aumentamos el correspondiente contador
        if target.action_name in ranged_spells:
            ranged_spells_count = ranged_spells_count + 1
        elif target.action_name in melee_spells:
            melee_spells_count = melee_spells_count + 1
        else:
            other_spells_count = other_spells_count + 1

        # Actualizamos la memoria de nuestro personaje con los nuevos contadores
        self.memory["ranged_spells_count"] = ranged_spells_count
        self.memory["melee_spells_count"] = melee_spells_count
        self.memory["other_spells_count"] = other_spells_count

    # Volcamos los contadores a la consola de Actividad
    self.debug("Ranged: " + str(ranged_spells_count))
    self.debug("Melee: " + str(melee_spells_count))
    self.debug("Other: " + str(other_spells_count))
    
    return target.action_name

Vamos a explicar paso a paso la estrategia.

Contadores en la memoria del personaje

Como podemos ver en el código anterior, hemos utilizado la memoria de nuestro personaje para almacenar el número de veces que se lanza un hechizo de distancia, las veces que se lanza uno cuerpo a cuerpo, y los otros tipos de hechizos, como Speed.

self.memory["last_spell"] = None
self.memory["ranged_spells_count"] = 0
self.memory["melee_spells_count"] = 0
self.memory["other_spells_count"] = 0

Además, tenemos otra variable en la memoria, last_spell, que nos ayudará a saber cuando ha empezado a invocar un hechizo el adversario, y por lo tanto incrementar los contadores, y cuando lleva un rato invocando el mismo hechizo, para no incrementar los contadores, porque ya lo hicimos.

Incrementando los contadores

Si detectamos, gracias a que self.memory["last_spell"] tiene el valor None, que nuestro adversario empieza a lanzar un hechizo, en primer lugar almacenamo su nombre en la memoria:

self.memory["last_spell"] = target.action_name

Así evitaremos en el siguiente ciclo de consulta a nuestro personaje, donde el adversario seguirá invocando el mismo hechizo, volver a incrementar los contadores, gracias al condicional anterior:

if last_spell is None:

Miramos qué tipo de hechizo es, buscándolo en las listas ranged_spells y melee_spells, encrementamos el correspondiente contador:

if target.action_name in ranged_spells:
    ranged_spells_count = ranged_spells_count + 1
elif target.action_name in melee_spells:
    melee_spells_count = melee_spells_count + 1
else:
    other_spells_count = other_spells_count + 1

Actualizando los contadores en la memoria

Cuando tengamos los contadores actualizados, actualizamos la memoria:

self.memory["ranged_spells_count"] = ranged_spells_count
self.memory["melee_spells_count"] = melee_spells_count
self.memory["other_spells_count"] = other_spells_count

Volcando la información en la consola de Actividad

Finalmente, lo que nos queda es volcar los datos que queramos en la consola de Actividad, que a medida que nuestro adversario vaya invocando hechizos obtendremos datos:

Como podéis ver, a medida que nuestro adversario utiliza un hechizo u otro, los contadores que nuestro personaje tiene en memoria se van actualizando, y son volcados a la consola de Actividad mediante la función self.debug().

Muchas más opciones, solo imagínatelas

CodedArena y Python son herramientas con las que ejercitar vuestra creatividad. Una vez entedáis las herramientas, sólo se trata de pensar en fórmulas que os resulten útiles.

Si consideráis que hay una opción interesante que, mezclando la memoria del personaje con datos extraídos de los personajes, o con cualquier otra herramienta que os ofrezca CodedArena, no dudéis en programar y probar, y no tengáis miedo a equivocaros, porque así mejoraréis. Depurad, analizad el flujo del programa, entended qué ha funcionado y qué no, y así conseguiréis estrategias y algoritmos increíbles.

¡Solo tenéis que imaginarlo, y hacerlo posible!

¡Adelante!