Codémon, te elijo a ti!


55

Su amable vecino, Doctor Tree, acaba de darle tres criaturas mágicas llamadas Codémon. Hay un torneo de batalla en la cercana ciudad de Colorville. ¿Eres el mejor, como nunca nadie lo fue?

Visión de conjunto

Este es un torneo de batalla. Cada jugador controla un equipo de tres monstruos, y el objetivo es noquear (matar) al otro equipo. Hay 100 rondas, con puntos otorgados por victorias y empates. ¡El equipo con más puntos gana!

Monstruos

Un Codémon es una pequeña criatura complicada. Hay cinco tipos (elementos) para elegir, tres estadísticas y tres espacios de movimiento en cada uno.

Tipos

Cada Codémon tiene asignado un tipo. Los cinco tipos son Normal, Psíquico, Fuego, Agua y Hierba. Cada uno tiene sus fortalezas y debilidades. El daño se basa en la siguiente tabla:

tabla de tipos

Los números son multiplicadores de daño. Por ejemplo, el Fuego que ataca al Agua tiene un modificador de 0.5 (medio daño), mientras que el Fuego que ataca a la Hierba se duplica (2).

Estadísticas

Cada monstruo tiene tres estadísticas que determinan sus habilidades de batalla. El ataque aumenta el daño que hace. La defensa reduce el daño que se necesita. La velocidad le permite moverse antes que aquellos con menor velocidad.

Cada monstruo tiene un valor inicial de 50 para cada estadística, y un máximo de 100. Cuando crees tus monstruos, podrás asignar 80 puntos de estadísticas adicionales (cada uno). Recuerde que ninguna estadística individual puede superar los 100. Por lo tanto, podría tener una distribución 100/80/50, 90/80/60 o 65/65/100, pero 120/50/60 es ilegal. Cualquier equipo con estadísticas ilegales es descalificado. No está obligado a usar los 80 puntos, pero probablemente no debería ir con el mínimo 50/50/50.

También podría considerar HP una estadística, pero cada Codémon tiene un 100 HP no modificable. Cuando HP cae a cero, no pueden seguir luchando. HP se rellena a 100 antes de cada batalla.

Movimientos

Cada monstruo conoce tres movimientos de batalla. Los tres elegidos deben ser distintos, por lo que no hay Punch / Punch / Punch.

Hay 15 movimientos, tres de cada tipo. Cada tipo tiene un ataque directo, un ataque más débil con un efecto y un movimiento de efecto único.

id  name        type    power   uses    usable  effect

0   Punch       N       20      -       NFWG
1   Heal        N        0      3       NFWG    Heals 50 HP
2   Slow        N       10      5       NFWG    Enemy speed x0.8
3   Pain        P       20      -       PFWG
4   Sleep       P        0      3       PFWG    No enemy action until wake
5   Weaken      P       10      5       PFWG    Enemy Atk x0.8
6   Fireball    F       20      -       NPFW
7   Burn        F        0      3       NPFW    Enemy -10 HP each turn
8   Sharpen     F       10      5       NPFW    Own Atk x1.25
9   Watergun    W       20      -       NPWG    
10  Confuse     W        0      3       NPWG    Enemy may strike itself (10 power)
11  Shield      W       10      5       NPWG    Own Def x1.25
12  Vine        G       20      -       NPFG
13  Poison      G        0      3       NPFG    Enemy -5xTurns HP each turn
14  Sap         G       10      5       NPFG    Enemy Def x0.8

typese refiere al tipo de movimiento. powerEs su sorprendente poder. usesindica cuántas veces se puede usar por batalla ( -es ilimitado). usablemuestra qué tipos puede usar (por ejemplo, Punch no se puede dar a un tipo psíquico, ya que no existe P). effectmuestra qué efectos tienen los movimientos. Hay un 75% de posibilidades de que cada efecto funcione, excepto Sanar, que siempre funciona.

Para los efectos que cambian las estadísticas de un monstruo, los efectos se pueden apilar . Por ejemplo, usar Weaken dos veces puede reducir el ataque de tu oponente a 0,64 de efectividad. Los efectos que no cambian las estadísticas de un monstruo (Dormir, Quemar, etc.) no se acumulan .

Dormir pone al oponente a dormir, con un 60% de probabilidad de despertarse al comienzo de cada turno. Los monstruos dormidos no tomarán ninguna medida.

Quemar daña al oponente 10 HP al final de cada turno cuando está activo . El veneno funciona de manera similar, pero toma una cantidad cada vez mayor en cada turno. En el primer turno, es 5, y gana 5 cada turno a partir de entonces. Entonces, en el cuarto turno, será dañino para 20. Estos son daños planos, no afectados por el tipo de monstruo o sujetos a bonificaciones.

La confusión puede hacer que un monstruo ataque en sí mismo en lugar de hacer lo que se le dijo que hiciera. Este ataque tiene un poder de 10, y tiene un 30% de probabilidad de suceder en un turno dado.

Para ser claros, los efectos duran hasta el final de la batalla (excepto el sueño, como se señaló anteriormente).

Los movimientos también reciben un aumento del 20% en potencia si los usa un monstruo del tipo correspondiente. Por ejemplo, un monstruo Grass que usa Vine es potenciado, mientras que usando Punch no lo es.

Estadísticas secretas

Las estadísticas y el tipo (pero no los movimientos) de cada monstruo son de conocimiento público. Tus oponentes podrán ver contra qué están luchando para elegir la mejor acción. Sin embargo, también hay bonos disponibles que están ocultos.

Específicamente, después de cada dos batallas, se te dará un punto de estadística de "bonificación" por cada monstruo en tu equipo. Se otorgan puntos a todos los monstruos, vivos o muertos, ganadores o perdedores. Puede asignar esto a cualquiera de las tres estadísticas que elija. No puedes apilarlos en un solo monstruo; cada monstruo recibe uno cada vez. Estos puntos son inmunes al límite de 100. Dado que habrá 100 rondas de batalla, esto significa que puede obtener una estadística única de hasta 149 si le asigna todas sus bonificaciones. Una vez más, el oponente solo verá tus estadísticas "base", por lo que cuanto más te acerques al torneo, más se separará su conocimiento de la verdad.

Batalla

La batalla tiene lugar entre equipos de tres, con uno activo en cada equipo a la vez. Al principio, se te mostrará el equipo del oponente y se te pedirá que elijas qué monstruo será tu primer jugador "activo".

Después de eso, los turnos tienen lugar con los siguientes pasos:

  • Interruptor: los cambios obligatorios de monstruos tienen lugar (si corresponde)
  • Elige acción de batalla
  • Interruptor: cualquier cambio de monstruo opcional (elegido como acción de batalla) tiene lugar
  • Comprobación del sueño: posibilidad de despertarse del sueño
  • Ataque 1: si puede, el monstruo más rápido usa su movimiento seleccionado
  • Ataque 2: si puede, el otro monstruo usa su movimiento seleccionado
  • Daño de efecto: aplica daño de quemadura / veneno a los monstruos vivos

"Más rápido" significa el monstruo con la mayor velocidad. Si ambas estadísticas de velocidad son iguales, se elige mediante el lanzamiento de moneda PRNG cada turno.

Al final de cualquier turno donde muera tu monstruo activo, se te pedirá que elijas un nuevo activo. Es posible también optar por cambiar monstruos activos como su movimiento de cualquier turno (siempre que tenga más de una vida). Nuevamente, si cambias como tu movimiento, no harás un movimiento de batalla ese turno.

Los monstruos no se "procesan" cuando están inactivos. Esto significa que no reciben daño por quemaduras / veneno, los contadores de veneno no se acumularán, no se despertarán del sueño, etc. No se eliminan ni cambian los efectos al cambiar . Este no es ese otro juego de lucha de monstruos. Si cambias con el ataque levantado y quemado, todavía estarán allí cuando vuelvas a entrar.

El daño del efecto tiene lugar tanto si matas a tu oponente activo como si no. De esta manera, los miembros de ambos equipos pueden morir en un solo turno.

Cuando un equipo se queda sin monstruos utilizables, pierden. Si ambos equipos se agotan en el mismo turno, es un empate. Si la batalla dura 1000 turnos, es un empate.

La fórmula para determinar el daño es:

floor((effAttack / effDefense) * movePower * typeMultiplier * moveBoost)

effAttacky effDefenseson las estadísticas efectivas para los monstruos. El ataque efectivo se obtiene agregando Attack y Bonus Attack, luego multiplicando (por 0.8 o 1.25) si algún efecto lo altera. Recuerda que estos efectos pueden acumularse.

El daño solo puede ser 0 cuando el modificador de tipo es 0 (Normal <--> Psíquico) o el Poder del movimiento es 0 (Sanar, Quemar, etc.). De lo contrario, el mínimo se aplica a 1.

Torneo

Los torneos duran 100 rondas. En cada ronda, los equipos se barajan y se emparejan aleatoriamente. Si hay un número impar de equipos, las sobras reciben un adiós (se puntúa como empate). Ganar una batalla le gana al equipo 2 puntos, los empates valen 1 y no pierde nada. ¡El equipo con más puntos al final gana!

Si los equipos están empatados, se realizará un torneo con solo los equipos empatados en el primer lugar para determinar el orden de desempate.

Protocolo

El controlador enviará a su programa uno de los cuatro comandos. El primer carácter determina el tipo de comando, con los siguientes datos si es necesario.

Su programa aceptará el comando como argumento y responderá en STDOUT dentro de un segundo . No te quedes con vida escuchando STDIN, no estará allí. Cada comando generará un nuevo proceso.

Puede escribir datos / estado en el disco. Coloque los archivos en una subcarpeta con el mismo nombre que su equipo. No escriba más de 32 kilobytes de datos, o será descalificado. Los datos persistirán entre rondas, pero se borrarán entre torneos.

Comandos

Datos del equipo

Esto se envía una vez al comienzo del torneo para registrar su equipo. Tu respuesta debe ser constante , no diferente para cada torneo.

Consulta:

T

Respuesta:

name|member0|member1|member2

namees una cadena con el nombre de tu equipo. Utilice solo alfanuméricos para facilitar el análisis. memberNes una cadena de miembros, que proporciona los detalles de cada monstruo:

Cadena de miembros:

name:typeid:attack:defense:speed:moveid0:moveid1:moveid2

Nuevamente, 'nombre' es una cadena, esta vez con el nombre de este monstruo. typeides su tipo Los ID de tipo están en el orden que se muestra en el cuadro anterior, con Normal = 0 y Grass = 4.

Los siguientes tres campos son sus estadísticas base. Tenga en cuenta los límites descritos en la sección de estadísticas anterior.

Los últimos tres son los movimientos de tu monstruo. Las ID se muestran en el gráfico de movimiento anterior.

Un ejemplo de respuesta de datos del equipo puede verse así:

DummyTeam|DummyA:0:50:60:70:0:1:2|DummyB:0:50:60:70:0:1:2|DummyC:0:50:60:70:0:1:2

Cualquier equipo que envíe datos basura, mal formateados o ilegales aquí no participará hasta que se solucione.

Elija activo

Esto se envía al comienzo de cada batalla, y cuando un monstruo muere y necesita ser cambiado.

Consulta:

C#battleState

battleStatemuestra el estado de la batalla actual. Ten paciencia conmigo aquí, es feo:

yourTeamState#theirTeamState

Donde se XteamStateve así:

name:activeSlot|member0state|member1state|member2state

activeSlotmuestra qué monstruo está actualmente activo (0-2). Los estados miembros vienen en dos sabores. Si es tu equipo, te da información adicional. Entonces,

Su miembro Xstate:

name:id:attack:defense:speed:hp:typeid:poisonedturns:moveCount0:moveCount1:moveCount2:bonusAttack:bonusDefense:bonusSpeed:effectid:effectid:effectid

Su miembro Xstate:

name:id:attack:defense:speed:hp:typeid:poisonedturns:effectid:effectid:effectid

ides simplemente un identificador entero que puedes usar para hacer un seguimiento de los monstruos si no te gusta usarlo name.

attack:defense:speedson tus estadísticas base .

poisonedturns te dice por cuántos turnos has sido envenenado.

moveCountXle dice cuántos usos le quedan para cada movimiento. Si es 0, no se puede usar. Para movimientos ilimitados, esto será negativo.

bonus(stat) es la cantidad de puntos de bonificación que ha asignado a cada estadística.

effectides una lista de efectos de tamaño variable que se han aplicado a tu monstruo. Habrá no haber un arrastre :de la cadena, si existen efectos activos presentes o no. Si hay efectos apilados, se mostrarán como efectos múltiples en la lista.

Los identificadores de efecto son:

0  NONE           (should not appear, internal use)
1  POISON
2  CONFUSION 
3  BURN 
4  SLEEP 
5  HEAL           (should not appear, internal use)
6  ATTACK_UP
7  ATTACK_DOWN
8  DEFENSE_UP
9  DEFENSE_DOWN
10 SPEED_DOWN

Respuesta:

memberSlot

La única respuesta requerida es un solo número 0,1,2, que indica qué miembro desea que esté activo. Este debe ser un miembro capaz de luchar. No envíe de vuelta 1si el miembro 1 está muerto.

Acción de batalla

Cada turno, debes decidir qué hacer.

Consulta:

A#battleState

El battleStateaquí es exactamente como se describe anteriormente.

Respuesta:

Para usar un movimiento, devuelva la ranura en la que se encuentra el movimiento. Por ejemplo, si asigné Punch a la ranura 0, el envío 0realiza Punch.

Para cambiar a otro miembro, envíe la ranura del miembro más diez . Entonces, para cambiar al miembro 2, envíe 12.

Cualquier cosa que no esté en [0,1,2,10,11,12] se considera inválida y no dará lugar a ninguna acción tomada este turno.

Estadísticas de bonificación

Después de cada dos batallas, recibes un punto secreto de bonificación por cada miembro del equipo.

Consulta:

B#yourTeamState

El estado de tu equipo es el mismo que se muestra arriba, no me hagas repetirlo.

Respuesta:

stat0:stat1:stat2

Su respuesta representará qué estadística aumentar para cada miembro del equipo. El ataque es 0, la defensa es 1, la velocidad es 2.

Entonces, para aumentar la velocidad del miembro, el ataque del miembro dos y la defensa del miembro tres, respondería con:

2:0:1

Controlador

El controlador se puede encontrar en BitBucket: https: //Geobits@bitbucket.org/Geobits/codemon.git

Simplemente arroje todos los archivos de clase compilados, presentaciones y players.conf en una carpeta y ejecútelos.

Se llama a la clase principal del controlador Tournament. El uso es:

java Tournament [LOG_LEVEL]

Los niveles de registro de 0-4 dan información creciente. El nivel 0 ejecuta el torneo en silencio y solo da los resultados, mientras que el nivel 3 ofrece comentarios paso a paso. El nivel 4 es la salida de depuración.

Puedes agregar presentaciones al torneo en players.confSimplemente agrega la cadena de línea de comando necesaria para ejecutar el programa, una por línea. Las líneas que comienzan con #son comentarios.

En su publicación, incluya el comando que necesitaré agregar a mi players.confy cualquier paso de compilación (si es necesario).

Se incluye un equipo ficticio compuesto por todos los miembros normales con los tres movimientos normales. Eligen movimientos al azar y tienen estadísticas terribles. Diviértete golpeándolos.

Reglas misceláneas

  • No puede leer ni escribir en ningún recurso externo (excepto en su propia subcarpeta, hasta 32 kB como se indicó anteriormente).

  • Su equipo necesita entrar en el torneo "a ciegas". Eso significa que no puede analizar la fuente de otras personas para descubrir qué hará un equipo / monstruo específico en una situación dada. Usted puede analizar su oponente se mueve / estadísticas y realizar un seguimiento como los progresos del torneo, pero sin codificar esta información en.

  • No interfiera con otros procesos / presentaciones. No invocarlos, usar la reflexión para obtener sus datos, etc. No te metas con mi computadora. Solo no lo intentes. Esto queda a mi discreción. Los infractores pueden ser excluidos de futuras entradas.

  • Los concursantes están limitados a un máximo de dos entradas. Si envía más, solo calificaré los dos primeros. Si desea revocar uno, bórrelo.

  • Las entradas pueden no existir únicamente para apuntalar otras entradas. Además, no puede intentar descalificar indirectamente a otros concursantes (por ejemplo, usando un nombre de equipo de 27 millones de caracteres para jugadores de DQ que intenten escribir esto en el disco). Cada presentación debe jugar para ganar por mérito propio.

  • Su programa puede generar un máximo de un proceso secundario a la vez (descendientes totales, no directos). Tanto el proceso principal como el secundario deben finalizar directamente después de dar salida. De cualquier manera, asegúrese de no pasar el tiempo de espera.

  • El torneo se llevará a cabo en mi computadora con Ubuntu con un procesador Intel i7 3770K.

Resultados

Estos son los resultados de los jugadores actuales. Está muy cerca entre los principales contendientes, y estoy pensando en aumentar el número de rondas hasta 500 (y ajustar el espacio de los puntos de bonificación para que coincida). ¿Alguna objeción, comentario?

------- Final Results -------

158     Happy3Campers
157     LittleKid
71      InsideYourHead
68      HardenedTrio
46      BitterRivals

Resultados completos de juego por juego en Google Drive


6262
Quiero ser el mejor / Como si nunca hubiera habido código / No bloquear es mi prueba / ¡Debug es mi causa! / Viajaré por la LAN / Scripting a lo largo y ancho / Tratando de entender / ¡Por qué mi BIOS se frió! / Codémon, somos tú y yo / Jugando al golf todo el ojo puede ver / Codémon, eres mi mejor amigo / ¡Después de que termine el programa! / Codémon, un idioma tan cierto / No habrá fallas seguras que nos atraviesen / Tú me enseñas y yo te enseñaré / ¡Codémon, tengo que jugar golf a todos!
Kaz Wolfe

1
En lugar de aumentar las rondas a 500, sería bueno si una ronda consistiera en que todos peleen contra todos. Por lo tanto, no más byepara un número desigual de competidores y se aseguraría de que los pares de partidos sean justos y distribuidos de manera uniforme.
foobar

@foobar Quería evitar eso porque escala las batallas en n^2lugar de hacerlo n. Con solo los 7 competidores actuales y 100 rondas, son 2100 batallas (frente a 300 como están y 1500 con 500 rondas). Solo empeora a medida que ingresan más entradas. Podría reducir el número de rondas, pero dudo en hacerlo debido a la variabilidad inherente (con respecto a los estados especialmente), y tener un múltiplo de 50 (para puntos de bonificación) es más fácil.
Geobits

¿Este desafío no requiere una actualización? :)
GholGoth21

@ GholGoth21 Sí, creo que sí. Probablemente no pueda hacerlo hoy, pero tal vez mañana o al día siguiente. Hazme un ping en el chat si no lo actualizas antes del jueves más o menos si lo deseas.
Geobits

Respuestas:


16

Happy 3 campistas - PHP

Un grupo de cobardes a los que les gusta lanzar a la oposición con hechizos debilitantes y verlos pudrirse.

EDITAR : el Sr. Lumpy ha sido severamente castigado y ya no dirá malas palabras


PrácticoPrácticoGrass - atk:50 def:99 spd:81 Confuse Poison Heal

Un castor venenoso sin brazos al que le gusta confundir a las personas con apretones de manos problemáticos.


FlippyFlippyWater - atk:50 def:99 spd:81 Confuse Burn Heal

Un veterano del tablón de anuncios con debilidad por las conversaciones aburridas y las guerras de fuego.


De nuezDe nuezFire - atk:50 def:99 spd:81 Burn Poison Heal

Las armas de destrucción masiva son sus dulces favoritos.


AterronadoAterronadoPhp - lines:500 clarity:05 spd:01 Gather Guess Store

Gracias a su coeficiente intelectual de casi 2 dígitos y su memoria fenomenal, Lumpy puede adivinar los movimientos enemigos. Bueno, mayormente.


Estrategia

La estrategia es envenenar, quemar y confundir a los adversarios lo antes posible.
El sueño no se usó ya que parecía menos poderoso que los 3 hechizos anteriores.
La confusión es mortal a largo plazo, ya que reduce los ataques en un 30% (tanto el daño infligido como el lanzamiento de hechizos), evita que los curanderos se curen a sí mismos y daña a los golpeadores pesados ​​(un monstruo de 50 def / 100 atk se infligirá 20 puntos de daño sobre sí mismo). )

Una vez que un enemigo está completamente pegado, mis campistas simplemente lo ven chisporrotear, pudrirse y pegarse un puñetazo.

La alta defensa y la curación se utilizan para mitigar el daño entrante durante la agonía.

Mientras mis 3 campistas luchan, Lumpy, el ciervo mágico, observa a los enemigos en cada movimiento y, a veces, logra identificarlos. La información se retroalimenta a nuestros luchadores, que hacen todo lo posible para aprovecharla.

Después de la defensa, la velocidad es la estadística más importante para impulsar.
La iniciativa es crucial para aplicar la curación antes de que llegue el próximo golpe.

El ataque no se usa en absoluto.

¿Son los hechizos el arma definitiva?

Los hechizos como veneno, quemadura y confusión confunden con la lógica general de piedra / papel / tijera de otros ataques.

Una vez que un monstruo se ve afectado, continuará perdiendo HP incluso después de que el lanzador de hechizos esté muerto. Es como si el fantasma del taumaturgo siguiera atacándolo.
Además, el veneno se vuelve rápidamente más poderoso que un ataque masivo completamente pulido (más de 50 puntos después de 5 turnos).

La esperanza de vida de un monstruo envenenado y quemado no va más allá de 8 turnos, incluso con 3 curaciones.

Como parecen indicar los robots de Martin , el equilibrio del juego es bastante bueno.
Es básicamente una iniciativa que inclinará el equilibrio entre lanzadores de hechizos puros y atacantes puros.

El código

Invocar con php campers.php

Es un desastre feo, pero, francamente, la interfaz tampoco ayuda.

Ahora que apareció una competencia adecuadamente agresiva, implementé mis movimientos enemigos planeados durante mucho tiempo adivinando.
Analizar los ataques requiere varias deducciones acrobáticas y almacenamiento persistente del estado del último turno, lo que significa una guerra a gran escala con la interfaz del controlador paranoico.
No es nada bonito ni tampoco un conejo de seis patas, pero hace un trabajo adecuado.

<?php

// ============================================================================
// Game
// ============================================================================
class G {
    static $code_type = array ("Normal", "Psychic", "Fire", "Water", "Grass", "?", "self"); 
    static $code_move = array    ("Punch", "Heal", "Slow", "Pain", "Sleep", "Weaken", "Fireball", "Burn", "Sharpen", "Watergun", "Confuse", "Shield", "Vine", "Poison", "Sap", "?", "self", "pass");
    static $move_uses = array (1000,3,5,1000,3,5,1000,3,5,1000,3,5,1000,3,5,   2000,2000);
    static $move_type      = array (0,0,0,1,1,1,2,2,2,3,3,3,4,4,4, 5,5,5);
    static $move_dmg       = array (20,0,10,20,0,10,20,0,10,20,0,10,20,0,10,  20,10,0);
    static $move_forbidden = array (1,1,1,0,0,0,4,4,4,2,2,2,3,3,3);
    static $code_effect = array ("N", "Poison", "Confuse", "Burn", "Sleep", "H", "Sharpen", "Weaken", "Shield", "Sap", "Slow"); 
    static $decode_type, $decode_move, $decode_effect;
    static $damage_multiplier = array (
        array (2, 0, 1, 1, 1, 0),
        array (0, 2, 1, 1, 1, 0),
        array (1, 1,.5, 2,.5, 0),
        array (1, 1,.5,.5, 2, 0),
        array (1, 1, 2,.5,.5, 0),
        array (2, 2, 2, 2, 2,-1),
        array (9, 9, 9, 9, 9, 9, 1));
    static $atk_score = array ("Poison"=> 1002, "Confuse"=>1001, "Burn"=>1000);
    static $status_field = "atk:def:spd:hp:type:Pturns";
    static $all_moves, $strong_moves, $medium_moves, $effect_moves, $possible_moves;

    function init()
    {
        self::$status_field = explode (":", self::$status_field);
        foreach (array ("type", "move", "effect") as $table) self::${"decode_$table"} = array_flip (self::${"code_$table"});
        foreach (self::$code_move as $c=>$m)
        {
            if ($m == "?") break;
            self::$all_moves[] = new Move($m);
            if (self::$move_uses[$c] >  5) self::$strong_moves[] = $m;
            if (self::$move_uses[$c] == 5) self::$medium_moves[] = $m;
            if (self::$move_uses[$c] == 3) self::$effect_moves[] = $m;
            for ($type = 0 ; $type != 5 ; $type++) if ((self::$move_uses[$c] >  5) && (self::$move_forbidden[$c] != $type)) self::$possible_moves[$type][] = $m;
        }
    }

    function __construct ($name, $team)
    {
        $this->turn = 0;
        $this->name = $name;
        $this->team = $team;
        $this->results_pending = false;
    }

    function parse_team ($tpack, $own_team)
    {
        $pack = explode ("|", $tpack);
        list ($name,$active) = explode (":", array_shift($pack));
        if ($own_team)
        {
            $team = $this->team;
        }
        else
        {
            if (!isset($this->enemies[$name])) $this->enemies[$name] = new Team(array (new Monster (), new Monster (), new Monster ()));
            $team = $this->foes = $this->enemies[$name];
        }
        $team->active = $active;
        foreach ($pack as $i=>$mpack) $team->monster[$i]->parse_monster ($own_team, $mpack);
    }

    function choose_active ()
    {
        // detect start of round
        $team = $this->team;
        $foes = $this->foes;
        foreach ($team->monster as $i=>$m) if ($m->hp > 0) $candidate[$i] = $m;
        if (count ($candidate) == 3)
        {
            $this->results_pending = false;
            $this->round++;

            // reinitialize all monsters
            foreach (array($team, $foes) as $t)
            foreach ($t->monster as $m)
                $m->start_round();

            // guess initial opponent
            $opponent = $foes->initial_opponent();
        }
        else
        {
            $this->analyze_last_round();
            $opponent = $foes->active();
        }
        return $this->do_switch ($opponent);
    }

    function choose_attacker ($foe)
    {
        foreach ($this->team->monster as $i=>$m) if ($m->can_attack($foe)) $candidate[$i] = $m;
        if (isset($candidate))
        {
            uasort ($candidate, function ($a,$b) use ($foe) { return ($a->atk_score != $b->atk_score) ? $b->atk_score - $a->atk_score : $b->life_expectancy($foe) - $a->life_expectancy($foe); });
            return key($candidate);
        }
        return -1;
    }

    function do_switch ($foe)
    {
        $replacement = $this->choose_attacker ($foe);
        if ($replacement < 0)
        {
            $candidate =  $this->team->monster;
            uasort ($candidate, function ($a,$b) use ($foe) { return $b->life_expectancy($foe) - $a->life_expectancy($foe); });
            $replacement = key($candidate);
        }

        $this->old_own = $this->team->monster[$replacement];
        $this->old_own->attack = "pass";
        return $replacement;
    }

    function choose_action ()
    {
        $this->analyze_last_round();
        $own = $this->team->active();
        $foe = $this->foes->active();
        $this->old_own = $own;

        if ($own->hp <= $own->max_damage($foe) && $own->can_do ("Heal")) return $own->execute("Heal");
        if ($attack = $own->can_attack($foe)) return $own->execute($attack);
        if ($own->hp <= 50 && $own->can_do ("Heal")) return $own->execute("Heal");

        return 10 + $this->do_switch ($foe);    
    }

    function choose_bonus()
    {
        foreach ($this->team->monster as $m)
        {
            if ($m->spd_b == 0) { $m->spd_b++; $res[] = 2; }
            else                { $m->def_b++; $res[] = 1; }
        }
        return implode (":", $res);
    }

    function parse ($parts)
    {
        self::parse_team ($parts[1], true);
        self::parse_team ($parts[2], false);    
    }

    function analyze_last_round()
    {
        if ($this->results_pending)
        {
            $this->results_pending = false;

            $foes = $this->foes;
            $foe = null;
            foreach ($foes->monster as $m) if ($m->hp != $m->old->hp) $foe = $m;
            if ($foe === null) $foe = $foes->monster[$foes->active];

            $this->old_own->guess_attack($foe);
        }
    }

    function process ($line)
    {
        $parts = explode ("#", $line);
        switch ($parts[0])
        {
        case "T": // register for tournament
            echo "$this->name|$this->team";
            break;
        case "C": // designate active monster
            $this->parse ($parts);
            echo $this->choose_active();
            break;
        case "A": // choose round action
            $this->parse ($parts);
            echo $this->choose_action();

            // save current state
            foreach (array($this->team, $this->foes) as $t)
            foreach ($t->monster as $m)
            {
                unset ($m->old);
                $m->old = clone ($m);
            }
            $this->results_pending = true;
            break;
        case "B": // distribute stat bonus
            echo $this->choose_bonus();
            break;
        }

    }
}
G::init();

// ============================================================================
// Move
// ============================================================================
class Move {
    function __construct ($move)
    {
        $this->register($move);
    }

    function register ($move)
    {
        $this->type = G::$decode_move[$move];
        $this->reinit();
    }

    function reinit()
    {
        $this->uses = G::$move_uses[$this->type];
    }

    function __tostring() { return G::$code_move[$this->type]."($this->uses)"; }
}

// ============================================================================
// Monster
// ============================================================================
class Monster { 
    function __construct ($name="?", $type="?", $atk=100, $def=100, $spd=100, $m0="?", $m1="?", $m2="?")
    {
        $this->name = $name;
        $this->type = G::$decode_type[$type];
        $this->atk  = $atk;
        $this->def  = $def;
        $this->spd  = $spd;
        $this->hp   = 100;
        $this->move = array (new Move($m0), new Move($m1), new Move($m2));
        $this->atk_b = 0;
        $this->def_b = 0;
        $this->spd_b = 0;
        foreach (G::$code_effect as $e) $this->$e = 0;
    }

    function __tostring ()
    {
        return implode (":", array (
            $this->name,
            $this->type,
            $this->atk,
            $this->def,
            $this->spd,
            $this->move[0]->type,
            $this->move[1]->type,
            $this->move[2]->type));
    }

    function start_round()
    {
        foreach ($this->move as $m) $m->reinit();
    }

    function parse_monster ($own_team, $spack)
    {
        $pack = explode (":", $spack);
        $name = array_shift ($pack); // get name
        array_shift ($pack); // skip id
        if ($this->name == "?") $this->name = $name; // get paranoid
        else if ($this->name != $name) die ("expected $this->name, got $name");

        // store updated values
        foreach (G::$status_field as $var) $this->$var = array_shift ($pack);
        if ($own_team)
        {
            foreach ($this->move as $m) $m->new_count = array_shift($pack);
            $pack = array_slice ($pack, 3); // these are maintained internally
        }
        $var = array();
        foreach ($pack as $e) @$var[G::$code_effect[$e]]++; 
        foreach (G::$code_effect as $e) $this->$e = @$var[$e]+0;
    }

    function damage_recieved ($attack, $foe=null)
    {
        if ($attack == "self") $foe = $this;
        $a = G::$decode_move[$attack];
        $type = G::$move_type[$a];
        $dmg = g::$move_dmg[$a];

        if ($dmg == 0) return 0;

        $atk = ($foe ->atk+$foe ->atk_b) * pow (.8, ($foe ->Weaken - $foe ->Sharpen));
        $def = ($this->def+$this->def_b) * pow (.8, ($this->Sap    - $this->Shield ));

        $boost = ($foe->type == $type) ? 1.2 : 1;
        return max (floor ($dmg * $atk / $def * $boost * G::$damage_multiplier[$this->type][$type]), 1);
    }

    function guess_attack_from_effect ($attacks)
    {
        foreach ($attacks as $status) if ($this->$status != $this->old->$status) return $status;
        return "?";
    }

    function guess_attack_from_damage ($foe, $damages)
    {
        $select = array();
        foreach (G::$possible_moves[$foe->type] as $attack)
        {
            $dmg = $this->damage_recieved ($attack, $foe);
            foreach ($damages as $damage) if ($damage != 0 && abs ($dmg/$damage-1) < 0.1) $select[$attack] = 1;
        }
        $res = array();
        foreach ($select as $a=>$x) $res[] = $a;
        return $res;
    }

    function guess_attack ($foe)
    {
        $attempt = G::$decode_move[$this->old->attack];
        $success = ($this->old->attack == "pass");
        foreach ($this->move as $m)
        {
            if ($m->type == $attempt)
            {
                if ($m->new_count == $m->uses-1)
                {
                    $m->uses--;
                    $success = true;
                }
                break;
            }
        }

        $possible = array();
        $attack = $this->guess_attack_from_effect (array("Burn", "Confuse", "Poison", "Sleep", "Slow", "Weaken", "Sap"));
        if ($attack == "?") $attack = $foe->guess_attack_from_effect (array("Sharpen", "Shield"));
        if ($attack == "?")
        {
            $foe_damage = $this->old->hp - $this->hp - (10 * $this->Burn + 5 * $this->Pturns*$this->Poison);
            if ($this->old->attack == "Heal" && $success) $foe_damage += 50;
            $possible_dmg[] = $foe_damage;
            //;!;if ($this->Confuse) $possible_dmg[] = $foe_damage + $this->damage_recieved ("self");
            $possible = $this->guess_attack_from_damage ($foe, $possible_dmg);
            if (count ($possible) == 1) $attack = $possible[0];
        }
        if ($attack == "?")
        {
            $own_damage = $foe->old->hp - $foe->hp 
                        - (10 * $foe->Burn + 5 * $foe->Pturns*$foe->Poison)
                        + $foe->damage_recieved ($this->attack);
            if (abs ($own_damage/50+1) < 0.1) $attack = "Heal";
        }
        if ($attack != "?")
        {
            $type = G::$decode_move[$attack];
            if ($attack != "?")
            {
                foreach ($foe->move as $m) if ($m->type == $type) goto found_old;
                foreach ($foe->move as $m) if ($m->type == 15) { $m->register($attack); goto found_new; }
            }
            found_new:
            found_old:
        }
    }

    function max_damage($foe)
    {
        $dmg = 0;
        foreach ($foe->move as $m) $dmg = max ($dmg, $this->damage_recieved (G::$code_move[$m->type], $foe));
        return $dmg;
    }

    function expected_damage ($foe)
    {
        return $this->max_damage($foe) + 10 * $this->Burn + 5 * ($this->Pturns+1);
    }

    function life_expectancy ($foe)
    {
        $hp = $this->hp;
        $poison = $this->Pturns;
        $heal = $this->can_do ("Heal");
        $dmg = $this->max_damage($foe);
        for ($turn = 0 ; $hp > 0 && $turn < 10; $turn++)
        {
            $hp -= 10 * $this->Burn + 5 * $poison;
            if ($poison > 0) $poison++;
            $hp -= $dmg;
            if ($hp <= 0 && $heal > 0) { $hp+=50; $heal--; }
        }
        return 100 * $turn + $this->hp;
    }

    function can_attack ($foe)
    {
        $attack = false;
        if ($this->hp > 0)
        {
            if      (!$foe->Poison  && $this->can_do ("Poison" )) $attack = "Poison";
            else if (!$foe->Confuse && $this->can_do ("Confuse")) $attack = "Confuse";
            else if (!$foe->Burn    && $this->can_do ("Burn"   )) $attack = "Burn";
        }
        $this->atk_score = ($attack === false) ? 0 : G::$atk_score[$attack];
        return $attack;
    }

    function can_do($move)
    {
        $type = G::$decode_move[$move];
        foreach ($this->move as $m) if ($m->type == $type && $m->uses > 0) return $m->uses;
        return false;
    }

    function execute($move)
    {
        $type = G::$decode_move[$move];
        foreach ($this->move as $i=>$m) if ($m->type == $type) 
        { 
            if ($m->uses > 0)
            {
//;!;               $m->uses--;
                $this->attack = $move;
            }
            else $this->attack = "pass";
            return $i; 
        }
        die ("$this asked to perform $move, available ".implode(",", $this->move));
    }
}

// ============================================================================
// Team
// ============================================================================
class Team {
    function __construct ($members)
    {
        $this->monster = $members;
    }

    function __tostring()
    {
        return implode ("|", $this->monster);
    }

    function active ()
    {
        return $this->monster[$this->active];
    }

    function initial_opponent()
    {
        return $this->monster[0];
    }
}

// ============================================================================
// main
// ============================================================================
$input = $argv[1];

$team_name = "H3C";
$mem_file = "$team_name/memory.txt";
$trc_file = "$team_name/trace.txt";
if (!file_exists($team_name)) mkdir($team_name, 0777, true) or die ("could not create storage directory '$team_name'");
if ($input == "T") array_map('unlink', glob("$team_name/*.txt"));

if (file_exists($mem_file)) $game = unserialize (file_get_contents ($mem_file));
else
{
    $team = new Team (
        array (
            new Monster ("Handy" , "Grass" , 50, 99, 81, "Confuse", "Poison", "Heal"),
            new Monster ("Nutty" , "Fire"  , 50, 99, 81, "Burn"   , "Poison", "Heal"),
            new Monster ("Flippy", "Water" , 50, 99, 81, "Confuse" , "Burn" , "Heal")));
    $game = new G($team_name,$team);
}

$game->process ($input);
file_put_contents ($mem_file, serialize($game));

Resultados

LittleKid sigue siendo amenazante, pero mi trío venció a sus monstruos venenosos por un margen justo.

Los robots de Martin están condenados por su falta de iniciativa, y aumentar su velocidad requeriría reducir el ataque, lo que reduciría su ventaja en la capacidad de daño.

Los nuevos contendientes del planeta JavaScript están a la par con el equipo en uno a uno, pero les va peor frente a otros competidores. De hecho, ayudan a disminuir la puntuación de LittleKid :).

Parece que mis tiernos amigos siguen siendo reyes de la colina, por ahora ...

170             H3C
158             Nodemon
145             LittleKid
55              InsideYourHead
42              HardenedTrio
30              BitterRivals

Y tampoco tengo PHP en el mío. Sin embargo, esperaría que trapees el piso con los Metapods, ya que van a una estrategia lenta y se envenenan hasta la muerte.
Sp3000

... * y * quemado hasta quedar crujiente: D. Ese es el problema con las reglas complicadas: es muy probable que surja una estrategia dominante. Como no hay protección contra estos hechizos, podrían ser el hombre gordo y el niño pequeño de codémon.

Lo compararía más con un juego no transitivo como tijeras, papel, piedra, ya que los efectos tardan un tiempo, un equipo de ataque completo debería poder derribarte :)
Sp3000

2
Sí, esto es lo que imaginé tan pronto como no veo remedio para Poison and Burn. Gracias por hacer realidad mi sueño.
justhalf

1
Ese es el Sr. Lumpy expresando su perplejidad cuando detecta más de 3 ataques diferentes del mismo adversario :). Tengo una versión fija casi completa, pero estoy en medio de alguna otra cosa en este momento, por lo que la solución se publicará en un día más o menos.

7

HardenedTrio, Python 3

Como Geobits fue lo suficientemente amable como para darnos dos presentaciones, pensé en presentar algo tonto para la primera: P

La fiesta son tres Codemon (Metapod1, Metapod2, Metapod3) con las mismas estadísticas y movimientos:

  • 80 ataque, 100 defensa, 50 velocidades
  • Golpear, sanar, endurecer escudo

Todos los puntos de bonificación también se asignan a la defensa.


from collections import namedtuple
import sys

BattleState = namedtuple("BattleState", ["us", "them"])
TeamState = namedtuple("TeamState", ["name", "active", "members"])
MemberState = namedtuple("MemberState", ["name", "id", "attack", "defense", "speed", "hp",
                                         "typeid", "poisonedturns", "otherstats"])

def parse_battle_state(state):
    return BattleState(*map(parse_team_state, state.split("#")))

def parse_team_state(state):
    na, *members = state.split("|")
    name, active = na.split(":")
    return TeamState(name, int(active), list(map(parse_member_state, members)))

def parse_member_state(state):
    name, id_, attack, defense, speed, hp, typeid, poisonedturns, *rest = state.split(":")
    return MemberState(name, int(id_), float(attack), float(defense), float(speed),
                       float(hp), int(typeid), int(poisonedturns), rest)

command = sys.argv[1].strip()

if command.startswith("T"):
    print("HardenedTrio|Metapod1:0:80:100:50:0:1:11|"
          "Metapod2:0:80:100:50:0:1:11|Metapod3:0:80:100:50:0:1:11")

elif command.startswith("C"):
    battle_state = parse_battle_state(command[2:])

    for i, codemon in enumerate(battle_state.us.members):
        if codemon.hp > 0:
            print(i)
            break

elif command.startswith("A"):
    battle_state = parse_battle_state(command[2:])
    current_codemon = battle_state.us.members[battle_state.us.active]

    if current_codemon.hp < 50 and int(current_codemon.otherstats[1]) > 0:
        print(1) # Heal up if low

    elif int(current_codemon.otherstats[2]) > 0:
        print(2) # Harden!

    else:
        print(0) # Punch!

elif command.startswith("B"):
    print("1:1:1")

Corre con

py -3 <filename>

(o con python/ en python3lugar de pydepender de su instalación)



1
@FryAmTheEggman Metapod, HARDEN!
Sp3000

Estoy tratando de leer tu código, pero me confundí int(current_codemon.otherstats[1])>0. Eso devuelve cierto si tiene un efecto de estado? ¿Y solo usa endurecer si tiene dos efectos de estado?
Pato mugido

@MooingDuck Para su Codemon, tiene moveCounts antes que effectids, por lo que está comprobando si todavía puede usar Harden. Me volví perezoso con el análisis, razón por la cual está concentrado allí.
Sp3000

@ Sp3000: ¡Oh! ¡Derecho! ¡JAJAJA!
Pato mugido

6

Dentro de tu cabeza, Ruby

  • Brian : Psychic, Attack: 100, Defense: 50, Speed: 80, Pain, Fireball, Watergun
  • Elemon1 : Psychic, Attack: 100, Defense: 50, Speed: 80, Fireball, Watergun, Vine
  • Elemon2 : Psychic, Attack: 100, Defense: 50, Speed: 80, Fireball, Watergun, Vine
TEAM_SPEC = "InsideYourHead"+
            "|Brian:1:100:50:80:3:6:9"+
            "|Elemon1:1:100:50:80:6:9:12"+
            "|Elemon2:1:100:50:80:6:9:12"

def parse_battle_state request
    request.map do |team_state|
        state = {}
        parts = team_state.split '|'
        state[:active] = parts.shift.split(':')[1].to_i
        state[:monsters] = parts.map do |monster_state|
            monster = {}
            parts = monster_state.split(':')
            monster[:name] = parts[0]
            monster[:hp] = parts[5].to_i
            monster[:type] = parts[6].to_i
            monster
        end
        state
    end
end

request = ARGV[0].split '#'
case request.shift
when 'T'
    puts TEAM_SPEC
when 'C'
    battle_state = parse_battle_state request
    my_state = battle_state[0]
    puts my_state[:monsters].find_index {|monster| monster[:hp] > 0}
when 'A'
    battle_state = parse_battle_state request
    my_state, their_state = *battle_state
    my_monster = my_state[:monsters][my_state[:active]]
    their_monster = their_state[:monsters][their_state[:active]]
    puts [1,0,1,2,0][their_monster[:type]]
when 'B'
    puts '0:0:0'
end

Corre con

ruby InsideYourHead.rb

Esto no funciona muy bien contra el bot de Manu, pero supera a los otros tres. Los nombres de equipo y monstruo son bastante aleatorios ... Podría cambiarlos si encuentro algo mejor

La estrategia es bastante simple: ¡atacar! Los tres monstruos solo tienen movimientos de ataque puro, y eligen su movimiento según el tipo de monstruo del oponente.

Podría experimentar arrojando una Curación más tarde.


1
Jeje esto se vuelve más interesante. Sabía que podía contar contigo para eso, Martin :)

6

LittleKid, Java

Un niño pequeño encontró 3 codémons idénticos y los entrenó. Son muy molestos con sus ataques de curación + veneno. Usar solo codémons de tipo normal elimina la necesidad de emparejarlos contra enemigos específicos, ya que el veneno funciona bien contra todos los tipos.

public class LittleKid {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("Geobits says you can't do this.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String me, them, out = "";
        switch(sections[0]){
            case "T":
                out = "LittleKid";
                out += "|Poisoner:0:80:100:50:0:1:13";
                out += "|Poisoner:0:80:100:50:0:1:13";
                out += "|Poisoner:0:80:100:50:0:1:13";
                break;
            case "B":
                out = "1:1:1";
                break;
            case "C":
                me = sections[1];
                them = sections[2];
                int pick = 0;

                if(!isAlive(me, pick)){
                    for(int i=0;i<3;i++){
                        if(isAlive(me,i))
                            pick = i;
                    }
                }

                out = String.valueOf(pick);
                break;
            case "A":
                me = sections[1];
                them = sections[2];
                int active = getActive(me);
                int enemyActive = getActive(them);
                if (getField(me, HP, active) < 50 && getField(me, MOVE1, active) != 0) {
                    out = "1";
                } else if (getEffectCount(them, POISON, enemyActive, false) < 1 && getField(me, MOVE2, active) != 0) {
                    out = "2";
                } else {
                    out = "0";
                }
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }

    static boolean isAlive(String teamState, int who){
        return getField(teamState, HP, who) > 0;
    }

    static int getActive(String teamState){
        return Integer.parseInt(teamState.split("\\|")[0].split(":")[1]);
    }

    static int getField(String teamState, int field, int who){
        String[] fields = teamState.split("\\|")[who+1].split(":");
        return Integer.parseInt(fields[field]);
    }

    static int getEffectCount(String teamState, int effect, int who, boolean mine){
            String[] fields = teamState.split("\\|")[who+1].split(":");
            int count = 0;
            for(int i=mine?14:8;i<fields.length;i++){
                if(Integer.parseInt(fields[i]) == effect)
                    count++;
            }
            return count;
    }

    final static int ID =       1; 
    final static int ATTACK =   2; 
    final static int DEFENSE =  3; 
    final static int SPEED =    4; 
    final static int HP =       5; 
    final static int TYPE =     6;
    final static int MOVE0 =    8; 
    final static int MOVE1 =    9; 
    final static int MOVE2 =    10;

    final static int POISON =           1;
}

55
" Geobits dice que no puedes hacer esto ": D
Geobits

Parece que el veneno es la verdadera bomba atómica en este juego :)

5

Nodémon - Javascript

Dado que el estado parece ser la estrategia dominante, este equipo se enfoca en la velocidad para obtener estados como veneno y confusión sobre los oponentes primero, y luego se detiene con curación y / o sueño mientras el oponente se consume.

No tengo PHP instalado, así que esto no se ha probado contra los Campers, pero parece ser un competidor decente para LittleKid en mis pruebas (y diezma a Bitter Rivals).

/*jshint node:true*/
'use strict';

var fs = require('fs');

var dataFile = 'Nodemon/data.json';
function getData(callback) {
  fs.readFile(dataFile, 'utf8', function(err, contents) {
    var data = {round: 0};

    if(!err) {
      data = JSON.parse(contents);
    }

    callback(data);
  });
}

function saveData(data, callback) {
  fs.mkdir('Nodemon', function() {    
    fs.writeFile(dataFile, JSON.stringify(data), callback);
  });
}

var effect = {
  poison: '1',
  confusion: '2',
  burn: '3',
  sleep: '4',
  heal: '5',
  attackUp: '6',
  attackDown: '7',
  defenseUp: '8',
  defenseDown: '9',
  speedDown: '10'
};

function parseMemberCommon(args) {
  return {
    name: args[0],
    id: args[1],
    baseAttack: +args[2],
    baseDefense: +args[3],
    baseSpeed: +args[4],
    hp: +args[5],
    typeId: args[6],
    poisonedTurns: +args[7],
    effects: args.slice(8)
  };
}

function parseOwnMember(arg) {
  var args = arg.split(':');

  var ownArgs = args.splice(8, 6);

  var member = parseMemberCommon(args);

  member.moveCount = [
    +ownArgs[0],
    +ownArgs[1],
    +ownArgs[2]
  ];

  member.bonusAttack = +ownArgs[3];
  member.bonusDefense = +ownArgs[3];
  member.bonusSpeed = +ownArgs[3];

  return member;
}

function parseOpponentMember(arg) {
  return parseMemberCommon(arg.split(':'));
}

function parseTeamStateCommon(arg, memberParse) {
  var args = arg.split(':');
  var state = {
    name: args[0],
    members: []
  };
  args = arg.substring(state.name.length + 1).split('|');
  var activeSlot = args[0];
  for(var index = 1; index < args.length; index++) {
    state.members.push(memberParse(args[index]));
  }
  state.activeMember = state.members[activeSlot];
  return state;
}

function parseOwnState(arg) {
  return parseTeamStateCommon(arg, parseOwnMember);
}

function parseOpponentState(arg) {
  return parseTeamStateCommon(arg, parseOpponentMember);
}

function parseBattleState(arg) {
  var args = arg.split('#');
  return {
    own: parseOwnState(args[0]),
    opponent: parseOpponentState(args[1])
  };
}

function teamData() {

  saveData({round:0}, function() {
    console.log('Nodemon|' + 
      'Charasaur:0:50:80:100:10:13:1|' +
      'Bulbtortle:4:50:80:100:10:13:1|' +
      'Squirtmander:1:50:80:100:10:13:4');
  });
}

function getActiveIndex(battleState) {
  for(var index = 0; index < battleState.own.members.length; index++) {
    var member = battleState.own.members[index];
    if(member.hp > 0) {
      return index;
    }
  }
}

function chooseActive(arg) {
  var battleState = parseBattleState(arg);

  getData(function(data) {
    var allFull = true;
    for(var index = 0; index < battleState.opponent.members.length; index++) {
      var member = battleState.opponent.members[index];
      if(!data.maxSpeed || member.baseSpeed > data.maxSpeed) {
        data.maxSpeed = member.baseSpeed;
      }
      if(member.hp < 100) {
        allFull = false;
      }
    }

    if(allFull) {
      data.round++;
    }

    saveData(data, function() {
      console.log(getActiveIndex(battleState));
    });    
  });
}

function useMove(moves, battleState) {
  var fighter = battleState.own.activeMember;

  for(var moveIndex = 0; moveIndex < moves.length; moveIndex++) {
    var move = moves[moveIndex];
    if(fighter.moveCount[move]) {
      return move;
    }

    for(var memberIndex = 0; memberIndex < battleState.own.members.length; memberIndex++) {
      var member = battleState.own.members[memberIndex];

      if(member.hp > 0 && member.moveCount[move] > 0) {
        return 10 + memberIndex;
      }
    }
  }

  return -1;  //do nothing
}

function battleAction(arg) {
  var battleState = parseBattleState(arg);

  var fighter = battleState.own.activeMember;
  var opponent = battleState.opponent.activeMember;

  var attemptedMoves = [];

  if(opponent.effects.indexOf(effect.poison) === -1) {
    attemptedMoves.push(1);
  }

  if(opponent.effects.indexOf(effect.confusion) === -1) {
    attemptedMoves.push(0);
  }

  if(fighter.name === 'Squirtmander') {
    //sleep
    if(opponent.effects.indexOf(effect.sleep) === -1) {
      attemptedMoves.push(2);
    }
  }
  else {
    //heal
    if(fighter.hp <= 60) {
      attemptedMoves.push(2);
    }
  }

  console.log(useMove(attemptedMoves, battleState));
}

function bonusStats(arg) {
  var teamState = parseOwnState(arg);

  getData(function(data) {
    var result = '1:';

    if(data.round % 4 === 0) {
      result += '1:';
    }
    else {
      result += '2:';
    }
    if(teamState.members[2].baseSpeed + teamState.members[2].bonusSpeed > data.maxSpeed + (data.round / 2)) {
      result += '1';
    }
    else {
      result += '2';
    }
    console.log(result);
  });
}

var actions = {
  'T': teamData,
  'C': chooseActive,
  'A': battleAction,
  'B': bonusStats
};

var arg = process.argv[2];
actions[arg[0]](arg.substring(2));

Corre con

node nodemon

PS Disculpas a nodemon .


Esto se está intensificando a una guerra global de scripts del lado del servidor: D

4

Rivales amargos - Java

Un equipo de hierba / fuego / agua al que le gusta cambiarlo.

Greenosaur

Tiene al menos una cobertura neutral para cualquiera. Alta velocidad para compensar la falta de defensa.

Type: Grass
Attack:   80     Vine
Defense:  50     Punch
Speed:   100     Pain

Searizard

Intenta agotar a los enemigos con un ataque bajo. Quemaduras y bolas de fuego después de eso.

Type: Fire
Attack:  100     Fireball
Defense:  50     Burn
Speed:    80     Sap

Escudo contra explosiones

Utiliza Shield para mejorar su defensa ya alta. Cura cuando es necesario.

Type: Water
Attack:   80     Watergun
Defense: 100     Shield
Speed:    50     Heal

Código

Esto también se incluye con el controlador. Este es un equipo competitivo, a diferencia de DummyTeam. El comando necesario para players.confes:

java BitterRivals

public class BitterRivals {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("You're not doing this right. Read the spec and try again.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String me, them, out = "";
        switch(sections[0]){
            case "T":
                out = "BitterRivals";
                out += "|Greenosaur:4:80:50:100:12:0:3";
                out += "|Searizard:2:100:50:80:6:7:14";
                out += "|Blastshield:3:80:100:50:9:11:1";
                break;
            case "B":
                out = "2:0:1";
                break;
            case "C":
                me = sections[1];
                them = sections[2];

                int pick = 0;
                switch(getField(them, TYPE, getActive(them))){
                    case 0:
                    case 1:
                    case 3:
                        pick = 0;
                        break;
                    case 2:
                        pick = 2;
                        break;
                    case 4:
                        pick = 1;
                        break;
                }

                if(!isAlive(me, pick)){
                    for(int i=0;i<3;i++){
                        if(isAlive(me,i))
                            pick = i;
                    }
                }

                out = pick + "";
                break;
            case "A":
                me = sections[1];
                them = sections[2];
                int active = getActive(me);
                int oType = getField(them, TYPE, getActive(them));
                switch(active){
                    case 0:         // Greenosaur
                        switch(oType){
                            case 0:
                            case 4:
                                out = "1";
                                break;
                            case 1:
                                out = "2";
                                break;
                            case 3:
                                out = "0";
                                break;
                            case 2:
                                if(isAlive(me, 2)){
                                    out = "12";
                                } else if(isAlive(me, 1)){
                                    out = "11";
                                } else {
                                    out = "1";
                                }
                                break;
                        }
                        break;
                    case 1:         // Searizard
                        if(oType == 3){
                            if(isAlive(me, 0)){
                                out = "10";
                                break;
                            } else if(isAlive(me, 2)){
                                out = "12";
                                break;
                            }
                            if(getEffectCount(them, BURN, getActive(them), false) < 1 && getField(me, MOVE1, active) > 0){
                                out = "1";
                            } else if(getField(me, MOVE2, active) > 0){
                                out = "2";
                            } else {
                                out = "3";
                            }                           
                        } else {
                            if(getField(them, ATTACK, getActive(them)) < 80){
                                if(getEffectCount(them, DEFENSE_DOWN, getActive(them), false) < 1 && getField(me, MOVE2, active) > 0){
                                    out = "2";
                                    break;
                                } else if(getEffectCount(them, BURN, getActive(them), false) < 1 && getField(me, MOVE1, active) > 0){
                                    out = "1";
                                    break;
                                }
                            }
                            out = "0";
                        }
                        break;
                    case 2:         // Blastshield
                        if(oType == 4){
                            if(isAlive(me, 1)){
                                out = "11";
                                break;
                            } else if(isAlive(me, 0)){
                                out = "10";
                                break;
                            }
                        }
                        if(getField(me, HP, active) < 50 && getField(me, MOVE2, active) > 0){
                            out = "2";
                        } else if(getEffectCount(me, DEFENSE_UP, active, true) < 3 && getField(me, MOVE1, active) > 0){
                            out = "1";
                        } else {
                            out = "0";
                        }
                        break;
                }
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }

    static boolean isAlive(String teamState, int who){
        return getField(teamState, HP, who) > 0;
    }

    static int getActive(String teamState){
        return Integer.parseInt(teamState.split("\\|")[0].split(":")[1]);
    }

    static int getField(String teamState, int field, int who){
        String[] fields = teamState.split("\\|")[who+1].split(":");
        return Integer.parseInt(fields[field]);
    }

    static int getEffectCount(String teamState, int effect, int who, boolean mine){
            String[] fields = teamState.split("\\|")[who+1].split(":");
            int count = 0;
            for(int i=mine?14:8;i<fields.length;i++){
                if(Integer.parseInt(fields[i]) == effect)
                    count++;
            }
            return count;
    }

    final static int ID =       1; 
    final static int ATTACK =   2; 
    final static int DEFENSE =  3; 
    final static int SPEED =    4; 
    final static int HP =       5; 
    final static int TYPE =     6; 
    final static int PTURNS =   7; 
    final static int MOVE0 =    8; 
    final static int MOVE1 =    9; 
    final static int MOVE2 =    10; 
    final static int HA =       11; 
    final static int HD =       12; 
    final static int HS =       13; 

    final static int POISON =           1;
    final static int CONFUSION =        2;
    final static int BURN =             3;
    final static int SLEEP =            4;
    final static int ATTACK_UP =        6;
    final static int ATTACK_DOWN =      7;
    final static int DEFENSE_UP =       8;
    final static int DEFENSE_DOWN =     9;
    final static int SPEED_DOWN =       10;
}

4

Error 310: Demasiados redireccionamientos - C ++

Un equipo altamente entrenado y organizado para contrarrestar los estragos del veneno.

Durante tres semanas, apenas entrené a mis codémons. Forme varios equipos. Y finalmente, estoy listo para enfrentar este desafío. Para responder a todos mis oponentes envenenadores, formé un equipo con codémons muy diferentes, cada uno con un papel específico.


Antídoto(imagen)

Type : Normal - atk:50 def:100 spd:80 - Poison/Burn/Heal

Antídoto ama el veneno. Tanto que no puedo evitar que se apresure a atacar con veneno.


zen(imagen)

Type : Fire - atk:100 def:80 spd:50 - Poison/Vine/Heal

Zen es un codémon muy sorprendente que se adapta a todos los efectos. Prefiere ver a sus enemigos luchar y agotar contra su silencio.


Trifuerza(imagen)

Type : Psychic - atk:88 def:60 spd:82 - Fireball/Watergun/Vine

Triforce es un clásico Pokémon, siempre listo para pelear. Éste usa su fuerza psíquica para controlar los tres elementos e infligir tantos daños como sea posible.


Puedes descargar el equipo aquí:

Linux:

http://dl.free.fr/iHYlmTOQ2

lanzar con ./Error310TMR

Ventanas:

http://dl.free.fr/vCyjtqo2s

lanzar con ./Error310TMR.exe

El código es un proyecto completo de c ++. No se como publicarlo.

$ wc -l src/*
    165 src/BruteForce.cpp
     26 src/BruteForce.h
    349 src/Codemon.cpp
     77 src/Codemon.h
     21 src/Logger.cpp
     35 src/Logger.h
    105 src/NoTimeToExplain.cpp
     27 src/NoTimeToExplain.h
    240 src/Recoverator.cpp
     31 src/Recoverator.h
     26 src/StrManip.cpp
     16 src/StrManip.h
    303 src/Team.cpp
     68 src/Team.h
     88 src/TooManyRedirects.cpp
     24 src/TooManyRedirects.h
     87 src/Unrecoverable.cpp
     27 src/Unrecoverable.h
     59 src/enums.cpp
    119 src/enums.h
     68 src/main.cpp
   1961 total

Pero es muy efectivo:

------- Final Results -------

176     Error310TMR
131     H3C
130     LittleKid
121     Nodemon
58      InsideYourHead
47      HardenedTrio
37      BitterRivals

2

Cuento de hadas

Un equipo intermedio bastante genérico. Basado en tres arquetipos que intentan hacer lo suyo, y cambian si no pueden hacer lo suyo.

Este equipo fue creado antes de que pareciera que el veneno era el nuevo meta, realmente no he estado a la altura de cambiar mi equipo desde que lo hice originalmente, la mayor parte del tiempo lo pasé solo tratando de analizarlo. Todavía no he podido ejecutar el Torneo para probarlo, pero ha sido una semana bastante ocupada. Si este no funciona realmente como parece con mis datos de prueba, le aplicaré más brillo después del trabajo.

#!/bin/perl
use 5.20.0;
use strict;

use constant MINE => 0;
use constant THEIRS => 1;

$_ = $ARGV[0];

if(/^T/){
    say 'FairyTale|Fairy:1:89:90:51:3:4:13|Dragon:2:100:50:80:6:1:8|Assassin:0:70:60:100:0:1:10';

} elsif(/^C#(.*)/){
    my $state = readBattleState($1);
    if($state->[MINE]->{$state->[MINE]->{slot}}{hp}){
        say $state->[MINE]->{slot};
    } elsif($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp}){
        say (($state->[MINE]->{slot}+1)%3);
    } else {
        say (($state->[MINE]->{slot}+2)%3);
    }

} elsif(/^A#(.*)/){
    my $state = readBattleState($1);
    my @actives = (
        $state->[MINE]->{$state->[MINE]->{slot}},
        $state->[THEIRS]->{$state->[THEIRS]->{slot}}
    );
    if($state->[MINE]->{slot} == 0){
        if(!exists($actives[THEIRS]{effects}{4}) && $actives[MINE]{pp}->[1]){
            say 1;
        } elsif(!exists($actives[THEIRS]{effects}{1}) && $actives[MINE]{pp}->[2]) {
            say 2;
        } elsif(!$actives[THEIRS]{type}) {
            if($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp} > 0){
                say (($state->[MINE]->{slot}+1)%3);
            } elsif($state->[MINE]->{($state->[MINE]->{slot}+2)%3}{hp} > 0) {
                say (($state->[MINE]->{slot}+2)%3);
            } else {
                say 0;
            }
        } else {
            say 0;
        }
    } elsif($state->[MINE]->{slot} == 1){
        if(!exists($actives[MINE]{effects}{6}) && $actives[MINE]{pp}->[2]){
            say 2;
        } elsif ($actives[MINE]{hp} > 10 && $actives[MINE]{hp} < 50 && $actives[MINE]{pp}->[1]){
            say 1;
        } else {
            say 0;
        }
    } elsif($state->[MINE]->{slot} == 2){
        if(!exists($actives[MINE]{effects}{6}) && $actives[MINE]{pp}->[2]){
            say 2;
        } elsif ($actives[MINE]{hp} > 10 && $actives[MINE]{hp} < 50 && $actives[MINE]{pp}->[1]){
            say 1;
        } elsif($actives[THEIRS]{type} == 1) {
            if($state->[MINE]->{($state->[MINE]->{slot}+1)%3}{hp} > 0){
                say (($state->[MINE]->{slot}+1)%3);
            } elsif($state->[MINE]->{($state->[MINE]->{slot}+2)%3}{hp} > 0) {
                say (($state->[MINE]->{slot}+2)%3);
            } else {
                say 0;
            }
        } else {
            say 0;
        }
    }

} elsif(/^B#(.*)/){
    my $state = readTeam($1, 1);
    say '1:0:2';
}

sub readBattleState {
    local $_ = $_[0];
    if(/^(.*?)#(.*?)$/){
        my @teams;
        $teams[0] = readTeam($1, 1);
        $teams[1] = readTeam($2, 0);
        return \@teams;
    }
}

sub readTeam {
    my $isMine = $_[1];
    local $_ = $_[0];
    if(/.*?:(?<slot>.*?)\|(.*?)\|(.*?)\|(.*?)$/){
        my %team;
        $team{slot} = $1;
        $team{0} = $isMine ? readYourMember($2) : readTheirMember($2);
        $team{1} = $isMine ? readYourMember($3) : readTheirMember($3);
        $team{2} = $isMine ? readYourMember($4) : readTheirMember($4);
        return \%team;
    }
    return 0;
}

sub readYourMember {
    local $_ = $_[0];
    if(/(?<name>.*?):(?<id>.*?):(?<atk>.*?):(?<def>.*?):(?<spd>.*?):(?<hp>.*?):(?<type>.*?):(?<poison>.*?):(?<move0>.*?):(?<move1>.*?):(?<move2>.*?):(?<batk>.*?):(?<bdef>.*?):(?<bspd>.*?)(?<effects>(?::.*)|$)/){
        my %effects = map { $_ => 1 } readEffects($+{effects});
        my %member = (
            name   => $+{name},
            id     => $+{id},
            hp     => $+{hp},
            atk    => $+{atk}+$+{batk},
            def    => $+{def}+$+{bdef},
            spd    => $+{spd}+$+{bspd},
            type   => $+{type},
            pp     => [$+{move0}, $+{move1}, $+{move2}],
            poistrn=> $+{poison},
            effects=> \%effects
        );
        return \%member;
    }
}

sub readTheirMember {
    local $_ = $_[0];
    if(/(?<name>.*?):(?<id>.*?):(?<atk>.*?):(?<def>.*?):(?<spd>.*?):(?<hp>.*?):(?<type>.*?):(?<poison>.*?)(?<effects>(?::.*)|$)/){
        my %effects = map { $_ => 1 } readEffects($+{effects});
        my %member = (
            name   => $+{name},
            id     => $+{id},
            hp     => $+{hp},
            atk    => $+{atk},
            def    => $+{def},
            spd    => $+{spd},
            type   => $+{type},
            poistrn=> $+{poison},
            effects=> \%effects
        );
        return \%member;
    }
    return 0;
}

sub readEffects {
    local $_ = $_[0];
    my @retval = /:([^:]*)/g;
    if(!@retval){
        @retval = (0);
    }
    return @retval;
}

Corre con

perl fairytale.pl

Este bot es expulsado por tratar de "elegir miembros muertos activos" después de los primeros dados. Nunca pasa la primera batalla que puedo ver.
Geobits

De Verdad? Pensé que había solucionado ese error antes, tal vez esta es una versión anterior de ese código ...
mezzoEmrys

No olvide que cuando se elimina un codemon, sus puntos de curación disminuyen a 0 o menos . Por lo tanto, la if($state->[MINE]->{$state->[MINE]->{slot}}{hp})declaración no funcionará correctamente sin >0.
GholGoth21

Ah, sí, eso lo haría.
mezzoEmrys

0

Equipo ficticio - Java

(CW no competidor)

Este es un equipo ficticio para practicar. Son todos los tipos normales, que eligen al azar entre sus movimientos (Punch, Heal, Slow) cada turno. Diviértete con eso.

public class DummyTeam {

    public static void main(String[] args) {
        if(args.length < 1){
            System.out.println("You need to run this from the tournament. Try to keep up.");
            System.exit(0);
        }

        String[] sections = args[0].split("#");
        String out = "";
        switch(sections[0]){
            // team data
            //      sends back all Normal types with minimum stats and Normal moves (randomized name suffixes to distinguish in tests)
            case "T":
                out = "DummyTeam";
                for(int i=0;i<3;i++)
                    out += "|Dummy"+((char)(Math.random()*26)+65) + i + ":0:50:50:50:0:1:2";
                break;
            // bonus points
            //      shoves them all in defense every time
            case "B":
                out = "1:1:1";
                break;
            // choose active
            //      picks last active if alive, otherwise loops to find first living member
            case "C":
                String[] team = sections[1].split("\\|");
                int current = Integer.parseInt(team[0].split(":")[1]);
                if(Integer.parseInt(team[current+1].split(":")[5]) > 0){
                    out = current + "";
                } else {
                    for(int i=1;i<team.length;i++){
                        if(Integer.parseInt(team[i].split(":")[5]) > 0){
                            out = (i - 1) + "";
                        }
                    }
                }               
                break;
            // choose action
            //      chooses a random move. does not check if it ran out of uses, so wastes turns quite often
            case "A":
                out = ((int)(Math.random()*3)) + "";
                break;
            default:
                out = "Invalid query from controller.";             
        }
        System.out.println(out);
    }


}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.