Codémon, eu escolho você!


55

Seu gentil vizinho, Doctor Tree, acabou de lhe dar três criaturas mágicas chamadas Codémon. Há um torneio de batalha na cidade vizinha de Colorville. Você é o melhor, como ninguém nunca foi?

visão global

Este é um torneio de batalha. Cada jogador controla um time de três monstros, e o objetivo é nocautear (matar) o outro time. São 100 rodadas, com pontos concedidos por vitórias e empates. O time com mais pontos ganha!

Monstros

Um Codémon é uma pequena criatura complicada. Existem cinco tipos (elementos) para escolher, três estatísticas e três slots de movimentação em cada um.

Tipos

Cada codémon recebe um tipo. Os cinco tipos são Normal, Psíquico, Fogo, Água e Grama. Cada um tem seus pontos fortes e fracos. O dano é baseado no seguinte gráfico:

tipo de gráfico

Os números são multiplicadores de danos. Por exemplo, Água atacando fogo tem um modificador de 0,5 (meio dano), enquanto Grama atacando fogo é dobrada (2).

Estatísticas

Cada monstro tem três estatísticas que determinam suas habilidades de batalha. Ataque aumenta o dano que causa. Defesa diminui o dano necessário. A velocidade permite que ele se mova antes daqueles com velocidade mais baixa.

Cada monstro tem um valor inicial de 50 para cada estatística e um máximo de 100. Quando você cria seus monstros, você poderá atribuir 80 pontos adicionais de estatística (cada). Lembre-se de que nenhuma estatística individual pode ultrapassar 100. Portanto, você pode ter uma distribuição 100/80/50, 90/80/60 ou 65/65/100, mas 120/50/60 é ilegal. Qualquer equipe com estatísticas ilegais é desqualificada. Você não precisa usar todos os 80 pontos, mas provavelmente não deve ir com o mínimo de 50/50/50.

Você também pode considerar a HP um status, mas cada codémon tem 100 HP não modificáveis. Quando a HP cai para zero, eles não conseguem continuar lutando. HP é reabastecido para 100 antes de cada batalha.

Movimentos

Cada monstro conhece três movimentos de batalha. Os três escolhidos devem ser distintos, portanto, não há furação / furação / furação.

Existem 15 movimentos, três de cada tipo. Cada tipo tem um ataque direto, um ataque mais fraco com um efeito e um único movimento de efeito.

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

typerefere-se ao tipo de movimento. poweré seu poder impressionante. usesindica quantas vezes ele pode ser usado por batalha ( -é ilimitado). usablemostra por quais tipos ele pode ser usado (por exemplo, o Punch não pode ser atribuído a um tipo psíquico, pois não existe P). effectmostra quais efeitos os movimentos têm. Há 75% de chance de cada efeito funcionar, exceto Heal, que sempre funciona.

Para efeitos que alteram as estatísticas de um monstro, os efeitos podem ser empilhados . Por exemplo, o uso de Weaken duas vezes pode reduzir o ataque do seu oponente para 0,64 de eficácia. Efeitos que não alteram as estatísticas de um monstro (Sleep, Burn, etc) não são acumulados .

O sono coloca o oponente no sono, com 60% de chance de acordar no início de cada turno. Nenhuma ação será tomada por monstros adormecidos.

Queimadura danifica o oponente em 10 PV ao final de cada turno, quando ativo . O veneno funciona da mesma forma, mas aumenta cada vez mais. No primeiro turno, são 5 e ganha 5 a cada turno depois. Então, no quarto turno, será prejudicial para 20. Estes são danos simples, não afetados pelo tipo de monstro ou sujeitos a bônus.

A confusão pode fazer um monstro atacar a si mesmo, em vez de fazer o que foi solicitado. Esse ataque tem poder 10 e tem 30% de chance de acontecer em um determinado turno.

Para ficar claro, os efeitos duram até o final da batalha (exceto Dormir, como mencionado acima).

Os movimentos também recebem um aumento de 20% no poder se usados ​​por um monstro do tipo correspondente. Por exemplo, um monstro Grass usando Vine é impulsionado, enquanto ele usa Punch, ele não é.

Estatísticas secretas

As estatísticas e o tipo (mas não os movimentos) de cada monstro são de conhecimento público. Seus oponentes poderão ver o que estão lutando, a fim de escolher a melhor ação. No entanto, também existem bônus disponíveis que estão ocultos.

Especificamente, após cada duas batalhas, você receberá um ponto de status "bônus" para cada monstro do seu time. Pontos são dados a todos os monstros, mortos ou vivos, vencedores ou perdedores. Você pode atribuir isso a qualquer uma das três estatísticas que escolher. Você não pode empilhá-los em um único monstro; cada monstro recebe um de cada vez. Esses pontos são imunes ao limite de 100. Como haverá 100 rodadas de batalha, isso significa que você pode obter uma única estatística de até 149 se distribuir todos os seus bônus a ela. Novamente, o oponente verá apenas suas estatísticas "básicas"; portanto, quanto mais você estiver no torneio, mais o conhecimento deles diverge da verdade.

Batalha

A batalha ocorre entre equipes de três, com uma ativa em cada equipe por vez. No início, você verá o time do oponente e será solicitado a escolher qual monstro será o seu primeiro jogador "ativo".

Depois disso, as curvas ocorrem com as seguintes etapas:

  • Switch: Switches obrigatórios de monstros ocorrem (se houver)
  • Escolha ação de batalha
  • Switch: Qualquer switch monstro opcional (escolhido como ação de batalha) ocorre
  • Verificação do sono: possibilidade de acordar do sono
  • Ataque 1: Se possível, o monstro mais rápido usa seu movimento selecionado
  • Ataque 2: Se possível, o outro monstro usa seu movimento selecionado
  • Dano causado: Aplique dano de queimadura / veneno a monstros vivos

"Mais rápido" significa o monstro com maior velocidade. Se ambas as estatísticas de velocidade forem as mesmas, é escolhido por PRNG coin flip a cada turno.

No final de qualquer turno em que seu monstro ativo morre, você será solicitado a escolher um novo ativo. Você também pode optar por trocar de monstro ativo como sua jogada em qualquer turno (desde que você tenha mais de um vivo). Novamente, se você alternar como sua jogada, não fará uma batalha nesse turno.

Monstros não são "processados" quando inativos. Isso significa que eles não sofrem danos por queimadura / veneno, os contadores de veneno não se acumulam, não acordam do sono, etc. Nenhum efeito é removido ou alterado ao alternar . Este não é o outro jogo de luta de monstros. Se você sair com o ataque levantado e queimado, eles ainda estarão lá quando você voltar.

Dano de efeito ocorre se você mata seu oponente ativo ou não. Dessa forma, os membros de ambas as equipes podem morrer em um único turno.

Quando um time fica sem monstros utilizáveis, eles perdem. Se as duas equipes acabarem no mesmo turno, será um empate. Se a batalha durar 1000 turnos, é um empate.

A fórmula para determinar os danos é:

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

effAttacke effDefensesão as estatísticas efetivas para os monstros. O ataque eficaz é obtido adicionando Ataque e Ataque Bônus, multiplicando (por 0,8 ou 1,25) se algum efeito o alterar. Lembre-se de que esses efeitos podem ser acumulados.

O dano só pode ser 0 quando o modificador de tipo é 0 (Normal <--> Psíquico) ou o Poder do movimento é 0 (Heal, Burn, etc). Caso contrário, o mínimo é imposto em 1.

Torneio

Os torneios duram 100 rodadas. Em cada rodada, as equipes são embaralhadas e emparelhadas aleatoriamente. Se houver um número ímpar de equipes, o restante receberá um tchau (pontuação como empate). Ao vencer uma batalha, o time ganha 2 pontos, o empate vale 1 e não perde nada. A equipe com mais pontos no final vence!

Se as equipes estiverem empatadas, um torneio com apenas as equipes empatadas em primeiro lugar será realizado para determinar a ordem dos desempates.

Protocolo

O controlador enviará ao seu programa um dos quatro comandos. O primeiro caractere determina o tipo de comando, com os dados a seguir, se necessário.

Seu programa aceitará o comando como argumento e responderá em STDOUT dentro de um segundo . Não fique vivo ouvindo STDIN, ele não estará lá. Cada comando gerará um novo processo.

Você pode gravar dados / estado no disco. Coloque todos os arquivos em uma subpasta com o mesmo nome que sua equipe. Não escreva mais de 32 kilobytes de dados ou você será desqualificado. Os dados persistirão entre as rodadas, mas serão limpos entre os torneios.

Comandos

Dados da equipe

Isso é enviado uma vez no início do torneio para registrar seu time. Sua resposta deve ser constante , não diferente para cada torneio.

Inquerir:

T

Resposta:

name|member0|member1|member2

nameé uma string com o nome da sua equipe. Use apenas alfanuméricos para facilitar a análise. memberNé uma string de membro, fornecendo os detalhes de cada monstro:

Cadeia de membros:

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

Novamente, 'name' é uma string, dessa vez com o nome desse monstro. typeidé o seu tipo Os IDs de tipo estão na ordem mostrada no gráfico acima, com Normal = 0 e Grama = 4.

Os próximos três campos são suas estatísticas básicas. Lembre-se dos limites descritos na seção de estatísticas acima.

Os três últimos são os movimentos do seu monstro. Os IDs são mostrados no gráfico de movimentação acima.

Um exemplo de resposta de dados da equipe pode ser assim:

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

Qualquer equipe que enviar de volta dados ilegais, mal formatados ou ilegais aqui não participará até que sejam corrigidos.

Escolha Ativo

Isso é enviado no início de cada batalha e quando um monstro morre e precisa ser trocado.

Inquerir:

C#battleState

battleStatemostra o estado da batalha atual. Tenha paciência comigo aqui, é feio:

yourTeamState#theirTeamState

Onde se XteamStateparece:

name:activeSlot|member0state|member1state|member2state

activeSlotmostra qual monstro está ativo no momento (0-2). Os estados membros têm dois sabores. Se é sua equipe, fornece informações extras. Assim,

Seu membroXstate:

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

Seu membro Xstate:

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

idé simplesmente um identificador inteiro que você pode usar para rastrear monstros, se não gostar de usar name.

attack:defense:speedsão suas estatísticas básicas .

poisonedturns informa quantos turnos você foi envenenado.

moveCountXinforma quantos usos restam para cada movimento. Se 0, não pode ser usado. Para movimentos ilimitados, isso será negativo.

bonus(stat) é a quantidade de pontos de bônus que você atribuiu a cada estatística.

effectidé uma lista de efeitos de tamanho variável que foi aplicada ao seu monstro. Haverá não ser uma fuga :na corda, se existem efeitos activos presentes ou não. Se houver efeitos empilhados, eles aparecerão como múltiplos efeitos na lista.

Os IDs de efeito são:

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

Resposta:

memberSlot

A única resposta necessária é um número único 0,1,2, informando qual membro você deseja estar ativo. Este deve ser um membro capaz de lutar. Não envie de volta 1se o membro 1 estiver morto.

Ação de Batalha

A cada turno, você precisa decidir o que fazer.

Inquerir:

A#battleState

O battleStateaqui é exatamente como descrito acima.

Resposta:

Para usar uma movimentação, envie de volta o slot em que a movimentação está. Por exemplo, se eu atribuir o Punch ao slot 0, o envio 0executará o Punch.

Para mudar para outro membro, envie o slot do membro mais dez . Então, para mudar para o membro 2, envie 12.

Qualquer coisa que não esteja em [0,1,2,10,11,12] é considerada inválida e não resultará em nenhuma ação nesse turno.

Estatísticas de bônus

Após cada duas batalhas, você recebe um ponto de bônus secreto para cada membro da equipe.

Inquerir:

B#yourTeamState

O estado da sua equipe é o mesmo mostrado acima, não me faça repeti-lo.

Resposta:

stat0:stat1:stat2

Sua resposta representará qual status aumentar para cada membro da equipe. O ataque é 0, a defesa é 1, a velocidade é 2.

Portanto, para aumentar a velocidade do membro, o ataque do membro dois e a defesa do membro três, você responderia com:

2:0:1

Controlador

O controlador pode ser encontrado no BitBucket: https: //Geobits@bitbucket.org/Geobits/codemon.git

Apenas jogue todos os arquivos de classe compilados, envios e players.conf em uma pasta e execute.

A classe principal do controlador é chamada Tournament. O uso é:

java Tournament [LOG_LEVEL]

Os níveis de log de 0 a 4 fornecem informações crescentes. O nível 0 realiza o torneio silenciosamente e apenas fornece os resultados, enquanto o nível 3 fornece comentários passo a passo. O nível 4 é saída de depuração.

Você pode adicionar envios ao torneio em players.confSimplesmente adicione a string da linha de comando necessária para executar o programa, uma por linha. As linhas que começam com #são comentários.

Na sua postagem, inclua o comando que precisarei adicionar ao meu players.confe todas as etapas de compilação (se necessário).

Está incluído um time fictício composto por todos os membros normais com os três movimentos normais. Eles escolhem movimentos aleatoriamente e têm estatísticas terríveis. Divirta-se batendo nele.

Regras diversas

  • Você não pode ler ou gravar em nenhum recurso externo (exceto em sua própria subpasta, até 32 kB, conforme observado acima).

  • Sua equipe precisa entrar no torneio "às cegas". Isso significa que você não pode analisar a fonte de outras pessoas para descobrir o que um time / monstro específico fará em uma determinada situação. Você pode analisar os movimentos / estatísticas do seu oponente e acompanhar o andamento do torneio, mas não codifica essas informações.

  • Não interfira com outros processos / envios. Não é possível invocá-los, usar a reflexão para acessar seus dados, etc. Não mexa no meu computador. Apenas não tente. Isso é a meu critério. Os infratores podem ser impedidos de entrada futura.

  • Os participantes estão limitados a um máximo de duas entradas. Se você enviar mais, pontuarei apenas os dois primeiros enviados. Se você deseja revogar um, exclua-o.

  • As entradas podem não existir apenas para apoiar outras entradas. Além disso, você não pode tentar desqualificar indiretamente outros competidores (por exemplo, usando um nome de equipe de 27 milhões de caracteres para jogadores de DQ que tentam gravá-lo no disco). Cada envio deve jogar para ganhar por seu próprio mérito.

  • Seu programa pode gerar no máximo um processo filho por vez (total de descendentes, não diretos). Os processos principal e qualquer processo filho devem terminar diretamente após a saída. De qualquer forma, verifique se você não excedeu o tempo limite.

  • O torneio será realizado no meu computador executando o Ubuntu com um processador Intel i7 3770K.

Resultados

Estes são os resultados dos jogadores atuais. É muito próximo entre os principais candidatos, e estou pensando em aumentar o número de rodadas até 500 (e ajustar o espaçamento dos pontos de bônus para combinar). Alguma objeção, comentário?

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

158     Happy3Campers
157     LittleKid
71      InsideYourHead
68      HardenedTrio
46      BitterRivals

Resultados completos de reprodução por reprodução no Google Drive


62
Eu quero ser o melhor / Como nenhum código jamais foi / Não travar é o meu teste / Depurar é a minha causa! / Vou viajar pela LAN / Scripting por toda parte / Tentando entender / Por que meu BIOS fritou! / Codémon, somos você e eu / Golfe que todos os olhos podem ver / Codémon, você é meu melhor amigo / Depois que o programa termina! / Codémon, uma linguagem tão verdadeira / Nenhum segfaults nos ajudará / Você me ensina e eu vou te ensinar / Codémon, tem que jogar tudo!
Kaz Wolfe

11
Em vez de aumentar as rodadas para 500, seria bom se uma rodada consistisse em todos lutando contra todos. Portanto, não há mais problemas byepara um número desigual de competidores e seria garantido que os pares de partidas fossem justos e distribuídos uniformemente.
foobar

@foobar Eu queria evitar isso porque escalava as batalhas com e n^2não n. Com apenas os 7 concorrentes atuais e 100 rodadas, são 2100 batalhas (vs 300 como estão e 1500 com 500 rodadas). Isso só piora à medida que mais entradas entram. Eu poderia reduzir o número de rodadas, mas hesito em fazer isso devido à variabilidade inerente (em relação aos status esp), e ter um múltiplo de 50 (para pontos de bônus) é mais fácil.
Geobits

Este desafio não requer uma atualização? :)
GholGoth21

@ GholGoth21 Sim, acredito que sim. Provavelmente não consigo chegar hoje, mas talvez amanhã ou no dia seguinte. Faça ping no meu bate-papo se não for atualizado até quinta-feira, se desejar.
Geobits

Respostas:


16

Felizes 3 campistas - PHP

Um grupo de covardes que gostam de atacar a oposição com feitiços debilitantes e vê-los apodrecer.

EDIT : O Sr. Lumpy foi severamente castigado e não diz mais palavrões


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

Um castor venenoso sem braços que gosta de confundir pessoas com apertos de mão problemáticos


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

Um veterano do quadro de avisos com uma queda por conversas tediosas e guerras de chamas.


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

Armas de destruição em massa são seus doces favoritos.


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

Graças ao seu QI de quase 2 dígitos e à memória fenomenal, Lumpy consegue adivinhar os movimentos do inimigo. Bem, principalmente.


Estratégia

A estratégia é envenenar, queimar e confundir os adversários o mais rápido possível.
O sono não foi usado, pois parecia menos poderoso que os três feitiços acima.
A confusão é mortal a longo prazo, uma vez que reduz os ataques em 30% (tanto o dano quanto o lançamento de feitiços), evita que os curandeiros se curem e prejudica muito os lançadores pesados ​​(um monstro de 50 def / 100 causará 20 pontos de dano a si mesmo )

Uma vez que um inimigo é completamente colado, meus campistas simplesmente o observam chiar, apodrecer e dar um soco até a morte.

Alta defesa e cura são usadas para mitigar os danos causados ​​durante a agonia.

Enquanto meus 3 campistas estão lutando, Lumpy, o cervo mágico, observa os inimigos a cada movimento e às vezes consegue identificá-los. A informação é devolvida aos nossos combatentes, que fazem o possível para tirar proveito dela.

Após a defesa, a velocidade é a estatística mais importante para aumentar.
A iniciativa é crucial para aplicar a cura antes do próximo golpe.

Ataque não é usado.

Os feitiços são a arma definitiva?

Feitiços como veneno, queimadura e confusão escapam à lógica geral de pedra / papel / tesoura de outros ataques.

Uma vez que um monstro seja afetado, ele continuará perdendo HPs mesmo depois que o lançador de feitiços estiver morto. É como se o fantasma do lançador o atacasse.
Além disso, o veneno se torna rapidamente mais poderoso do que um ataque maciço totalmente aprimorado (acima de 50 pontos após 5 turnos).

A expectativa de vida de um monstro envenenado e queimado não excede 8 turnos, mesmo com 3 curas.

Como os bots de Martin parecem indicar, o equilíbrio do jogo é muito bom.
Basicamente, é uma iniciativa que irá alterar o equilíbrio entre puro conjurador e puro atacante.

O código

Invocar com php campers.php

É uma bagunça feia, mas francamente a interface também não ajuda.

Agora que uma competição adequadamente agressiva apareceu, eu implementei meus movimentos inimigos planejados há muito tempo.
A análise de ataques requer várias deduções acrobáticas e armazenamento persistente do estado do último turno, o que significa guerra em grande escala com a interface do controlador paranóico.
Também não é uma coisa bonita e nem um coelho de seis pernas, mas faz um trabalho adequado.

<?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 ainda é ameaçador, mas meu trio venceu seus malucos venenosos por uma margem justa.

Os bots de Martin estão condenados por sua falta de iniciativa, e aumentar sua velocidade exigiria menos ataques, o que diminuiria sua vantagem na capacidade de causar dano.

Os novos competidores do planeta JavaScript estão em pé de igualdade com a equipe individualmente, mas se saem pior contra outros concorrentes. Eles realmente ajudam a diminuir a pontuação de LittleKid :).

Parece que meus amigos fofinhos continuam sendo reis da colina - por enquanto ...

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

E eu também não tenho PHP no meu. Eu esperaria que você esfregasse o chão com os Metapods, já que eles fazem um esforço lento e seriam envenenados até a morte.
Sp3000 04/04

... * e * queimado até ficar crocante: D. Esse é o problema de regras complicadas: é provável que uma estratégia dominante surja. Como não há proteção contra esses feitiços, eles podem ser o gordo e o menininho de codémon.

Eu compará-lo mais para um jogo não-transitivo como tesouras, papel, pedra - já que os efeitos demorar um pouco, uma equipe completa-ataque deve ser capaz de levá-lo para baixo :)
SP3000

2
Sim, é isso que imaginei assim que não vejo remédio para Poison and Burn. Obrigado por trazer meu sonho à vida.
Justhalf

11
É o Sr. Lumpy que expressa sua perplexidade quando detecta mais de 3 ataques diferentes do mesmo adversário :). Eu tenho uma versão corrigida quase completa, mas estou no meio de outra coisa agora, então a correção será publicada em mais ou menos um dia.

7

HardenedTrio, Python 3

Como o Geobits foi bom o suficiente para nos enviar duas submissões, pensei em enviar algo bobo para a primeira: P

O grupo é composto por três codemons (Metapod1, Metapod2, Metapod3) com as mesmas estatísticas e movimentos:

  • 80 ataque, 100 defesa, 50 velocidades
  • Soco, Cura, Endurecimento de Escudo

Todos os pontos de bônus também são atribuídos à defesa.


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

Correr com

py -3 <filename>

(ou com python/ em python3vez de pydepender da sua instalação)



11
@FryAmTheEggman Metapod, HARDEN!
Sp3000

Estou tentando ler seu código, mas fiquei confuso em int(current_codemon.otherstats[1])>0. Isso retorna verdadeiro se ele tiver um efeito de status? E ele só usa harden se tiver dois efeitos de status?
Mooing Duck

@MooingDuck Para o seu Codemon, você tem moveCounts antes do effectids, por isso está verificando se ainda pode usar o Harden. Fiquei preguiçoso com a análise, e é por isso que ele está lá dentro.
Sp3000 6/02/15

@ Sp3000: Ah! Direito! HAHAHA!
Mooing Duck

6

Dentro de sua cabeça, Ruby

  • Brian : Psíquico, Ataque: 100, Defesa: 50, Velocidade: 80, Dor, Bola de Fogo, Arma de Água
  • Elemon1 : Psíquico, Ataque: 100, Defesa: 50, Velocidade: 80, Bola de Fogo, Arma de Água, Videira
  • Elemon2 : Psíquico, Ataque: 100, Defesa: 50, Velocidade: 80, Bola de Fogo, Arma de Água, Videira
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

Correr com

ruby InsideYourHead.rb

Isso não funciona muito bem contra o bot de Manu, mas supera os outros três. Os nomes dos times e dos monstros são bem aleatórios ... Eu posso alterá-los se surgir algo melhor

A estratégia é bem simples: ataque! Todos os três monstros têm apenas movimentos de ataque puros, e eles escolhem seus movimentos com base no tipo de monstro do oponente.

Eu poderia experimentar jogar um Heal mais tarde.


11
Hehe isso se torna mais interessante. Eu sabia que podia contar com você para isso, Martin :)

6

LittleKid, Java

Uma criança encontrou 3 códigos idênticos e os treinou. Eles são muito irritantes com seus ataques de cura + veneno. Usar apenas códigos-tipo do tipo normal elimina a necessidade de emparelhá-los contra inimigos específicos, pois o veneno funciona bem contra todos os 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;
}

5
" Geobits diz que você não pode fazer isso ": D
Geobits 04/04/2015

Parece veneno é o verdadeiro A-bomba neste jogo :)

5

Nodémon - Javascript

Como o status parece ser a estratégia dominante, esse time se concentra na velocidade para obter status como veneno e confusão para os oponentes primeiro e depois fica paralisado com a cura e / ou o sono enquanto o oponente perde.

Eu não tenho o PHP instalado, então isso não foi testado contra os Campers, mas parece ser um concorrente decente para o LittleKid em meus testes (e dizima os 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));

Correr com

node nodemon

PS Desculpas para nodemon .


Esta é escalada para uma guerra global script do lado do servidor: D

4

Rivais Amargos - Java

Uma equipe de grama / fogo / água que gosta de trocá-lo.

Greenosaur

Tem pelo menos cobertura neutra em qualquer pessoa. Alta velocidade para compensar a falta de defesa.

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

Searizard

Tenta Sap inimigos com baixo ataque. Queimaduras e bolas de fogo depois disso.

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

Blastshield

Usa Shield para melhorar sua defesa já alta. Cura quando necessário.

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

Código

Isso também está incluído no controlador. Esta é uma equipe competidora, diferentemente do DummyTeam. O comando necessário para players.confé:

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

Erro 310: Muitos redirecionamentos - C ++

Uma equipe altamente treinada e organizada para combater a devastação do veneno

Durante três semanas, mal treinei meus códigos. Eu formei várias equipes. E, finalmente, estou pronto para enfrentar esse desafio. Para responder a todos os meus oponentes envenenadores, formei uma equipe com codemons muito diferentes, cada um com um papel específico.


Antídoto(imagem)

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

Antídoto adora veneno. Tanto é assim que não consigo impedi-lo de correr para ataques de veneno.


zen(imagem)

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

Zen é um codémon muito surpreendente que acomoda todos os efeitos. Ele prefere assistir seus inimigos se esforçarem e esgotarem-se contra seu silêncio.


Triforce(imagem)

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

Triforce é um codémon clássico, sempre pronto para lutar. Este usa sua força psíquica para controlar os três elementos e infligir o máximo de dano possível.


Você pode baixar a equipe aqui:

Linux:

http://dl.free.fr/iHYlmTOQ2

iniciar com ./Error310TMR

Janelas :

http://dl.free.fr/vCyjtqo2s

iniciar com ./Error310TMR.exe

O código é um projeto c ++ completo. Não sei como publicá-lo.

$ 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

Mas é muito eficaz:

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

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

2

Conto de fadas

Uma equipe bastante genérica do meio da estrada. Baseado em três arquétipos que tentam fazer suas coisas, e mudam se não conseguem fazer nada.

Essa equipe foi criada antes de parecer que o veneno era a nova meta, eu realmente não acompanho a mudança da minha equipe desde que a criei originalmente, a maior parte do tempo era gasta apenas tentando fazer a análise. Ainda não pude fazer o torneio rodar para testá-lo, mas tem sido uma semana bastante movimentada. Se este não funcionar como parece com meus dados de teste, aplicarei mais brilho depois do trabalho.

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

Correr com

perl fairytale.pl

Este bot é expulso por tentar 'escolher membros ativos' após o primeiro falecimento. Nunca supera a primeira batalha que posso ver.
Geobits

Realmente? Eu pensei que tinha chegado a esse bug funcionou antes, talvez esta é uma versão mais antiga do que o código ...
mezzoEmrys

Não se esqueça que quando um codemon é eliminado, seus pontos de cura diminuem em 0 ou menos . Portanto, a if($state->[MINE]->{$state->[MINE]->{slot}}{hp})declaração não funcionará corretamente sem >0.
GholGoth21

Ah, sim, isso faria isso.
precisa saber é o seguinte

0

Equipe Dummy - Java

(CW não concorrente)

Esta é uma equipe fictícia para praticar. São todos os tipos normais, que escolhem aleatoriamente entre seus movimentos (Soco, Cura, Lento) a cada turno. Divirta-se com isso.

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


}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.