Codémon, je vous choisis!


55

Votre aimable voisin, Doctor Tree, vient de vous donner trois créatures magiques appelées Codémon. Il y a un tournoi de bataille dans la ville voisine de Colorville. Es-tu le meilleur, comme personne ne l'a jamais été?

Vue d'ensemble

C'est un tournoi de bataille. Chaque joueur contrôle une équipe de trois monstres et l'objectif est d'assommer l'autre équipe. Il y a 100 tours, avec des points attribués pour les victoires et les matchs nuls. L'équipe avec le plus de points gagne!

Monstres

Un codémon est une petite créature compliquée. Vous avez le choix entre cinq types (éléments), trois statistiques et trois cases de déplacement pour chacun.

Les types

Chaque Codémon se voit attribuer un type. Les cinq types sont Normal, Psychique, Feu, Eau et Herbe. Chacun a ses forces et ses faiblesses. Les dommages sont basés sur le tableau suivant:

tableau des types

Les chiffres sont des multiplicateurs de dégâts. Par exemple, l'eau attaquant le feu a un modificateur de 0,5 (demi-dégâts), alors que l'herbe attaquant le feu est doublée (2).

Statistiques

Chaque monstre a trois statistiques qui déterminent ses capacités de combat. L'attaque augmente les dégâts. La défense réduit les dégâts qu’elle subit. La vitesse lui permet de se déplacer avant ceux qui ont une vitesse inférieure.

Chaque monstre a une valeur de départ de 50 pour chaque statistique, et un maximum de 100. Lorsque vous créez vos monstres, vous pourrez attribuer 80 points de statistique supplémentaires (chacun). N'oubliez pas qu'aucune statistique individuelle ne peut dépasser 100. Vous pouvez donc disposer d'une distribution 100/80/50, 90/80/60 ou 65/65/100, mais utiliser une distribution 120/50/60 est illégal. Toute équipe avec des statistiques illégales est disqualifiée. Vous n'êtes pas obligé d'utiliser les 80 points, mais vous ne devriez probablement pas aller avec le minimum 50/50/50.

Vous pouvez également considérer HP comme une statistique, mais chaque Codémon a une valeur 100 HP non modifiable. Lorsque HP tombe à zéro, ils ne peuvent plus continuer à se battre. HP est rempli à 100 avant chaque bataille.

Se déplace

Chaque monstre connaît trois mouvements de combat. Les trois choisis doivent être distincts, donc pas de punch / punch / punch.

Il y a 15 coups, trois de chaque type. Chaque type a une attaque directe, une attaque plus faible avec un effet et un seul effet.

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

typefait référence au type de déménagement. powerc'est son pouvoir de frappe. usesindique combien de fois il peut être utilisé par bataille ( -illimité). usableindique les types par lesquels il peut être utilisé (par exemple, Punch ne peut pas être attribué à un type Psychic, car il n'y en a pas P). effectmontre quels effets les mouvements ont. Il y a 75% de chances que chaque effet fonctionne, sauf que l'effet est soigné, qui fonctionne toujours.

Pour les effets qui changent les statistiques d'un monstre, les effets peuvent être empilés . Par exemple, utiliser Weaken deux fois peut réduire l'attaque de votre adversaire à une efficacité de 0,64. Les effets qui ne changent pas les statistiques d'un monstre (Sleep, Burn, etc.) ne s'empilent pas .

Le sommeil endormit l'adversaire, avec 60% de chances de se réveiller au début de chaque tour. Aucune action ne sera entreprise par les monstres endormis.

Brûlure inflige des dégâts à l'adversaire 10 HP à la fin de chaque tour lorsqu'il est actif . Le poison fonctionne de la même manière, mais prend une quantité croissante à chaque tour. Au premier tour, c'est 5 et il gagne 5 à chaque tour par la suite. Donc, à partir du quatrième tour, cela fera 20 dégâts. Ce sont des dégâts plats, qui ne sont pas affectés par le type du monstre ou qui sont sujets à des bonus.

La confusion peut amener un monstre à s’attaquer lui-même au lieu de faire ce qui lui a été demandé. Cette attaque a une puissance de 10 et a 30% de chances d’arriver à un tour donné.

Pour être clair, les effets durent jusqu'à la fin du combat (sauf le sommeil, comme indiqué ci-dessus).

Les mouvements reçoivent également une augmentation de 20% de leur puissance s'ils sont utilisés par un monstre du type correspondant. Par exemple, un monstre Grass utilisant Vine est boosté, alors que Punch n'est pas utilisé.

Statistiques secrètes

Les statistiques et le type (mais pas les déplacements) de chaque monstre sont de notoriété publique. Vos adversaires seront en mesure de voir ce qu'ils combattent, afin de choisir la meilleure action. Cependant, il y a aussi des bonus disponibles qui sont cachés.

Plus précisément, toutes les deux batailles, vous recevrez un point statistique "bonus" pour chaque monstre de votre équipe. Les points sont attribués à tous les monstres, morts ou en vie, gagnants ou perdants. Vous pouvez l'affecter à l'une des trois statistiques que vous choisissez. Vous ne pouvez pas les empiler sur un seul monstre; chaque monstre en reçoit un à chaque fois. Ces points sont immunisés à la limite de 100. Puisqu'il y aura 100 rounds de combat, cela signifie que vous pouvez obtenir une seule statistique jusqu'à 149 si vous lui attribuez tous vos bonus. Encore une fois, l'adversaire ne verra que vos statistiques "de base", donc plus vous avancez dans le tournoi, plus leurs connaissances s'écartent de la vérité.

Bataille

La bataille se déroule entre des équipes de trois personnes, une active par équipe à la fois. Au début, il vous sera montré l'équipe de l'adversaire et invité à choisir quel monstre sera votre premier joueur "actif".

Après cela, les tournants ont lieu avec les étapes suivantes:

  • Switch: les monstres sont obligatoires (le cas échéant)
  • Choisissez l'action de combat
  • Switch: tous les monstres optionnels (choisis comme action de combat) ont lieu
  • Sleep Check: Chance de se réveiller
  • Attaque 1: Si possible, le monstre le plus rapide utilise le coup choisi
  • Attaque 2: Si possible, l'autre monstre utilise son coup sélectionné
  • Effet des dégâts: Appliquez des dégâts de brûlure / poison aux monstres vivants

"Speedier" signifie le monstre avec la vitesse la plus élevée. Si les deux statistiques de vitesse sont identiques, il est choisi par tour de pièce PRNG à chaque tour.

À la fin de chaque tour où votre monstre actif meurt, il vous sera demandé de choisir un nouvel actif. Tu peux également choisir de changer de monstre actif en tant que mouvement pour n’importe quel tour (à condition d’avoir plus d’un vivant). Encore une fois, si vous changez de mouvement, vous ne ferez pas un mouvement de combat ce tour-ci.

Les monstres ne sont pas "traités" lorsqu'ils sont inactifs. Cela signifie qu'ils ne subissent aucune brûlure / poison, que les compteurs de poison ne s'accumulent pas, qu'ils ne se réveillent plus du sommeil, etc. Aucun effet n'est supprimé ou modifié lors du changement . Ce n'est pas cet autre jeu de combat de monstres. Si vous changez d'attaque avec l'attaque levée et brûlée, ils seront toujours là lorsque vous reviendrez.

Les dégâts sont causés que vous tuiez ou non votre adversaire actif. De cette façon, les membres des deux équipes peuvent mourir en un seul tour.

Lorsqu'une équipe est à court de monstres utilisables, elle perd. Si les deux équipes s'épuisent au même tour, c'est une égalité. Si la bataille dure 1000 tours, c'est une égalité.

La formule pour déterminer les dommages est la suivante:

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

effAttacket effDefensesont les statistiques efficaces pour les monstres. Une attaque efficace est obtenue en ajoutant Attack et Bonus Attack, puis en multipliant (par 0.8 ou 1.25) si des effets l’altèrent. N'oubliez pas que ces effets peuvent s'empiler.

Les dégâts ne peuvent être 0 que lorsque le modificateur de type est 0 (Normal <-> Psychique) ou que la puissance du mouvement est 0 (Guérison, Brûlure, etc.). Sinon, le minimum est appliqué à 1.

Tournoi

Les tournois durent 100 tours. À chaque tour, les équipes sont mélangées et appariées au hasard. S'il y a un nombre impair d'équipes, les restants reçoivent un bye (les scores sont à égalité). Gagner une bataille rapporte à l’équipe 2 points, les égalités valent 1 et les pertes ne sont rien. L'équipe avec le plus de points à la fin gagne!

Si les équipes sont à égalité, un tournoi avec seulement les équipes à égalité pour la première place aura lieu pour déterminer l'ordre de départage.

Protocole

Le contrôleur enverra à votre programme l’une des quatre commandes. Le premier caractère détermine le type de commande, suivi de données si nécessaire.

Votre programme acceptera la commande comme argument et répondra sur STDOUT dans la seconde qui suit. . Ne restez pas en vie en écoutant STDIN, il ne sera pas là. Chaque commande va engendrer un nouveau processus.

Vous pouvez écrire des données / états sur le disque. Placez tous les fichiers dans un sous-dossier portant le même nom que votre équipe. N'écrivez pas plus de 32 kilo-octets de données, sinon vous serez disqualifié. Les données persisteront entre les tours, mais seront effacées entre les tournois.

Les commandes

Données d'équipe

Ceci est envoyé une fois au début du tournoi pour inscrire votre équipe. Votre réponse devrait être constante , pas différente pour chaque tournoi.

Requete:

T

Réponse:

name|member0|member1|member2

nameest une chaîne avec le nom de votre équipe. Veuillez utiliser uniquement des caractères alphanumériques pour faciliter l'analyse.memberNest une chaîne membre, donnant les détails de chaque monstre:

Chaîne membre:

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

Encore une fois, 'name' est une chaîne, cette fois avec le nom de ce monstre. typeidest son type. Les identifiants de types sont dans l'ordre indiqué dans le tableau ci-dessus, avec Normal = 0 et Grass = 4.

Les trois champs suivants sont vos statistiques de base. Gardez à l'esprit les limites décrites dans la section des statistiques ci-dessus.

Les trois derniers sont les mouvements de votre monstre. Les identifiants sont indiqués dans le tableau ci-dessus.

Un exemple de réponse de données d'équipe peut ressembler à ceci:

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

Toute équipe qui renvoie ici des données vides, mal formatées ou illégales ne participera pas tant que cela n’aura pas été corrigé.

Choisissez actif

Ceci est envoyé au début de chaque bataille et lorsqu'un monstre meurt et doit être changé.

Requete:

C#battleState

battleStatemontre l'état de la bataille en cours. Ours avec moi ici, c'est moche:

yourTeamState#theirTeamState

XteamStateressemble:

name:activeSlot|member0state|member1state|member2state

activeSlotmontre quel monstre est actuellement actif (0-2). Les États membres se déclinent en deux versions. Si c'est votre équipe, cela donne des informations supplémentaires. Alors,

Votre membreXstate:

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

Leur membreXstate:

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

idest simplement un identifiant entier que vous pouvez utiliser pour garder une trace des monstres si vous n'aimez pas utiliser name.

attack:defense:speedsont vos statistiques de base .

poisonedturns vous indique le nombre de tours pour lesquels vous avez été empoisonné.

moveCountXindique le nombre d'utilisations qu'il vous reste pour chaque mouvement. Si 0, il ne peut pas être utilisé. Pour des mouvements illimités, ce sera négatif.

bonus(stat) est le nombre de points bonus que vous avez attribués à chaque statistique.

effectidest une liste d'effets de taille variable qui ont été appliqués à votre monstre. Il n'y aura pas de fin :sur la chaîne, qu'il y ait ou non des effets actifs. S'il y a des effets empilés, ils apparaîtront sous forme d'effets multiples dans la liste.

Les identifiants d'effet sont:

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

Réponse:

memberSlot

La seule réponse requise est un nombre unique 0,1,2, indiquant le membre que vous voulez activer. Ce doit être un membre capable de se battre. Ne pas renvoyer 1si le membre 1 est mort.

Action de combat

À chaque tour, vous devez décider quoi faire.

Requete:

A#battleState

Le battleStatevoici exactement comme décrit ci-dessus.

Réponse:

Pour utiliser un déplacement, renvoyez l'emplacement dans lequel se trouve le déplacement. Par exemple, si j'ai affecté la perforation à l'emplacement 0, l'envoi 0effectue la perforation.

Pour passer à un autre membre, envoyez la fente du membre plus dix . Donc, pour passer au membre 2, envoyez 12.

Tout ce qui n'est pas dans [0,1,2,10,11,12] est considéré comme invalide et aucune action ne sera prise ce tour-ci.

Statistiques bonus

Toutes les deux batailles, vous recevez un point secret de bonus pour chaque membre de l'équipe.

Requete:

B#yourTeamState

Votre état d’équipe est le même que celui indiqué ci-dessus, ne me faites pas le répéter.

Réponse:

stat0:stat1:stat2

Votre réponse représentera quelle statistique augmenter pour chaque membre de l'équipe. L'attaque est 0, la défense est 1, la vitesse est 2.

Donc, pour augmenter la vitesse d'un membre, l'attaque du membre deux et la défense du membre trois, vous devez répondre par:

2:0:1

Manette

Le contrôleur peut être trouvé sur BitBucket: https: //Geobits@bitbucket.org/Geobits/codemon.git

Il suffit de lancer tous les fichiers de classe compilés, les soumissions et players.conf dans un dossier et de les exécuter.

La classe principale du contrôleur est appelée Tournament. L'utilisation est:

java Tournament [LOG_LEVEL]

Les niveaux de log de 0 à 4 donnent des informations croissantes. Le niveau 0 exécute le tournoi en silence et ne donne que les résultats, le niveau 3 commentant tour à tour. Le niveau 4 correspond à la sortie de débogage.

Vous pouvez ajouter des soumissions au tournoi en players.confajoutant simplement la chaîne de ligne de commande nécessaire à l'exécution du programme, une par ligne. Les lignes commençant par #sont des commentaires.

Dans votre message, incluez la commande que je devrai ajouter à ma players.confet à toutes les étapes de compilation (si nécessaire).

Une équipe factice est composée de tous les membres normaux ayant effectué les trois coups normaux. Ils choisissent des mouvements au hasard et ont des statistiques terribles. Amusez-vous à les battre.

Règles diverses

  • Vous ne pouvez pas lire ou écrire sur des ressources externes (sauf dans votre propre sous-dossier, jusqu'à 32 Ko comme indiqué ci-dessus).

  • Votre équipe doit participer au tournoi "à l'aveugle". Cela signifie que vous ne pouvez pas analyser la source d'autres personnes pour déterminer ce que fera une équipe / un monstre spécifique dans une situation donnée. Vous pouvez analyser les mouvements / statistiques de votre adversaire et garder une trace de l'avancement du tournoi, mais pas de codage en dur de cette information.

  • Ne pas interférer avec d'autres processus / soumissions. Ne pas les invoquer, utiliser la réflexion pour obtenir leurs données, etc. Ne jouez pas avec mon ordinateur. Juste ne l'essaie pas. Ceci est à ma discrétion. Les contrevenants peuvent être interdits d’entrée ultérieure.

  • Les candidats sont limités à un maximum de deux inscriptions. Si vous soumettez plus, je ne marquerai que les deux premiers. Si vous souhaitez en révoquer un, supprimez-le.

  • Les entrées peuvent ne pas exister uniquement pour soutenir d'autres entrées. En outre, vous ne pouvez pas essayer de disqualifier indirectement d’autres concurrents (par exemple, en utilisant un nom d’équipe de 27 millions de personnages pour les lecteurs DQ qui essaient de l’écrire sur le disque). Chaque soumission doit jouer pour gagner sur son propre mérite.

  • Votre programme peut générer au maximum un processus enfant à la fois (nombre total de descendants, pas de descendants directs). Les processus enfant et principal doivent tous deux être terminés directement après la sortie. Quoi qu'il en soit, assurez-vous de ne pas dépasser le délai d'attente.

  • Le tournoi aura lieu sur mon ordinateur sous Ubuntu avec un processeur Intel i7 3770K.

Résultats

Ce sont les résultats des joueurs actuels. C'est très serré entre les meilleurs prétendants, et je pense augmenter le nombre de tours à 500 (et ajuster l'espacement des points bonus pour correspondre). Des objections, des commentaires?

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

158     Happy3Campers
157     LittleKid
71      InsideYourHead
68      HardenedTrio
46      BitterRivals

Résultats complets de lecture après lecture sur Google Drive


62
Je veux être le meilleur / Comme aucun code n'a jamais été / Ne pas planter, c'est mon test / Déboguer, c'est ma cause! / Je vais voyager à travers le réseau local / Des scripts lointains / Tenter de comprendre - Pourquoi mon BIOS frit! / Codémon, c’est vous et moi / Jouer au golf à portée de main / Codémon, vous êtes mon meilleur ami / Après la fin du programme! / Codémon, un langage tellement vrai / Aucun segfaut ne nous tirera à travers / Vous m'apprenez et je vous apprendrai / Codémon, je dois tout jouer au golf!
Kaz Wolfe

1
Au lieu d’augmenter le nombre de rounds à 500, ce serait bien si un round consistait à mettre tout le monde au combat. Donc, plus de temps byepour un nombre inégal de concurrents et on s’assurerait que les paires de matches sont justes et équitablement réparties.
Foobar

@foobar Je voulais éviter cela parce que cela met les batailles à la n^2place de n. Avec seulement les 7 concurrents et les 100 tours actuels, cela représente 2100 combats (contre 300 joueurs en l’état et 1500 avec 500 tours). Cela ne fait qu'empirer avec l'arrivée d'un plus grand nombre d'entrées. Je pourrais réduire le nombre de tours, mais j'hésite à le faire en raison de la variabilité inhérente (en ce qui concerne les statuts esp) et il est plus facile d'avoir un multiple de 50 (pour les points bonus).
Geobits

Ce défi ne nécessite-t-il pas une mise à jour? :)
GholGoth21

@ GholGoth21 Oui, je le crois. Je ne peux probablement pas y arriver aujourd'hui, mais peut-être demain ou le lendemain. Ping-moi dans le chat si pas mis à jour le jeudi ou si vous le souhaitez.
Geobits

Réponses:


16

Happy 3 campeurs - PHP

Une bande de lâches qui aiment peler l'opposition et la regarder pourrir.

EDIT : M. Lumpy a été sévèrement réprimé et ne dira plus rien de mauvais


PratiquePratiqueGrass - atk:50 def:99 spd:81 Confuse Poison Heal

Un castor sans bras venimeux qui aime confondre les gens avec des poignées de main problématiques


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

Un vétéran du babillard avec un faible pour les discussions ennuyeuses et les guerres de flammes.


NoisetteNoisetteFire - atk:50 def:99 spd:81 Burn Poison Heal

Les armes de destruction massive sont ses bonbons préférés.


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

Grâce à son QI de près de deux chiffres et à sa mémoire phénoménale, Lumpy peut deviner les mouvements de l’ennemi. Eh bien, surtout.


Stratégie

La stratégie consiste à empoisonner, brûler et confondre les adversaires dès que possible.
Le sommeil n'a pas été utilisé car il semblait moins puissant que les 3 sorts ci-dessus.
La confusion est mortelle à long terme, puisqu'elle réduit les attaques de 30% (dégâts et sortilège), empêche les guérisseurs de se soigner et blesse gravement les gros frappeurs (un monstre de 50 def / 100 atk inflige 20 points de dégâts à lui-même. )

Une fois qu'un ennemi est complètement collé, mes campeurs le regardent simplement grésiller, pourrir et se frapper à mort.

Haute défense et guérison sont utilisés pour atténuer les dégâts lors de l'agonie.

Pendant que mes 3 campeurs se battent, le cerf magique Lumpy surveille chaque mouvement de l'ennemi et parvient parfois à l'identifier. Les informations sont transmises à nos combattants qui font de leur mieux pour en profiter.

Après la défense, la vitesse est la statistique la plus importante à améliorer.
L'initiative est cruciale pour appliquer la guérison avant le prochain coup.

L'attaque n'est pas utilisée du tout.

Les sorts sont-ils l'arme ultime?

Des sorts comme poison, brûlure et confusion échappent à la logique générale rock / papier / ciseaux d'autres attaques.

Une fois qu'un monstre est affecté, il continue à perdre des PV même après la mort du lanceur de sorts. C'est comme si le fantôme du lanceur de sorts continuait de l'attaquer.
En outre, le poison devient rapidement plus puissant qu'une attaque massive entièrement améliorée (supérieure à 50 points après 5 tours).

L'espérance de vie d'un monstre empoisonné et brûlé ne dépasse pas 8 tours, même avec 3 guérisons.

Comme les robots de Martin semblent l'indiquer, la balance est assez bonne.
Il s’agit essentiellement d’une initiative qui fera pencher la balance entre les lanceurs de sorts purs et les attaquants purs.

Le code

Invoquer avec php campers.php

C'est un vilain gâchis, mais franchement l'interface n'aide pas non plus.

Maintenant qu’une compétition suffisamment agressive est apparue, j’ai mis en œuvre mes mouvements d’ennemis planifiés depuis longtemps en devinant.
L'analyse des attaques nécessite diverses déductions acrobatiques et un stockage persistant de l'état du dernier virage, ce qui signifie une guerre à grande échelle avec l'interface du contrôleur paranoïaque.
Ce n'est pas une belle chose ni un jackrabbit à six pattes non plus, mais il fait un travail adéquat.

<?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));

Résultats

LittleKid est toujours menaçant, mais mon trio a battu ses monstres venimeux de justesse.

Les robots de Martin sont voués à l'échec par leur manque d'initiative. Pour augmenter leur vitesse, il faudrait réduire l'attaque, ce qui affaiblirait leur capacité de traitement des dégâts.

Les nouveaux concurrents de la planète JavaScript sont à égalité avec l’équipe en tête-à-tête, mais ils réussissent moins bien que d’autres concurrents. Ils aident réellement à diminuer le score de LittleKid :).

Il semble donc que mes amis câlins restent les rois de la colline - pour l'instant ...

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

Et je n'ai pas de PHP sur le mien non plus. Cependant, je m'attendrais à ce que vous nettoyiez le sol avec les métapodes, car ils opteraient pour une stratégie lente et seraient empoisonnés à mort.
Sp3000

... * et * brûlés: D. C'est le problème avec des règles compliquées: une stratégie dominante est très susceptible d'émerger. Puisqu'il n'y a aucune protection contre ces sorts, ils pourraient être le gros homme et le petit garçon de codémon.

Je le comparerais plutôt à un jeu non-transitif comme des ciseaux, du papier, du rock - puisque les effets
durent longtemps

2
Oui, c'est ce que j'ai imaginé dès que je ne vois aucun remède contre Poison and Burn. Merci d'avoir donné vie à mon rêve.
moitié

1
C'est M. Lumpy qui exprime son étonnement lorsqu'il détecte plus de 3 attaques différentes chez le même adversaire :). J'ai une version corrigée presque complète, mais je suis en train de faire autre chose, donc le correctif sera publié dans un jour ou deux.

7

HardenedTrio, Python 3

Étant donné que Geobits a eu la gentillesse de nous faire deux propositions, j’ai pensé que je ferais une proposition idiote pour la première: P

La partie est composée de trois Codemon (Metapod1, Metapod2, Metapod3) avec les mêmes statistiques et mouvements:

  • 80 attaque, 100 défense, 50 vitesses
  • Punch, Guérir, Bouclier Harden

Tous les points bonus sont également attribués à la défense.


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")

Courir avec

py -3 <filename>

(ou avec python/ python3au lieu de pydépendre de votre installation)




J'essaie de lire votre code, mais je me suis trompé int(current_codemon.otherstats[1])>0. Cela revient vrai s'il a un effet de statut? Et utilise-t-il seulement Harden s'il a deux effets de statut?
Mooing Duck

@MooingDuck Pour votre Codemon, vous avez déjà moveCountprécédé le effectids, il est donc nécessaire de vérifier s'il peut toujours utiliser Harden. Je suis devenu paresseux avec l'analyse, c'est pourquoi il est regroupé là-bas.
Sp3000

@ Sp3000: Oh! Droite! HAHAHA!
Mooing Duck

6

Ruby dans ta tête

  • Brian : Psychic, Attaque: 100, Défense: 50, Vitesse: 80, Douleur, Fireball, Watergun
  • Elemon1 : Psychic, Attaque: 100, Défense: 50, Vitesse: 80, Boule de feu, Pistolet à eau, Vigne
  • Elemon2 : Psychic, Attaque: 100, Défense: 50, Vitesse: 80, Boule de feu, Pistolet à eau, Vigne
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

Courir avec

ruby InsideYourHead.rb

Cela ne fonctionne pas très bien contre le bot de Manu, mais il bat les trois autres. Les noms des équipes et des monstres sont assez aléatoires ... Je pourrais les changer si je trouve quelque chose de mieux

La stratégie est assez simple: attaque! Les trois monstres n'ont que des mouvements d'attaque purs et ils choisissent leur mouvement en fonction du type de monstre de l'adversaire.

Je pourrais expérimenter avec jeter un soin plus tard.


1
Hehe cela devient plus intéressant. Je savais que je pouvais compter sur vous pour cela, Martin :)

6

LittleKid, Java

Un petit enfant a trouvé 3 codémons identiques et les a formés. Ils sont très ennuyeux avec leurs attaques de soin + poison. Utiliser uniquement des codémons de type normal évite de les associer à des ennemis spécifiques, car le poison convient bien à tous les types.

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;
}

5
" Geobits dit que tu ne peux pas faire ça ": D
Geobits

On dirait que le poison est la vraie bombe A dans ce jeu :)

5

Nodémon - Javascript

Étant donné que le statut semble être la stratégie dominante, cette équipe se concentre sur la rapidité pour obtenir d’abord des états comme un poison et de la confusion sur l’adversaire, puis s’arrête pour guérir et / ou dormir pendant que l’adversaire dépérit.

Je n'ai pas installé PHP, donc ce n'est pas testé contre les Campers, mais cela semble être un concurrent décent pour LittleKid dans mes essais (et décime 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));

Courir avec

node nodemon

PS excuses à nodemon .


Cela dégénère en guerre globale de script côté serveur: D

4

Rivaux amers - Java

Une équipe Grass / Fire / Water qui aime l’activer.

Greenosaur

A au moins une couverture neutre sur quiconque. Haute vitesse pour compenser le manque de défense.

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

Searizard

Essaie de saper les ennemis avec une faible attaque. Brûlures et boules de feu après cela.

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

Blastshield

Utilise Shield pour améliorer sa défense déjà élevée. Soigne quand c'est nécessaire.

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

Code

Ceci est également inclus avec le contrôleur. C'est une équipe en compétition, à la différence DummyTeam. La commande nécessaire pour players.confest:

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

Erreur 310: Trop de redirections - C ++

Une équipe hautement formée et organisée pour contrer les ravages du poison

Pendant trois semaines, j'ai à peine formé mes codémons. J'ai formé plusieurs équipes. Et enfin, je suis prêt à relever ce défi. Pour répondre à tous mes adversaires empoisonneurs, j'ai constitué une équipe composée de codémons très différents, chacun ayant un rôle spécifique.


Antidote(image)

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

Antidote aime le poison. Si bien que je ne peux pas l'empêcher de se précipiter pour attaquer par des poisons.


Zen(image)

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

Zen est un codémon très surprenant qui s'adapte à tous les effets. Il préfère regarder ses ennemis s’efforcer et s’épuiser contre son silence.


Triforce(image)

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

Triforce est un codémon classique, toujours prêt à se battre. Celui-ci utilise sa force psychique pour contrôler les trois éléments et infliger le plus de dommages possible.


Vous pouvez télécharger l'équipe ici:

Linux:

http://dl.free.fr/iHYlmTOQ2

lancer avec ./Error310TMR

Les fenêtres :

http://dl.free.fr/vCyjtqo2s

lancer avec ./Error310TMR.exe

Le code est un projet c ++ complet. Je ne sais pas comment le publier.

$ 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

Mais c'est très efficace:

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

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

2

Conte de fée

Une équipe de milieu de terrain assez générique. Basé sur trois archétypes qui essaient de faire leur truc, et qui échangent s'ils ne peuvent pas faire leur truc du tout.

Cette équipe a été créée avant qu'il ne semble que poison soit la nouvelle méta, je ne suivais pas vraiment l'évolution de mon équipe depuis le début, mais la plupart du temps, je passais juste à essayer de faire l'analyse. Je n'ai pas encore réussi à tester le tournoi, mais la semaine a été bien remplie. Si celui-ci ne fonctionne pas vraiment comme il semble avec mes données de test, j'appliquerai un peu plus de brillance après le travail.

#!/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;
}

Courir avec

perl fairytale.pl

Ce bot est expulsé pour avoir tenté de «choisir» des membres morts après le premier décès. La première bataille que je peux voir ne passe jamais.
Geobits

Vraiment? Je pensais avoir corrigé ce bogue auparavant. Il s'agit peut-être d'une version plus ancienne de ce code ...
mezzoEmrys

N'oubliez pas que lorsqu'un codémon est mis KO, ses points de vie diminuent à 0 ou moins . Donc, la if($state->[MINE]->{$state->[MINE]->{slot}}{hp})déclaration ne fonctionnera pas correctement sans >0.
GholGoth21

Ah, oui, ça le ferait.
mezzoEmrys

0

Équipe factice - Java

(CW non compétitif)

C'est une équipe factice à pratiquer. Ce sont tous des types normaux, qui choisissent au hasard entre leurs mouvements (Punch, Heal, Slow) à chaque tour. Aie du plaisir avec ça.

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);
    }


}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.